Motivation
Thanks to the amazing efforts of the community, the UI E2E test suite has reached solid coverage across many areas of the Airflow UI. The work tracked in the previous meta issue #59028 successfully introduced Playwright-based tests and implemented a wide range of scenarios 🎉
However, as the test suite has grown, we have started to observe some inconsistencies in testing patterns and practices across different files.
By improving consistency first, it will become easier to extend coverage, stabilize flaky tests, and maintain the E2E suite going forward.
Description
Our e2e test suite has grown over time, and we'd like to align it more closely with
Playwright's recommended best practices.
These changes are not intended to alter test coverage. The goal is to make tests more readable, more stable in CI, and easier to maintain.
This issue tracks several improvements that can be addressed independently.
Patterns to improve
The following patterns appear in multiple tests and can be improved to better align with Playwright best practices.
1. page.waitForFunction() with DOM queries → locator-based waiting
Using document.querySelector() inside waitForFunction() bypasses Playwright's built-in auto-waiting and retry logic.
// current pattern
await page.waitForFunction(() => {
const rows = document.querySelectorAll("table tbody tr");
return rows.length > 0;
});
// preferred
await expect(page.locator("table tbody tr")).not.toHaveCount(0);
2. page.waitForTimeout() → state-based waiting
Fixed timeouts can slow tests and may introduce flakiness in CI.
Each occurrence should be reviewed individually. In some cases the wait may be intentional
(e.g., animations or debounced inputs), so the correct replacement may vary depending on context.
// current pattern
await page.waitForTimeout(500);
// preferred — wait for the expected UI state
await expect(element).toBeVisible();
3. waitForLoadState("networkidle") → wait for specific UI state
Playwright discourages using networkidle as a general waiting strategy:
https://playwright.dev/docs/api/class-frame#frame-wait-for-load-state
It can be unreliable for modern SPAs that use polling, websockets, or other long-lived connections.
// current pattern
await page.waitForLoadState("networkidle");
// preferred — wait for the UI state you expect
await expect(page.getByRole("table")).toBeVisible();
4. Manual assertions → web-first assertions
Assertions such as expect(await locator.isVisible()).toBe(true) check the condition once.
Playwright’s web-first assertions retry automatically until the condition is met or a timeout occurs.
// current pattern
expect(await page.getByText("welcome").isVisible()).toBe(true);
const count = await rows.count();
expect(count).toBeGreaterThan(0);
// preferred
await expect(page.getByText("welcome")).toBeVisible();
await expect(rows).not.toHaveCount(0);
5. page.evaluate() for DOM manipulation → observe UI state instead
Tests should observe application state rather than modifying the DOM directly.
// current pattern
await page.evaluate(() => {
document.querySelector(".backdrop")?.remove();
});
// preferred
await expect(page.locator(".backdrop")).toBeHidden();
6. CSS :has-text() → user-facing locators
Playwright recommends user-facing locators such as getByRole() or getByText().
These tend to be more resilient to markup changes and better reflect how users interact with the UI.
// current pattern
page.locator('th:has-text("DAG ID")')
page.locator('button:has-text("Filter")')
// preferred
page.getByRole("columnheader", { name: "DAG ID" })
page.getByRole("button", { name: "Filter" })
How to test in local
breeze testing ui-e2e-tests --test-pattern <spec file name> --browser <browser name> --workers 2
<spec file name>: the test file you are working on (e.g. connections.spec.ts)
<browser name>: one of chromium, firefox, or webkit
eg.
breeze testing ui-e2e-tests --test-pattern "connections.spec.ts" --browser firefox --workers 2
Notes for contributors
- All files are under
airflow-core/src/airflow/ui/tests/e2e/
- See tests/e2e/README.md for instructions on running tests locally.
- Keeping PRs small and focused (for example, one pattern or one spec at a time) will make reviews easier.
- Not every instance is a straightforward replacement — please review the surrounding context and choose the appropriate approach.
- If you plan to work on a file, feel free to leave a comment so others know it is being worked on.
- For discussions related to this effort, contributors are welcome to join the Slack channel
#sig-ui-e2e-tests.
Some of these tasks may also be good first contributions for people interested in improving the UI test suite 🙌
Files to review
The following files may contain patterns that can be improved.
Page Objects
Specs
References
Note:
Still flaky parts
Ignore spec now
- "/dag-runs-tab.spec.ts",
- "/dag-runs.spec.ts",
- "/dag-grid-view.spec.ts",
- "/task-logs.spec.ts",
- "/dag-tasks.spec.ts",
- "/variable.spec.ts",
After Work
Committer
Motivation
Thanks to the amazing efforts of the community, the UI E2E test suite has reached solid coverage across many areas of the Airflow UI. The work tracked in the previous meta issue #59028 successfully introduced Playwright-based tests and implemented a wide range of scenarios 🎉
However, as the test suite has grown, we have started to observe some inconsistencies in testing patterns and practices across different files.
By improving consistency first, it will become easier to extend coverage, stabilize flaky tests, and maintain the E2E suite going forward.
Description
Our e2e test suite has grown over time, and we'd like to align it more closely with
Playwright's recommended best practices.
These changes are not intended to alter test coverage. The goal is to make tests more readable, more stable in CI, and easier to maintain.
This issue tracks several improvements that can be addressed independently.
Patterns to improve
The following patterns appear in multiple tests and can be improved to better align with Playwright best practices.
1.
page.waitForFunction()with DOM queries → locator-based waitingUsing
document.querySelector()insidewaitForFunction()bypasses Playwright's built-in auto-waiting and retry logic.2.
page.waitForTimeout()→ state-based waitingFixed timeouts can slow tests and may introduce flakiness in CI.
Each occurrence should be reviewed individually. In some cases the wait may be intentional
(e.g., animations or debounced inputs), so the correct replacement may vary depending on context.
3.
waitForLoadState("networkidle")→ wait for specific UI statePlaywright discourages using
networkidleas a general waiting strategy:https://playwright.dev/docs/api/class-frame#frame-wait-for-load-state
It can be unreliable for modern SPAs that use polling, websockets, or other long-lived connections.
4. Manual assertions → web-first assertions
Assertions such as
expect(await locator.isVisible()).toBe(true)check the condition once.Playwright’s web-first assertions retry automatically until the condition is met or a timeout occurs.
5.
page.evaluate()for DOM manipulation → observe UI state insteadTests should observe application state rather than modifying the DOM directly.
6. CSS
:has-text()→ user-facing locatorsPlaywright recommends user-facing locators such as
getByRole()orgetByText().These tend to be more resilient to markup changes and better reflect how users interact with the UI.
How to test in local
<spec file name>: the test file you are working on (e.g.connections.spec.ts)<browser name>: one ofchromium,firefox, orwebkiteg.
Notes for contributors
airflow-core/src/airflow/ui/tests/e2e/#sig-ui-e2e-tests.Some of these tasks may also be good first contributions for people interested in improving the UI test suite 🙌
Files to review
The following files may contain patterns that can be improved.
Page Objects
pages/AssetDetailPage.tsE2E: Improve Playwright test patterns in asset.spec.ts #63037pages/AssetListPage.tsE2E: Improve Playwright test patterns in asset.spec.ts #63037pages/BackfillPage.tsE2E: Improve Playwright test patterns in backfill.spec.ts #63041pages/BasePage.tsE2E: Improve Playwright test patterns in BasePage.ts #63038pages/ConnectionsPage.tsE2E: Improve Playwright test patterns in connections.spec.ts #63088pages/DagCalendarTab.tsE2E: Improve Playwright test patterns in dag-calendar-tab.spec.ts #63409pages/DagCodePage.tsE2E: Improve Playwright test patterns in dag-code-tab.spec.ts #63410pages/DagRunsPage.ts*flaky E2E: Improve Playwright test patterns in dag-runs.spec.ts #63168pages/DagRunsTabPage.ts*flaky E2E: Improve Playwright test patterns in dag-runs-tab.spec.ts #63165pages/DagsPage.tsE2E: Improve Playwright test patterns in dags-list.spec.ts #63426 E2E: Improve Playwright test patterns in dag-tasks.spec.ts #63425pages/EventsPage.tsE2E: Improve Playwright test patterns in events-page.spec.ts #63427pages/GridPage.tsE2E: Improve Playwright test patterns in dag-grid-view.spec.ts #63411pages/HomePage.tsE2E: Improve Playwright test patterns in home-dashboard.spec.ts #63428pages/LoginPage.tspages/PluginsPage.tsE2E: Improve Playwright test patterns in plugins.spec.ts #63429pages/PoolsPage.tsE2E: Improve Playwright test patterns in pools.spec.ts #63430pages/ProvidersPage.tsE2E: Improve Playwright test patterns in providers.spec.ts #63431pages/RequiredActionsPage.ts*flaky E2E: Improve Playwright test patterns in requiredAction.spec.ts #63432pages/TaskInstancePage.tsE2E: Improve Playwright test patterns in specs/task-instances.spec.ts #63963pages/TaskInstancesPage.tsE2E: Improve Playwright test patterns in specs/task-logs.spec.ts #63964pages/VariablePage.tsE2E: Improve Playwright test patterns in specs/variable.spec.ts #63965pages/XComsPage.tsE2E: Improve Playwright test patterns in specs/xcoms.spec.ts #63966pages/configurationpage.tsE2E: Improve Playwright test patterns in configuration.spec.ts #63087Specs
specs/asset.spec.tsE2E: Improve Playwright test patterns in asset.spec.ts #63037specs/backfill.spec.tsE2E: Improve Playwright test patterns in backfill.spec.ts #63041specs/configuration.spec.tsE2E: Improve Playwright test patterns in configuration.spec.ts #63087specs/connections.spec.tsE2E: Improve Playwright test patterns in connections.spec.ts #63088specs/dag-audit-log.spec.tsE2E: Improve Playwright test patterns in dag-audit-log.spec.ts #63408specs/dag-calendar-tab.spec.tsE2E: Improve Playwright test patterns in dag-calendar-tab.spec.ts #63409specs/dag-code-tab.spec.tsE2E: Improve Playwright test patterns in dag-code-tab.spec.ts #63410specs/dag-grid-view.spec.tsE2E: Improve Playwright test patterns in dag-grid-view.spec.ts #63411specs/dag-runs-tab.spec.ts*flaky E2E: Improve Playwright test patterns in dag-runs-tab.spec.ts #63165specs/dag-runs.spec.ts*flaky E2E: Improve Playwright test patterns in dag-runs.spec.ts #63168specs/dag-tasks.spec.tsE2E: Improve Playwright test patterns in dag-tasks.spec.ts #63425specs/dags-list.spec.tsE2E: Improve Playwright test patterns in dags-list.spec.ts #63426specs/events-page.spec.tsE2E: Improve Playwright test patterns in events-page.spec.ts #63427specs/home-dashboard.spec.tsE2E: Improve Playwright test patterns in home-dashboard.spec.ts #63428specs/plugins.spec.tsE2E: Improve Playwright test patterns in plugins.spec.ts #63429specs/pools.spec.tsE2E: Improve Playwright test patterns in pools.spec.ts #63430specs/providers.spec.tsE2E: Improve Playwright test patterns in providers.spec.ts #63431specs/requiredAction.spec.ts*flaky E2E: Improve Playwright test patterns in requiredAction.spec.ts #63432specs/task-instances.spec.tsE2E: Improve Playwright test patterns in specs/task-instances.spec.ts #63963specs/task-logs.spec.ts*flaky E2E: Improve Playwright test patterns in specs/task-logs.spec.ts #63964specs/variable.spec.tsE2E: Improve Playwright test patterns in specs/variable.spec.ts #63965specs/xcoms.spec.tsE2E: Improve Playwright test patterns in specs/xcoms.spec.ts #63966References
Note:
Still flaky parts
specs/backfill.spec.tsFix flaky backfill E2E test by targeting clickable label element #63837specs/dag-runs-tab.spec.tsFix flaky DagRunTab E2E test for Dag runs state filtering #63794 Fix flaky DagRunsTab E2E test race condition in markRunAs #63890 *Not fully updated yet.specs/dag-grid-view.spec.tsspecs/task-logs.spec.tsIgnore spec now
After Work
E2E: prevent Dag parsing races and improve Dag run cleanup #64540 (comment)
Committer