You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/08/12 17:37:33 UTC

(superset) branch fix/embedded-dashboard-e2e-flake created (now ed338a63452)

This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a change to branch fix/embedded-dashboard-e2e-flake
in repository https://gitbox.apache.org/repos/asf/superset.git


      at ed338a63452 test(embedded): stabilize flaky embedded dashboard E2E

This branch includes the following new commits:

     new ed338a63452 test(embedded): stabilize flaky embedded dashboard E2E

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(superset) 01/01: test(embedded): stabilize flaky embedded dashboard E2E

Posted by ru...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch fix/embedded-dashboard-e2e-flake
in repository https://gitbox.apache.org/repos/asf/superset.git

commit ed338a6345234f3e71dca5c5bd25c7396d89fb46
Author: Evan Rusackas <ev...@rusackas.com>
AuthorDate: Wed Aug 12 10:37:07 2026 -0700

    test(embedded): stabilize flaky embedded dashboard E2E
    
    The embedded dashboard E2E tests intermittently timed out waiting for the iframe grid to become visible, with no diagnostics -- the guest-token/SDK postMessage handshake can drop on a cold CI start. Add a bounded retry of the render sequence, and race the grid wait against the test app's error box so a real embed failure surfaces its message instead of a blind 30s timeout.
    
    Co-Authored-By: Claude Opus 4.8 <no...@anthropic.com>
---
 superset-frontend/playwright/pages/EmbeddedPage.ts | 67 +++++++++++++++++++---
 .../tests/embedded/embedded-dashboard.spec.ts      |  8 +--
 2 files changed, 62 insertions(+), 13 deletions(-)

diff --git a/superset-frontend/playwright/pages/EmbeddedPage.ts b/superset-frontend/playwright/pages/EmbeddedPage.ts
index b9b61612937..690c5cf2c86 100644
--- a/superset-frontend/playwright/pages/EmbeddedPage.ts
+++ b/superset-frontend/playwright/pages/EmbeddedPage.ts
@@ -103,16 +103,69 @@ export class EmbeddedPage {
   /**
    * Wait for dashboard content to render inside the iframe.
    * Looks for the grid-container which indicates charts are loading/loaded.
+   *
+   * Races the grid against the test app's `#error` box so an embed failure
+   * surfaces its message immediately, instead of blindly timing out on the
+   * grid selector and hiding the real reason.
    */
   async waitForDashboardContent(options?: { timeout?: number }): Promise<void> {
-    const frame = this.iframe;
-    await frame
+    const timeout = options?.timeout ?? EMBEDDED.DASHBOARD_RENDER;
+    const grid = this.iframe
       .locator('.grid-container, [data-test="grid-container"]')
-      .first()
-      .waitFor({
-        state: 'visible',
-        timeout: options?.timeout ?? EMBEDDED.DASHBOARD_RENDER,
-      });
+      .first();
+    const errorBox = this.page.locator(EmbeddedPage.SELECTORS.ERROR);
+
+    const ready = grid
+      .waitFor({ state: 'visible', timeout })
+      .then(() => 'ready' as const)
+      .catch(() => 'gridTimeout' as const);
+    const failed = errorBox
+      .waitFor({ state: 'visible', timeout })
+      .then(() => 'error' as const)
+      .catch(() => 'errorTimeout' as const);
+
+    const outcome = await Promise.race([ready, failed]);
+    if (outcome === 'ready') return;
+    if (outcome === 'error') {
+      const message = (await errorBox.textContent())?.trim() || 'unknown error';
+      throw new Error(`Embedded dashboard failed to render: ${message}`);
+    }
+    const status = (
+      await this.page.locator(EmbeddedPage.SELECTORS.STATUS).textContent()
+    )?.trim();
+    throw new Error(
+      `Embedded dashboard did not render within ${timeout}ms ` +
+        `(status: ${status ?? 'unknown'})`,
+    );
+  }
+
+  /**
+   * Navigate to the test app and wait for the dashboard to render, retrying
+   * the whole sequence once on failure. The embedded guest-token / SDK
+   * postMessage handshake can intermittently drop on a cold CI start, leaving
+   * the dashboard stuck before the grid renders; a fresh load clears it.
+   */
+  async render(
+    params: Parameters<EmbeddedPage['goto']>[0],
+    options?: { attempts?: number; timeout?: number },
+  ): Promise<void> {
+    const attempts = options?.attempts ?? 2;
+    for (let attempt = 1; ; attempt += 1) {
+      try {
+        await this.goto(params);
+        await this.waitForIframe(options);
+        await this.waitForDashboardContent(options);
+        return;
+      } catch (err) {
+        if (attempt >= attempts) throw err;
+        // eslint-disable-next-line no-console
+        console.warn(
+          `[embedded] dashboard render attempt ${attempt} failed, retrying: ${
+            err instanceof Error ? err.message : String(err)
+          }`,
+        );
+      }
+    }
   }
 
   /**
diff --git a/superset-frontend/playwright/tests/embedded/embedded-dashboard.spec.ts b/superset-frontend/playwright/tests/embedded/embedded-dashboard.spec.ts
index 9a717c50108..68eef12cafa 100644
--- a/superset-frontend/playwright/tests/embedded/embedded-dashboard.spec.ts
+++ b/superset-frontend/playwright/tests/embedded/embedded-dashboard.spec.ts
@@ -165,13 +165,11 @@ test.describe('Embedded Dashboard E2E', () => {
     await embeddedPage.exposeTokenFetcher(async () =>
       getGuestToken(page, dashboardId, { accessToken }),
     );
-    await embeddedPage.goto({
+    await embeddedPage.render({
       appUrl: appServer.url,
       uuid: embedUuid,
       supersetDomain: SUPERSET_DOMAIN,
     });
-    await embeddedPage.waitForIframe();
-    await embeddedPage.waitForDashboardContent();
     return embeddedPage;
   }
 
@@ -251,14 +249,12 @@ test.describe('Embedded Dashboard E2E', () => {
     await embeddedPage.exposeTokenFetcher(async () =>
       getGuestToken(page, dashboardId, { accessToken }),
     );
-    await embeddedPage.goto({
+    await embeddedPage.render({
       appUrl: appServer.url,
       uuid: embedUuid,
       supersetDomain: SUPERSET_DOMAIN,
       hideTitle: true,
     });
-    await embeddedPage.waitForIframe();
-    await embeddedPage.waitForDashboardContent();
 
     // The iframe URL should include uiConfig parameter
     await expect(