fix: reset embed iframe queue consistently to preserve UI commands (#27419)

* Reset queue correctly all places

* test: verify UI config persists on second modal open (#27422)

* test: verify UI config persists on second modal open

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: update iframe selection logic in getEmbedIframe function

Refactor the getEmbedIframe function to improve iframe selection by using a more specific selector and ensuring the last iframe is targeted in cases of repeated modal openings. This change enhances reliability in iframe handling during tests.

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix test

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Hariom Balhara
2026-02-02 13:51:25 +00:00
committed by GitHub
co-authored by hariom@cal.com Devin AI
parent 4f0a1d0f9b
commit bc392584b2
3 changed files with 55 additions and 14 deletions
@@ -83,11 +83,23 @@ export const getEmbedIframe = async ({
},
{ polling: 500 }
);
const embedIframe = page.frame(`cal-embed=${calNamespace}`);
const iframeSelector = `iframe[name="cal-embed=${calNamespace}"]`;
// In case of modal we don't cleanup previous iframe on repeat click, so we should read the last one by default as that would be the one actie
const targetIframeElement = page.locator(iframeSelector).last();
let elementHandle = await targetIframeElement.elementHandle();
let embedIframe = await elementHandle?.contentFrame();
if (!embedIframe) {
return null;
}
const u = new URL(embedIframe.url());
let url = embedIframe.url();
if (!url) {
// If iframe hasn't even initiated loading yet, wait for it to be visible
await targetIframeElement.waitFor();
url = embedIframe.url();
}
const u = new URL(url);
if (u.pathname === `${pathname}/embed`) {
return embedIframe;
}
@@ -314,20 +314,45 @@ test.describe("Popup Tests", () => {
await embeds.gotoPlayground({ calNamespace, url: "/" });
await page.click(`[data-cal-namespace="${calNamespace}"]`);
await test.step("First modal open - verify UI config is applied", async () => {
await page.click(`[data-cal-namespace="${calNamespace}"]`);
const embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/free/30min" });
const embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/free/30min" });
await expect(embedIframe).toBeEmbedCalLink(calNamespace, embeds.getActionFiredDetails, {
pathname: "/free/30min",
await expect(embedIframe).toBeEmbedCalLink(calNamespace, embeds.getActionFiredDetails, {
pathname: "/free/30min",
});
if (!embedIframe) {
throw new Error("Embed iframe not found");
}
// Verify that event type details are hidden (hideEventTypeDetails: true was set via ui() call)
await expect(embedIframe.locator('[data-testid="event-meta"]')).toBeHidden();
});
if (!embedIframe) {
throw new Error("Embed iframe not found");
}
await test.step("Close the modal", async () => {
await page.locator("cal-modal-box .close").click();
await expect(page.locator("cal-modal-box")).toBeHidden();
});
// Verify that event type details are hidden (hideEventTypeDetails: true was set via ui() call)
await expect(embedIframe.locator('[data-testid="event-meta"]')).toBeHidden();
await test.step("Second modal open - verify UI config persists", async () => {
await page.click(`[data-cal-namespace="${calNamespace}"]`);
const embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/free/30min" });
await expect(embedIframe).toBeEmbedCalLink(calNamespace, embeds.getActionFiredDetails, {
pathname: "/free/30min",
});
if (!embedIframe) {
throw new Error("Embed iframe not found");
}
// Verify that event type details are STILL hidden on second open
// This tests that UI config persists across modal reopens (the bug was that iframeDoQueue was being cleared completely)
await expect(embedIframe.locator('[data-testid="event-meta"]')).toBeHidden();
});
});
});
+7 -3
View File
@@ -412,14 +412,18 @@ export class Cal {
}
}
iframeReset() {
this.iframeReady = false;
resetQueue() {
// Only keep UI related instructions in the queue, as we want to ensure that newly loaded iframe has the same UI configuration applied automatically
this.iframeDoQueue = this.iframeDoQueue.filter((doInIframeArg) =>
this.commandsPersistAcrossIframeResets.includes(doInIframeArg.method)
);
}
iframeReset() {
this.iframeReady = false;
this.resetQueue();
}
constructor(namespace: string, q: Queue) {
this.__config = {
// Use WEBAPP_URL till full page reload problem with website URL is solved
@@ -469,7 +473,7 @@ export class Cal {
this.iframeDoQueue.forEach((doInIframeArg) => {
this.doInIframe(doInIframeArg);
});
this.iframeDoQueue = [];
this.resetQueue();
});
this.actionManager.on("__routeChanged", () => {