Skip to content

improve e2e tests for dag audit log#63463

Merged
choo121600 merged 8 commits into
apache:mainfrom
sjyangkevin:issues/63408/improve-e2e-dag-audit-log-spec
Mar 24, 2026
Merged

improve e2e tests for dag audit log#63463
choo121600 merged 8 commits into
apache:mainfrom
sjyangkevin:issues/63408/improve-e2e-dag-audit-log-spec

Conversation

@sjyangkevin

@sjyangkevin sjyangkevin commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

Summary

1. page.locator('[data-testid="..."]')page.getByTestId("...")**

getByTestId is a semantic, first-class Playwright API for test-id lookups. It's shorter and communicates intent clearly.

2. :has-text()getByRole()**

:has-text() is a Playwright-specific CSS pseudo-selector extension rather than standard CSS and not resilient to structural changes. getByRole queries the accessibility tree, matching how real users and assistive technologies perceive the element.

3. :has-text()getByRole().filter()

Capture column header and avoid time-out due to matching issue, narrow by visible text. The filter() method uses Playwright's internal text matching rather than a CSS :has-text().

4. filterBar, addFilter(), :has-text() button selectors → getByRole()

getByRole("button", { name: "Filter" }) queries by accessible name, which is exactly how screen readers identify buttons. For the menu items, the accessible name is the item's text, so this works directly without a CSS pseudo-selector.

5. tableRows, locator("tbody tr")locator("tbody").getByRole("row")

getByRole("row") queries by ARIA role rather than HTML tag, making it resilient to markup changes (e.g., if <tr> were ever replaced with a role="row" <div>). Chaining .locator("tbody") first scopes the search to body rows only, excluding the header row.

6. waitForTableLoad(), waitForFunction() → web-first assertions; beforeAll polling, waitForFunction()expect().toPass()

Same waitForFunction anti-pattern. expect().toPass() keeps all locator logic in Playwright's layer (with proper retries and error messages), instead of dropping into raw DOM manipulation inside a browser-evaluated function.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: [Claude Code (claude-sonnet-4-6)] following the guidelines


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@boring-cyborg boring-cyborg Bot added the area:UI Related to UI/UX. For Frontend Developers. label Mar 12, 2026
@sjyangkevin sjyangkevin force-pushed the issues/63408/improve-e2e-dag-audit-log-spec branch 2 times, most recently from d86eb8a to c192e95 Compare March 13, 2026 01:33
@sjyangkevin sjyangkevin marked this pull request as ready for review March 13, 2026 02:46
Comment thread airflow-core/src/airflow/ui/tests/e2e/pages/EventsPage.ts Outdated
Comment thread airflow-core/src/airflow/ui/tests/e2e/pages/EventsPage.ts Outdated
Comment thread airflow-core/src/airflow/ui/tests/e2e/specs/dag-audit-log.spec.ts Outdated
@sjyangkevin sjyangkevin force-pushed the issues/63408/improve-e2e-dag-audit-log-spec branch 3 times, most recently from 17ad933 to 7a46501 Compare March 15, 2026 00:23

@sjyangkevin sjyangkevin left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @choo121600 , thank you for your time to review. I found that the tests are still not very stable after making the changes in the PR. I still encountered failure from time to time, and sometime not re-produceable , and will take some time to look into it. Not sure if you've experienced with the following situations.

Image Image Image

@choo121600

Copy link
Copy Markdown
Member

@sjyangkevin
Yes, there are still quite a few flaky parts in our E2E tests.
With this meta issue, we are trying to standardize the test patterns first, and then address the flaky parts as the next step.

Could you share the command you used to run the tests?

@sjyangkevin

Copy link
Copy Markdown
Contributor Author

@sjyangkevin Yes, there are still quite a few flaky parts in our E2E tests. With this meta issue, we are trying to standardize the test patterns first, and then address the flaky parts as the next step.

Could you share the command you used to run the tests?

Thanks and agree, I use breeze to run only test for event page and for all browser.

breeze testing ui-e2e-tests --browser all --test-pattern "dag-audit-log.spec.ts"

@potiuk

potiuk commented Mar 16, 2026

Copy link
Copy Markdown
Member

@sjyangkevin This PR has a few issues that need to be addressed before it can be reviewed — please see our Pull Request quality criteria.

