Skip to content

fix(playwright): stabilize flaky CustomMetric, TestSuiteMultiPipeline, BulkEditEntity tests#30207

Open
ShaileshParmar11 wants to merge 2 commits into
open-metadata:mainfrom
ShaileshParmar11:fix/playwright-flaky-table-etag-stale-reads
Open

fix(playwright): stabilize flaky CustomMetric, TestSuiteMultiPipeline, BulkEditEntity tests#30207
ShaileshParmar11 wants to merge 2 commits into
open-metadata:mainfrom
ShaileshParmar11:fix/playwright-flaky-table-etag-stale-reads

Conversation

@ShaileshParmar11

@ShaileshParmar11 ShaileshParmar11 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

De-flakes three Playwright specs that intermittently fail in AUT (worse under load). All fixes are test-side only — no app or backend changes.

1. CustomMetric & TestSuiteMultiPipeline — stale table reads

Cause: a child mutation — PUT /tables/{id}/customMetric, or the executable test suite created when the first test case is added — does not bump the parent table's version/updatedAt. The entity ETag is hash(version + "-" + updatedAt), so it is unchanged after the write. The client's conditional refetch (If-None-Match, via etagInterceptor.ts) then gets a not-modified response and the UI renders the stale table body — the new custom metric / the add-pipeline-button never appears and the test times out.

Trace evidence: the failing /api/v1/tables/name/... GETs carry If-None-Match and return bodySize: 0, and the served body is the pre-write one (customMetrics: 0 while the PUT response has customMetrics: 1, both at table version 0.1 / same updatedAt).

