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

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 9d4f495a804 Fix flaky Firefox calendar-tab e2e tests: read run states 
from the DOM (#67879)
9d4f495a804 is described below

commit 9d4f495a804e9a79015f1618665029d8a24e8edc
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue Jun 2 17:10:07 2026 +0200

    Fix flaky Firefox calendar-tab e2e tests: read run states from the DOM 
(#67879)
    
    The Dag Calendar tab e2e tests read run states by hovering each cell to open
    the tooltip. BasicTooltip opens on a mouseenter after a 500ms delay and 
renders
    through a portal, which synthetic pointer events do not open reliably in
    headless Firefox — the three state-reading tests timed out there (chromium 
was
    unaffected), and a stepped real-pointer hover did not help either.
    
    Expose the per-cell run states as a data-states attribute on the calendar 
cell,
    computed with the same view-mode-aware logic the tooltip uses, and read that
    attribute in the tests instead of hovering. Reading a DOM attribute is
    deterministic and backend-independent, so the tests pass on every browser.
---
 .../ui/src/pages/Dag/Calendar/CalendarCell.tsx     | 14 +++++++++++
 .../airflow/ui/tests/e2e/pages/DagCalendarTab.ts   | 28 ++++++++--------------
 .../ui/tests/e2e/specs/dag-calendar-tab.spec.ts    |  2 +-
 3 files changed, 25 insertions(+), 19 deletions(-)

diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarCell.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarCell.tsx
index a64ac790ea8..ea91e34309b 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarCell.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarCell.tsx
@@ -51,6 +51,18 @@ export const CalendarCell = ({
   const hasData = Boolean(cellData && relevantCount > 0);
   const hasTooltip = Boolean(cellData);
 
+  // States present in this cell, computed with the same view-mode-aware logic 
the
+  // tooltip uses (see CalendarTooltip). Exposed as a `data-states` attribute 
so e2e
+  // tests can read run states from the DOM instead of hovering — the tooltip 
does
+  // not open reliably under synthetic pointer events in headless Firefox.
+  const runStates = cellData
+    ? Object.entries(cellData.counts)
+        .filter(
+          ([key, value]) => key !== "total" && value > 0 && (viewMode === 
"failed" ? key === "failed" : true),
+        )
+        .map(([key]) => key)
+    : [];
+
   const isMixedState =
     typeof backgroundColor === "object" && "planned" in backgroundColor && 
"actual" in backgroundColor;
 
@@ -60,6 +72,7 @@ export const CalendarCell = ({
       borderRadius="2px"
       cursor={hasData ? "pointer" : "default"}
       data-has-data={hasData ? "true" : "false"}
+      data-states={runStates.join(" ")}
       data-testid="calendar-cell"
       data-view-mode={viewMode}
       height="14px"
@@ -90,6 +103,7 @@ export const CalendarCell = ({
       borderRadius="2px"
       cursor={hasData ? "pointer" : "default"}
       data-has-data={hasData ? "true" : "false"}
+      data-states={runStates.join(" ")}
       data-testid="calendar-cell"
       data-view-mode={viewMode}
       height="14px"
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/DagCalendarTab.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/DagCalendarTab.ts
index 77b6d465864..74e46a1802d 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/DagCalendarTab.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/DagCalendarTab.ts
@@ -33,10 +33,6 @@ export class DagCalendarTab extends BasePage {
     return this.page.getByTestId("calendar-cell");
   }
 
-  public get tooltip(): Locator {
-    return this.page.getByTestId("calendar-tooltip");
-  }
-
   public constructor(page: Page) {
     super(page);
 
@@ -92,22 +88,18 @@ export class DagCalendarTab extends BasePage {
     const count = await this.activeCells.count();
     const states: Array<string> = [];
 
+    // Read run states from the cell's `data-states` attribute rather than 
hovering to
+    // read the tooltip. The tooltip (BasicTooltip) opens on a `mouseenter` 
after a
+    // 500ms delay and renders through a portal; synthetic pointer events do 
not open
+    // it reliably in headless Firefox, which made these tests flaky. 
`data-states` is
+    // populated with the same view-mode-aware logic the tooltip uses (see
+    // CalendarCell), so the assertions are unchanged and now 
backend-independent.
     for (let i = 0; i < count; i++) {
-      const cell = this.activeCells.nth(i);
+      const raw = (await this.activeCells.nth(i).getAttribute("data-states")) 
?? "";
 
-      // Firefox sometimes fails to trigger tooltips on hover.
-      // Retry the hover + tooltip visibility check to handle this.
-      let text = "";
-
-      await expect(async () => {
-        await cell.hover({ force: true });
-        await expect(this.tooltip).toBeVisible({ timeout: 3000 });
-        text = ((await this.tooltip.textContent()) ?? "").toLowerCase();
-      }).toPass({ intervals: [1000], timeout: 20_000 });
-
-      if (text.includes("success")) states.push("success");
-      if (text.includes("failed")) states.push("failed");
-      if (text.includes("running")) states.push("running");
+      for (const state of raw.split(" ").filter(Boolean)) {
+        states.push(state);
+      }
     }
 
     return states;
diff --git 
a/airflow-core/src/airflow/ui/tests/e2e/specs/dag-calendar-tab.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/dag-calendar-tab.spec.ts
index d05f4b9e9bc..771f7241f85 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/dag-calendar-tab.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/dag-calendar-tab.spec.ts
@@ -48,7 +48,7 @@ test.describe("Dag Calendar Tab", () => {
     expect(states.length).toBeGreaterThanOrEqual(2);
   });
 
-  test("verify hover shows correct run states", async ({ dagCalendarTab }) => {
+  test("verify success and failed run states are detected", async ({ 
dagCalendarTab }) => {
     await dagCalendarTab.switchToHourly();
 
     const states = await dagCalendarTab.getManualRunStates();

Reply via email to