Issues found:

  • ⚠️ Unresolved review comments: This PR has 3 unresolved review threads from maintainers: @choo121600 (MEMBER): 3 unresolved threads. Please review and resolve all inline review comments before requesting another review. You can resolve a conversation by clicking 'Resolve conversation' on each thread after addressing the feedback. See pull request guidelines.

Note: Your branch is 31 commits behind main. Some check failures may be caused by changes in the base branch rather than by your PR. Please rebase your branch and push again to get up-to-date CI results.

What to do next:

  • The comment informs you what you need to do.
  • Fix each issue, then mark the PR as "Ready for review" in the GitHub UI - but only after making sure that all the issues are fixed.
  • There is no rush — take your time and work at your own pace. We appreciate your contribution and are happy to wait for updates.
  • Maintainers will then proceed with a normal review.

There is no rush — take your time and work at your own pace. We appreciate your contribution and are happy to wait for updates. If you have questions, feel free to ask on the Airflow Slack.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@choo121600

choo121600 commented Mar 16, 2026

Copy link
Copy Markdown
Member

Thanks and agree, I use breeze to run only test for event page and for all browser.

Running tests across multiple browsers simultaneously can cause race conditions. In particular, when several browsers check the state of the same Dag, it may lead to inconsistent results.

This is a known issue that we plan to address in the future. If you're interested, exploring possible solutions would be a great contribution :)

@choo121600 choo121600 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM :)
one nit

Comment thread airflow-core/src/airflow/ui/tests/e2e/pages/EventsPage.ts
@sjyangkevin

Copy link
Copy Markdown
Contributor Author

Thanks and agree, I use breeze to run only test for event page and for all browser.

Running tests across multiple browsers simultaneously can cause race conditions. In particular, when several browsers check the state of the same Dag, it may lead to inconsistent results.

This is a known issue that we plan to address in the future. If you're interested, exploring possible solutions would be a great contribution :)

Yes. I think I encountered similar issues when testing a change to audit log. Since we are testing multiple browsers against one Airflow server. Each browser beforeAll runs the DAGs multiple times, and cause the tests run against a changing set of data (when some tests are running, DAG is still running, and inserting records, etc.)

I feel there are multiple ways might be feasible, and happy to discuss in further details.

  • run browser tests in sequence
  • move trigger DAG Run into global setup, run before all browsers start, and we can test a static set of data

@sjyangkevin sjyangkevin force-pushed the issues/63408/improve-e2e-dag-audit-log-spec branch 2 times, most recently from 715fe60 to 52f4b12 Compare March 18, 2026 02:39
@sjyangkevin

Copy link
Copy Markdown
Contributor Author

It looks like the updates resulting in the following timeout when the tests are running against all browsers at the same time. I attempted to run each browser one-by-one, and all tests passed. 🤔

        136 |     const filterInput = this.page.getByRole("textbox", { name: filterLabel });
        137 |
      > 138 |     await expect(filterInput).toBeVisible({ timeout: 10_000 });

@choo121600

Copy link
Copy Markdown
Member

InputWithAddon in airflow-core/src/airflow/ui/src/components/ui/InputWithAddon.tsx should add aria-label={label} to the <Input> for a11y — screen readers can't identify this input.

Comment thread airflow-core/src/airflow/ui/tests/e2e/pages/EventsPage.ts Outdated
@sjyangkevin sjyangkevin force-pushed the issues/63408/improve-e2e-dag-audit-log-spec branch from d13f98c to 8587f0c Compare March 19, 2026 02:40
@linxi507

Copy link
Copy Markdown

I noticed a small improvement in dag-audit-log.spec.ts, specifically in the "verify audit log entries display valid data" test.

Currently, the test extracts text using textContent() and then asserts non-empty values. This pattern can sometimes be less stable and bypasses Playwright’s built-in retry logic.

It might be more robust to use locator-based assertions instead, for example:

await expect(whenCell).toHaveText(/\S+/);
await expect(eventCell).toHaveText(/\S+/);
await expect(userCell).toHaveText(/\S+/);

This keeps the assertions within Playwright’s retry mechanism and could potentially help with some of the intermittent stability issues mentioned above.

Happy to help implement this if it sounds reasonable 🙂

@sjyangkevin

Copy link
Copy Markdown
Contributor Author

I will push one small change to improve the audit log show valid data, to use more explicit matching pattern instead of verifying only not empty string, during the weekend, bandwidth a bit limited this week. Thanks

@sjyangkevin

Copy link
Copy Markdown
Contributor Author

I am facing some issues with CI locally, and wasn't able to push the changes, working on it.

@sjyangkevin sjyangkevin force-pushed the issues/63408/improve-e2e-dag-audit-log-spec branch from 0536a19 to df8fefc Compare March 24, 2026 01:14
setFilterValue() previously used getByPlaceholder(filterLabel) to find the
filter input after selecting a filter from the menu. This never matched
because InputWithAddon renders the label as a visible <Text> sibling of the
<input>, not as a placeholder attribute. The filter configs (e.g. EVENT_TYPE,
DAG_ID) do not define a placeholder field, so filter.config.placeholder is
undefined and React omits the attribute from the DOM entirely.

The fix uses the text-matching approach already established in XComsPage:

  page.locator("div").filter({ hasText: `${filterLabel}:` }).locator("input").first()

Note: getByPlaceholder() could be restored once the relevant filter configs
in filterConfigs.tsx expose a placeholder value, or once InputWithAddon gains
an aria-label prop (enabling the preferred getByLabel() approach). Both
require a coordinated source change discussed with UI maintainers.

Additional stability improvements:
- Raise addFilter() menu visibility timeout 5s → 10s (CI headroom)
- Raise setFilterValue() input visibility timeout 5s → 10s (CI headroom)
- Raise dag-audit-log individual test timeout 60s → 120s; waitForTableLoad()
  can consume up to 60s internally, leaving no room for navigation + assertions
  under the old limit on loaded CI runners
@sjyangkevin sjyangkevin force-pushed the issues/63408/improve-e2e-dag-audit-log-spec branch from df8fefc to a32d643 Compare March 24, 2026 02:17
@sjyangkevin sjyangkevin requested a review from choo121600 March 24, 2026 11:59

@choo121600 choo121600 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool LGTM 🙌
@sjyangkevin Thanks for Contribution :)

@choo121600 choo121600 merged commit 6c97aed into apache:main Mar 24, 2026
151 of 153 checks passed
@sjyangkevin

Copy link
Copy Markdown
Contributor Author

Cool LGTM 🙌 @sjyangkevin Thanks for Contribution :)

Thanks for all the feedback and review! I also learned new things from it!

vatsrahul1001 pushed a commit that referenced this pull request Mar 25, 2026
* improve e2e tests for dag audit log

* refine matching on filter

* improve by never snapshot the DOM to assert against the snapshot

* fix skeleton load

* Fix filter input locator and stabilize dag audit log e2e tests

setFilterValue() previously used getByPlaceholder(filterLabel) to find the
filter input after selecting a filter from the menu. This never matched
because InputWithAddon renders the label as a visible <Text> sibling of the
<input>, not as a placeholder attribute. The filter configs (e.g. EVENT_TYPE,
DAG_ID) do not define a placeholder field, so filter.config.placeholder is
undefined and React omits the attribute from the DOM entirely.

The fix uses the text-matching approach already established in XComsPage:

  page.locator("div").filter({ hasText: `${filterLabel}:` }).locator("input").first()

Note: getByPlaceholder() could be restored once the relevant filter configs
in filterConfigs.tsx expose a placeholder value, or once InputWithAddon gains
an aria-label prop (enabling the preferred getByLabel() approach). Both
require a coordinated source change discussed with UI maintainers.

Additional stability improvements:
- Raise addFilter() menu visibility timeout 5s → 10s (CI headroom)
- Raise setFilterValue() input visibility timeout 5s → 10s (CI headroom)
- Raise dag-audit-log individual test timeout 60s → 120s; waitForTableLoad()
  can consume up to 60s internally, leaving no room for navigation + assertions
  under the old limit on loaded CI runners

* use getByRole instead of hasText

* fix filterInput, locate the container and get text

* improve verify audit log entries by explicit matching patterns
nailo2c pushed a commit to nailo2c/airflow that referenced this pull request Mar 30, 2026
* improve e2e tests for dag audit log

* refine matching on filter

* improve by never snapshot the DOM to assert against the snapshot

* fix skeleton load

* Fix filter input locator and stabilize dag audit log e2e tests

setFilterValue() previously used getByPlaceholder(filterLabel) to find the
filter input after selecting a filter from the menu. This never matched
because InputWithAddon renders the label as a visible <Text> sibling of the
<input>, not as a placeholder attribute. The filter configs (e.g. EVENT_TYPE,
DAG_ID) do not define a placeholder field, so filter.config.placeholder is
undefined and React omits the attribute from the DOM entirely.

The fix uses the text-matching approach already established in XComsPage:

  page.locator("div").filter({ hasText: `${filterLabel}:` }).locator("input").first()

Note: getByPlaceholder() could be restored once the relevant filter configs
in filterConfigs.tsx expose a placeholder value, or once InputWithAddon gains
an aria-label prop (enabling the preferred getByLabel() approach). Both
require a coordinated source change discussed with UI maintainers.

Additional stability improvements:
- Raise addFilter() menu visibility timeout 5s → 10s (CI headroom)
- Raise setFilterValue() input visibility timeout 5s → 10s (CI headroom)
- Raise dag-audit-log individual test timeout 60s → 120s; waitForTableLoad()
  can consume up to 60s internally, leaving no room for navigation + assertions
  under the old limit on loaded CI runners

* use getByRole instead of hasText

* fix filterInput, locate the container and get text

* improve verify audit log entries by explicit matching patterns
Suraj-kumar00 pushed a commit to Suraj-kumar00/airflow that referenced this pull request Apr 7, 2026
* improve e2e tests for dag audit log

* refine matching on filter

* improve by never snapshot the DOM to assert against the snapshot

* fix skeleton load

* Fix filter input locator and stabilize dag audit log e2e tests

setFilterValue() previously used getByPlaceholder(filterLabel) to find the
filter input after selecting a filter from the menu. This never matched
because InputWithAddon renders the label as a visible <Text> sibling of the
<input>, not as a placeholder attribute. The filter configs (e.g. EVENT_TYPE,
DAG_ID) do not define a placeholder field, so filter.config.placeholder is
undefined and React omits the attribute from the DOM entirely.

The fix uses the text-matching approach already established in XComsPage:

  page.locator("div").filter({ hasText: `${filterLabel}:` }).locator("input").first()

Note: getByPlaceholder() could be restored once the relevant filter configs
in filterConfigs.tsx expose a placeholder value, or once InputWithAddon gains
an aria-label prop (enabling the preferred getByLabel() approach). Both
require a coordinated source change discussed with UI maintainers.

Additional stability improvements:
- Raise addFilter() menu visibility timeout 5s → 10s (CI headroom)
- Raise setFilterValue() input visibility timeout 5s → 10s (CI headroom)
- Raise dag-audit-log individual test timeout 60s → 120s; waitForTableLoad()
  can consume up to 60s internally, leaving no room for navigation + assertions
  under the old limit on loaded CI runners

* use getByRole instead of hasText

* fix filterInput, locate the container and get text

* improve verify audit log entries by explicit matching patterns
abhijeets25012-tech pushed a commit to abhijeets25012-tech/airflow that referenced this pull request Apr 9, 2026
* improve e2e tests for dag audit log

* refine matching on filter

* improve by never snapshot the DOM to assert against the snapshot

* fix skeleton load

* Fix filter input locator and stabilize dag audit log e2e tests

setFilterValue() previously used getByPlaceholder(filterLabel) to find the
filter input after selecting a filter from the menu. This never matched
because InputWithAddon renders the label as a visible <Text> sibling of the
<input>, not as a placeholder attribute. The filter configs (e.g. EVENT_TYPE,
DAG_ID) do not define a placeholder field, so filter.config.placeholder is
undefined and React omits the attribute from the DOM entirely.

The fix uses the text-matching approach already established in XComsPage:

  page.locator("div").filter({ hasText: `${filterLabel}:` }).locator("input").first()

Note: getByPlaceholder() could be restored once the relevant filter configs
in filterConfigs.tsx expose a placeholder value, or once InputWithAddon gains
an aria-label prop (enabling the preferred getByLabel() approach). Both
require a coordinated source change discussed with UI maintainers.

Additional stability improvements:
- Raise addFilter() menu visibility timeout 5s → 10s (CI headroom)
- Raise setFilterValue() input visibility timeout 5s → 10s (CI headroom)
- Raise dag-audit-log individual test timeout 60s → 120s; waitForTableLoad()
  can consume up to 60s internally, leaving no room for navigation + assertions
  under the old limit on loaded CI runners

* use getByRole instead of hasText

* fix filterInput, locate the container and get text

* improve verify audit log entries by explicit matching patterns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:UI Related to UI/UX. For Frontend Developers.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

E2E: Improve Playwright test patterns in events-page.spec.ts E2E: Improve Playwright test patterns in dag-audit-log.spec.ts

4 participants