Fix: a forceFreshTableReads(page) helper strips If-None-Match on **/api/v1/tables/** at the Playwright network layer, so table reads always return the current body. Scoped to table reads only.

2. BulkEditEntity › Glossary Term (Nested) — wrong export scope

Cause: the test asserts processed: 1 but intermittently saw 5 / 2 / 4. The bulk-edit grid export is scoped by the activeGlossary store value (GlossaryTermTab.handleEditGlossary), which could be stale when bulk-edit-table is clicked — exporting a wrong, larger term set. The backend export for a term is correctly FQN-scoped, so a fresh parent-with-one-child should be exactly 1.

Fix:

  • Wait for the nested term to be indexed before bulk-edit (matches the sibling glossary test).
  • Scope the export wait to the parent term's FQN so the grid can't load a wrong scope.
  • Assert the grid has exactly one row before importing, so a wrong-scope export fails fast instead of surfacing as a confusing processed-row count.

Validation

Ran locally against a live server:

  • CustomMetric / TestSuiteMultiPipeline: pass.
  • BulkEditEntity › Glossary Term (Nested): 3/3 green (--repeat-each + reruns), --workers=1 --retries=0.
  • Checkstyle clean (organize-imports + eslint + prettier).

Notes

These contain the test flakes. The underlying product behavior in (1) — a table GET can serve a stale body after a child mutation that doesn't bump the table version — is left as a separate follow-up.

🤖 Generated with Claude Code

Greptile Summary

This test-only PR adds two targeted de-flaking mechanisms for Playwright E2E tests: a forceFreshTableReads route interceptor that strips If-None-Match headers on /api/v1/tables/** requests so stale ETag cache hits cannot cause the UI to render pre-mutation table state, and a waitForSearchIndexed + scoped waitForResponse pair in BulkEditEntity to ensure the glossary search index is current and the bulk-edit export is scoped to the correct parent term before assertions run.

  • entity.ts: New forceFreshTableReads(page) utility registers a Playwright route handler that deletes the if-none-match header from all table API requests, so the server always returns a fresh body instead of a 304 Not Modified when table child mutations (custom metrics, executable test suites) don't bump the ETag.
  • CustomMetric.spec.ts / TestSuiteMultiPipeline.spec.ts: Both specs call forceFreshTableReads immediately after redirectToHomePage, scoping the bypass to table reads only.
  • BulkEditEntity.spec.ts: Adds a waitForSearchIndexed call before navigating to the bulk-edit view and an FQN-scoped waitForResponse wait after clicking bulk-edit, plus a .rdg-row count assertion to fail fast when the wrong scope is used.

Confidence Score: 5/5

Test-only change with no production code modifications; safe to merge.

All changes are confined to Playwright test utilities and specs. The forceFreshTableReads helper is narrowly scoped to table API requests and uses a well-established Playwright route interception pattern already present throughout the codebase. No application or backend code is touched.

No files require special attention.

Important Files Changed

Filename Overview
openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts Adds forceFreshTableReads utility that intercepts all /api/v1/tables/** requests and strips the If-None-Match header, preventing stale 304 responses after child mutations; implementation is correct and well-documented.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/CustomMetric.spec.ts Adds forceFreshTableReads after redirectToHomePage in both table-level and column-level custom metric tests; minimal, correct change.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TestSuiteMultiPipeline.spec.ts Adds forceFreshTableReads after redirectToHomePage in both multi-pipeline test cases; follows the same pattern as CustomMetric.spec.ts.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BulkEditEntity.spec.ts Adds search-index wait for the nested glossary term, an FQN-scoped export response wait before the bulk-edit assertion, and a row-count guard; all consistent with established codebase patterns.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant PW as Playwright Test
    participant RT as Route Interceptor
    participant UI as Browser / UI
    participant API as /api/v1/tables/**

    Note over PW: forceFreshTableReads registered
    PW->>RT: "page.route('**/api/v1/tables/**')"

    PW->>UI: navigate / trigger mutation (PUT customMetric)
    UI->>API: "PUT /tables/{id}/customMetric"
    API-->>UI: 200 OK (customMetrics: 1, version still 0.1)

    UI->>RT: GET /tables/name/... (If-None-Match: etag_v0.1)
    Note over RT: delete headers['if-none-match']
    RT->>API: GET /tables/name/... (no If-None-Match)
    API-->>RT: 200 OK (full body, customMetrics: 1)
    RT-->>UI: fresh response

    Note over PW: Without interceptor
    UI->>API: GET /tables/name/... (If-None-Match: etag_v0.1)
    API-->>UI: 304 Not Modified (bodySize: 0)
    Note over UI: stale body rendered, assertion timeout
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant PW as Playwright Test
    participant RT as Route Interceptor
    participant UI as Browser / UI
    participant API as /api/v1/tables/**

    Note over PW: forceFreshTableReads registered
    PW->>RT: "page.route('**/api/v1/tables/**')"

    PW->>UI: navigate / trigger mutation (PUT customMetric)
    UI->>API: "PUT /tables/{id}/customMetric"
    API-->>UI: 200 OK (customMetrics: 1, version still 0.1)

    UI->>RT: GET /tables/name/... (If-None-Match: etag_v0.1)
    Note over RT: delete headers['if-none-match']
    RT->>API: GET /tables/name/... (no If-None-Match)
    API-->>RT: 200 OK (full body, customMetrics: 1)
    RT-->>UI: fresh response

    Note over PW: Without interceptor
    UI->>API: GET /tables/name/... (If-None-Match: etag_v0.1)
    API-->>UI: 304 Not Modified (bodySize: 0)
    Note over UI: stale body rendered, assertion timeout
Loading

Reviews (2): Last reviewed commit: "test(playwright): de-flake Glossary Term..." | Re-trigger Greptile

…fresh table reads

These two specs intermittently fail in AUT because a child mutation
(custom metric via PUT /tables/{id}/customMetric, or an executable test
suite created when the first test case is added) does not bump the parent
table's version/updatedAt. The entity ETag is hash(version + "-" + updatedAt),
so it is unchanged after the write, the client's conditional refetch matches
If-None-Match, and the UI renders a stale table body — the new custom metric
or the add-pipeline CTA never appears and the test times out.

Add a `forceFreshTableReads` helper that strips the `If-None-Match` header on
`/api/v1/tables/**` requests at the Playwright network layer, so table reads
always return the current body. Scoped to table reads only; no app or backend
changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ShaileshParmar11
ShaileshParmar11 requested a review from a team as a code owner July 18, 2026 17:59
Copilot AI review requested due to automatic review settings July 18, 2026 17:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

🟡 Playwright Results — all passed (30 flaky)

✅ 4541 passed · ❌ 0 failed · 🟡 30 flaky · ⏭️ 95 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 433 0 7 16
✅ Shard 2 11 0 0 0
🟡 Shard 3 824 0 8 8
🟡 Shard 4 821 0 2 18
🟡 Shard 5 840 0 1 5
🟡 Shard 6 783 0 5 46
🟡 Shard 7 829 0 7 2
🟡 30 flaky test(s) (passed on retry)
  • Features/DataAssetRulesDisabled.spec.ts › Database Schema (shard 1, 1 retry)
  • Features/Glossary/GlossaryPagination.spec.ts › should check for nested glossary term search (shard 1, 1 retry)
  • Features/Pagination.spec.ts › should test API Collection normal pagination (shard 1, 1 retry)
  • Flow/TestConnectionModal.spec.ts › modal opens with gate card and capability checks sections (shard 1, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab IS visible for supported type: metric (shard 1, 1 retry)
  • Pages/SearchSettings.spec.ts › Preview config reflects reverted n-gram weight after save (shard 1, 1 retry)
  • Flow/SearchRBAC.spec.ts › a fully denied user sees neither asset type when browsing (shard 1, 1 retry)
  • Features/BulkEditEntity.spec.ts › Glossary Term (Nested) (shard 3, 1 retry)
  • Features/BulkImportWithDotInName.spec.ts › Import at database level with dot in service name (shard 3, 1 retry)
  • Features/ContextCenterArchive.spec.ts › archive page lazy-loads more rows on scroll within its own scroll container (shard 3, 1 retry)
  • Features/ContextCenterArticles.spec.ts › Article listing search filters, clears, and shows empty state (shard 3, 1 retry)
  • Features/ContextCenterArticles.spec.ts › Article list cards, recently viewed widget, and pagination work (shard 3, 1 retry)
  • Features/ContextCenterArticles.spec.ts › description: switching articles does not bleed unsaved content into next article (shard 3, 1 retry)
  • Features/ContextCenterMemories.spec.ts › adding a linked asset in edit mode shows entity badge on the row (shard 3, 1 retry)
  • Features/ContextCenterMemories.spec.ts › typing the linked table name in the asset search returns it as a result (shard 3, 1 retry)
  • Features/Glossary/GlossaryTermRelationsGraphNested.spec.ts › viewing a child term: parentOf edge is rendered between parent and child (shard 4, 1 retry)
  • Features/Table.spec.ts › should persist page size (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Table (shard 5, 1 retry)
  • Pages/Entity.spec.ts › User as Owner with unsorted list (shard 6, 1 retry)
  • Pages/Entity.spec.ts › User as Owner with unsorted list (shard 6, 1 retry)
  • Pages/Entity.spec.ts › Inactive Announcement create & delete (shard 6, 1 retry)
  • Pages/EntityDataConsumer.spec.ts › Tier Add, Update and Remove (shard 6, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 6, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should allow Data Steward to edit tags for searchIndex (shard 7, 1 retry)
  • Pages/Glossary.spec.ts › Approve and reject glossary term from Glossary Listing (shard 7, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Import partial success - some terms pass, some fail (shard 7, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify Impact Analysis service filter selection (shard 7, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab is NOT visible for pipelineService in platform lineage (shard 7, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab is NOT visible for apiService in platform lineage (shard 7, 1 retry)
  • Pages/TestSuite.spec.ts › Logical TestSuite (shard 7, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

The Nested bulk-edit test asserted processed:1 but intermittently saw 5/2/4
because the bulk-edit grid export is scoped by the activeGlossary store value,
which could be stale when bulk-edit-table is clicked — exporting a wrong,
larger term set.

- Wait for the nested term to be indexed before bulk-edit (matches sibling).
- Scope the export wait to the parent term's FQN so the grid can't load a
  wrong scope.
- Assert the grid has exactly one row before importing, so a wrong-scope
  export fails fast instead of surfacing as a confusing processed-row count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 19, 2026 06:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ShaileshParmar11 ShaileshParmar11 changed the title test(playwright): de-flake CustomMetric & TestSuiteMultiPipeline via fresh table reads test(playwright): de-flake CustomMetric, TestSuiteMultiPipeline & BulkEditEntity Nested Jul 19, 2026
@gitar-bot

gitar-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Adds a helper to bypass stale ETag-based responses for table API calls, effectively de-flaking the CustomMetric and TestSuiteMultiPipeline Playwright tests. No issues found.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@ShaileshParmar11 ShaileshParmar11 changed the title test(playwright): de-flake CustomMetric, TestSuiteMultiPipeline & BulkEditEntity Nested fix(playwright): stabilize flaky CustomMetric, TestSuiteMultiPipeline, BulkEditEntity tests Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants