Skip to content

Fixes #33087: validate full Playwright coverage and repair CI races - #33088

Open
harshach wants to merge 165 commits into
mainfrom
harshach/debug-merge-queue
Open

harshach wants to merge 165 commits into
mainfrom
harshach/debug-merge-queue

Conversation

@harshach

@harshach harshach commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Describe your changes:

Fixes #33087

PRs could reach the merge queue with selected Playwright coverage, where full validation exposed shared-helper defects and backend races. Run the full suite on authorized PR pushes, repair those defects, and retain first-attempt failures without test retries or quarantine.

Type of change:

  • Bug fix

High-level design:

  • Coverage and CI: full PostgreSQL, RDF, visual, and data-access checks for authorized PR pushes; existing fork/draft gates retained. Merge groups verify their commit and complete native execution evidence locally, without diagnostic uploads or HTML/blob generation. PR artifacts remain available; missing or invalid execution evidence still fails the required summary. Five-build queue concurrency is unchanged.
  • Test design: checked fixture setup/cleanup; response listeners matched to the actual request; fresh execution/version polling; stable task, dropdown, grid, navigation, and page-size interactions; guards against repeated mutations and ignored first responses. Fix undefined search-index mapping, remounted query-builder controls, Following-widget IDs, native grid editors, and accordion/task selectors.
  • Backend and ingestion: Airflow 3 deploy waits for fresh registered/serialized DAG metadata before the first trigger; metadata-only pipeline deletion does not require a runner. Correct search-query encoding, stale read/pagination handling, contract result lookup by execution ID, ontology declaration for table aliases, and retry-queue test isolation.
  • Diagnosis and performance: fingerprint-validated fixture warming, bounded request/latency metrics, paginated failure attribution, raw first-attempt reporting, and a complete source inventory: 395 E2E specs, 663 TypeScript files, 2,900 static test declarations. See the audit and failure evidence and recovery criteria.
  • Fresh-runner fixes: build the linked component library and gate browser helpers once during planning; overlap planning with the distribution build. Stable visual fixtures replace random glossary content and the stale open-menu baseline, at unchanged screenshot thresholds. Airflow scanners use spawn instead of unsafe threaded fork, and session-owned test initialization replaces deletion of the shared database.
  • CodeQL: isolate component-library dependency installation from the CI runner, keep its package cache bounded, and store dependencies/build outputs under RUNNER_TEMP; source stays read-only and builds/browser checks run without network access. No alert suppression.
  • Coverage reporting: remove the obsolete Sonar path rewrite that discarded coverage for 47 Airflow files despite passing tests. The standalone repair in #33155 must merge first because pull_request_target executes the workflow from main; then synchronize this PR with main for a fresh validation.

Tests run:

Unit tests

  • 230 CI-script tests
  • 50 backend tests: 43 ingestion repository + seven RDF ontology contracts
  • Seven search-API tests covering reserved characters
  • 45 HTTP/helper tests, including first-response errors and execution identity

Backend integration tests

  • Earlier validation: four DataContractResourceIT result tests against PostgreSQL/Elasticsearch; both new result-ID regressions reproduced before repair

Ingestion integration tests

  • Earlier validation: 23 Airflow tests on each of PostgreSQL and SQLite, including concurrent deployments, rejected/invalid DAGs, lifecycle cleanup, and immediate triggering
  • Current full Airflow API suite: 41 passed in 63 seconds on Linux, Airflow 3.3.1, Python 3.12, SQLite, and filelock 3.32.6; shared-database deletion and file-lock startup failures reproduced before repair; original test order passes afterward. GitHub Python 3.10 / Airflow 3.3.1 validation at c3305dea56: all 41 tests passed in run 34487109427; the Sonar analysis discarded their coverage due to the base-workflow path rewrite
  • The deploy-success/first-trigger failure reproduced before the readiness change and passes afterward

Playwright (UI) tests

  • Fresh-checkout mount regression: reproduced Docker exit 125 with no core dependency/build directories; corrected preflight passes dependency installation, library build, and 15 browser-helper tests with zero retries against a Git-exported checkout

  • After the main merge: 124 planning tests, 117 lint-rule tests, Playwright lint, and discovery of 4,574 instances pass. The revised container preflight passes a clean dependency install, library build, and 15 browser-helper tests; runtime isolation probes pass

  • The 50-repetition results and full Maven build below were recorded at 56a4fcc4da, before the latest main merge

  • Linux Playwright 1.57: 15 browser-helper scenarios × 50 repetitions = 750 passes in 1.6 minutes, three workers, zero retries

  • All 15 visual scenarios × 50 repetitions = 750 passes in 8.7 minutes against the current-source local Docker stack, three workers, zero retries; only two affected baselines refreshed, with unchanged thresholds

  • Full Maven distribution build passed in 5:05; isolated component-library preflight build passed in 7 seconds

  • Local Select/ComboBox and slider reproductions

  • Full discovery: 4,571 configured test instances, no collection errors

  • Java/UI formatting and license checks pass. No new Playwright type diagnostic signatures: 165 current versus 166 on main

The full GitHub run on the updated commit is pending. The merge of main includes #33079 and its previously missing DataInsightsExtension; a fresh Collate compatibility run is pending. The stored-procedure read-after-delete failure observed in #32919 still needs deterministic cache-race reproduction.

Operational acceptance remains 50 affected full-scenario repetitions on GitHub under normal concurrency, cold and warm, with zero retries; then 20 complete consecutive merge-group validations and monitoring of the next 100. The local visual repetitions reuse one seeded environment; they do not establish complete-suite or cold/warm GitHub acceptance. No 100% first-pass or throughput improvement is claimed yet.

UI screen recording / screenshots:

No visual design change. Browser reproductions and CI trace evidence are documented in the audit.

Checklist:

  • CONTRIBUTING reviewed
  • Tracking issue linked and title formatted
  • Design, audit scope, evidence, and remaining failures documented
  • Regression tests added and actual validation results reported
  • Full GitHub repetition/merge-queue acceptance
  • UI recording attached
Greptile Summary

This PR substantially expands full Playwright validation on authorized pull requests and merge groups while repairing CI races, test-fixture isolation, backend readiness, search encoding, data-contract result lookup, and UI interaction stability.

  • Runs broader PostgreSQL, RDF, visual, and data-access coverage with stricter native execution-evidence validation.
  • Adds CI planning, artifact normalization, performance reporting, cache management, and merge-queue diagnostics.
  • Hardens Playwright helpers and fixtures against stale responses, repeated mutations, remounts, pagination, and asynchronous backend state.
  • Repairs Airflow deployment readiness, metadata-only deletion, data-contract execution lookup, RDF declarations, and retry-queue isolation.
  • The only change since the previous review documents why the lineage breadcrumb uses a native title instead of a nested design-system tooltip; it does not alter behavior.
Confidence Score: 5/5

No new actionable issue was introduced since the previous review, and the PR appears safe to merge based on the reviewed changes.

The only post-review change adds explanatory comments around existing lineage breadcrumb markup. The merge-group cache adjustment is self-contained because every consuming job can rebuild its environment on a miss, and no outstanding previous findings were supplied.

Important Files Changed
Filename Overview
.github/workflows/playwright-e2e-reusable.yml Expands reusable Playwright orchestration, merge-group validation, artifact handling, and native execution-evidence checks.
.github/scripts/build_playwright_shards.py Updates shard planning to support complete coverage and revised CI execution modes.
.github/scripts/render_playwright_summary.cjs Strengthens result normalization and required-summary validation for first-attempt execution evidence.
openmetadata-airflow-apis/openmetadata_managed_apis/operations/deploy.py Waits for fresh Airflow DAG registration and serialization before deployment is considered trigger-ready.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DataContractRepository.java Corrects data-contract result retrieval to use execution identity.
openmetadata-ui/src/main/resources/ui/playwright/utils/apiResponse.ts Hardens response waiting and error handling used across Playwright tests.
openmetadata-ui/src/main/resources/ui/src/components/Lineage/LineageMap/LineageMap.component.tsx Adds rationale for the existing native breadcrumb title and its targeted lint suppression without changing runtime behavior.
Sequence Diagram
sequenceDiagram
    participant PR as Pull request / merge group
    participant Plan as Playwright planning
    participant Build as Distribution and fixture build
    participant E2E as Native Playwright shards
    participant Artifacts as Evidence normalization
    participant Summary as Required summary

    PR->>Plan: Determine authorized full coverage
    par Planning and build
      Plan->>Plan: Discover and validate test inventory
      Build->>Build: Build distribution and isolated UI dependencies
    end
    Plan->>E2E: PostgreSQL, RDF, visual, and data-access plans
    Build->>E2E: Server and stable fixtures
    E2E->>Artifacts: Raw first-attempt results and execution evidence
    Artifacts->>Summary: Validate completeness and attribution
    Summary-->>PR: Pass only with valid native execution evidence
Loading

Reviews (3): Last reviewed commit: "fix(ui): justify the lineage breadcrumb'..." | Re-trigger Greptile

Copilot AI lite review requested due to automatic review settings September 9, 2026 18:43
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

✅ PR checks passed

The linked issue has a description and all required Shipping project fields set. Thanks!

@github-actions github-actions Bot added backend safe to test Add this label to run secure Github workflows on PRs labels Sep 9, 2026

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.

🟡 Changes recommended

There are verified issues in the new CI artifact download recovery logic and an API-contract mismatch in test pipeline creation that should be corrected before merge.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR targets merge-queue reliability by hardening GitHub Actions artifact transport/recovery and by removing common Playwright/E2E races (selection, search readiness, polling, and deterministic cleanup), while also isolating flaky shared dependencies (AutoPilot MySQL source, Airflow DAG lifecycle, and retry-queue worker interference).

Changes:

  • Add bounded artifact upload/download recovery + execution/commit integrity normalization, and tighten merge-group gating to require verifiable execution evidence.
  • Stabilize Playwright helpers/specs (focus-first selection, response predicates, execution-aware polling for contracts/AutoPilot, deterministic API context disposal/cleanup).
  • Improve supporting infrastructure/tests: dedicated AutoPilot MySQL fixture, Airflow DAG deploy/delete behavior + integration tests, retry-queue IT isolation, and richer server-latency/performance reporting.
File summaries
File Description
openmetadata-ui/src/main/resources/ui/playwright/utils/serviceFormUtils.ts Stabilize select interactions via focus and post-selection assertions.
openmetadata-ui/src/main/resources/ui/playwright/utils/search.ts Use page-scoped API context for domain asset-count gating; fail fast on HTTP errors.
openmetadata-ui/src/main/resources/ui/playwright/utils/dataContracts.ts Refactor contract validation/polling with stricter response matching and execution-id-based polling.
openmetadata-ui/src/main/resources/ui/playwright/utils/contractExecution.ts New API polling helper for contract execution results with strict validation.
openmetadata-ui/src/main/resources/ui/playwright/utils/contractExecution.test.ts New helper tests for contract execution polling behaviors.
openmetadata-ui/src/main/resources/ui/playwright/utils/autopilotExecution.ts New API polling helper for AutoPilot workflow instance completion and error surfacing.
openmetadata-ui/src/main/resources/ui/playwright/utils/autopilotExecution.test.ts New helper tests for AutoPilot polling scenarios.
openmetadata-ui/src/main/resources/ui/playwright/utils/AutoPilot.ts Switch AutoPilot status checks to execution-aware polling via new helper.
openmetadata-ui/src/main/resources/ui/playwright/support/fixtures/serverLoad.ts Clarify routing/cache implications when intercepting requests under server-load fixture.
openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts Allow configurable test-suite pipeline schedule interval for redeploy scenarios.
openmetadata-ui/src/main/resources/ui/playwright/support/entity/ingestion/ServiceBaseClass.ts Remove unnecessary routing and stabilize runner selection assertions.
openmetadata-ui/src/main/resources/ui/playwright/support/entity/ingestion/MySqlIngestionClass.ts Allow per-test injected MySQL connection settings (supports dedicated AutoPilot source).
openmetadata-ui/src/main/resources/ui/playwright/helper-tests.config.ts New Playwright config to run helper/unit-like tests in CI.
openmetadata-ui/src/main/resources/ui/playwright/eslint-rules/tests/corpus.test.mjs Update baseline counts after locator-rule improvements.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts Simplify contract validation visibility assertion based on new deterministic polling.
openmetadata-ui/src/main/resources/ui/playwright/e2e/PageObject/Explore/OverviewPageObject.ts Replace forced click with retryable open-and-assert flow for owner picker.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/MetricListSearch.spec.ts Derive restored count from clear response; add response shape checks.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/IngestionBot.spec.ts Ensure API context is always disposed and gate asset picker on indexing.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TestSuitePipelineRedeploy.spec.ts Make fixture creation deterministic, assert per-pipeline deploy responses, add cleanup.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/GlobalPageSize.spec.ts Stabilize page-size changes with response predicates and retryable menu interactions.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/TestLibrary.spec.ts Use retryable select option helper; assert visible trigger text for selection.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterMemories.spec.ts Tighten search response waiting and scope options to popover to avoid locator collisions.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/AutoPilot.spec.ts Add dedicated AutoPilot MySQL support, execution-aware polling, and stronger cleanup.
openmetadata-ui/src/main/resources/ui/eslint-suppressions.json Remove suppressions made unnecessary by locator/test fixes.
openmetadata-ui-core-components/src/main/resources/ui/src/components/base/select/select.test.tsx New regression test for Select behavior in modal/unfocused trigger scenarios.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexRetryQueueIT.java Isolate retry-queue IT from app worker; add stability test asserting no worker interference.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/bootstrap/TestSuiteBootstrap.java Add accessor to retrieve the search-index retry worker managed object.
openmetadata-airflow-apis/tests/unit/test_operation_diagnostics.py Adjust unit test stubs to align with new Airflow deletion path.
openmetadata-airflow-apis/tests/integration/operations/test_dag_lifecycle.py New Airflow 3.x integration tests for versioned DAG deletion, invalid DAG deploy, concurrency.
openmetadata-airflow-apis/openmetadata_managed_apis/operations/deploy.py Ensure deploy fails when DAG isn’t actually loaded; improve exception logging.
openmetadata-airflow-apis/openmetadata_managed_apis/operations/delete.py Delegate DAG deletion to Airflow’s delete_dag to respect FK ordering and running-task rejection.
docs/index.md Index new merge-queue recovery documentation.
docker/development/playwright-autopilot-mysql.sql New SQL fixture for isolated AutoPilot MySQL source.
.github/workflows/populate-playwright-caches.yml Adjust cache-warmer concurrency behavior (no cancel-in-progress).
.github/workflows/playwright-postgresql-e2e.yml Use retry-capable artifact downloads; add normalization + native-evidence coverage requirement.
.github/workflows/playwright-e2e-reusable.yml Use retryable artifact transport; run helper polling tests; extend diagnostics and execution status stamping.
.github/workflows/merge-queue-daily-report.yml Grant actions: read for richer reporting.
.github/workflows/integration-tests-postgres-opensearch.yml Rename retry artifact to avoid ghost-name collision; use retryable downloads.
.github/workflows/integration-tests-mysql-elasticsearch.yml Rename retry artifact to avoid ghost-name collision; use retryable downloads.
.github/scripts/verify_playwright_coverage.py Add native-evidence verification option and quarantine inventory accounting.
.github/scripts/tests/test_playwright_server_latency.py New unit tests for bounded latency histogram behavior and merge semantics.
.github/scripts/tests/test_playwright_native_coverage.py New unit tests for native-evidence coverage verification.
.github/scripts/tests/test_playwright_ci_planning.py Update merge-group summary expectations to require execution evidence + integrity; add conflict test.
.github/scripts/tests/test_playwright_artifact_recovery.py New unit tests for normalization/integrity rules around transport retries and reruns.
.github/scripts/tests/test_merge_queue_recovery_metrics.py New unit tests for paginated checks and report aggregation behavior.
.github/scripts/test_classify_playwright_outcome.py Add regression for raw first-attempt accounting (retries/skips).
.github/scripts/summarize_playwright_requests.py Add bounded latency histograms per route + merge support.
.github/scripts/stop_playwright_fast_environment.sh Ensure AutoPilot MySQL container is removed during teardown.
.github/scripts/start_playwright_fast_environment.sh Start isolated AutoPilot MySQL fixture and export related env vars.
.github/scripts/start_playwright_autopilot_mysql.sh New script to boot and health-check isolated MySQL fixture container.
.github/scripts/render_playwright_summary.cjs Normalize artifacts, treat optional reporting failures as warnings, and emit server-traffic/latency summary.
.github/scripts/playwright_cache_fingerprint.py Include new AutoPilot MySQL fixture inputs in cache fingerprint.
.github/scripts/normalize_playwright_artifacts.cjs New execution artifact normalizer validating shard/commit/run/attempt identity and reconciling retries.
.github/scripts/merge_queue_metrics.py Add paginated REST helpers, richer failure evidence, and report artifact reading (via gh api).
.github/scripts/merge_queue_daily_report.py Expand daily report with failure details + Playwright report aggregation and formatting updates.
.github/scripts/evaluate_playwright_performance.py Merge latency histograms across shards and emit richer performance metrics.
.github/scripts/classify_playwright_outcome.py Add measurement fields (first-attempt vs retries), and surface coverage/integrity verification into output.
.github/playwright/impact-map.generated.json Update generated impact map for changed/added Playwright coverage.
.github/actions/upload-artifact-with-retry/action.yml New composite action for bounded upload recovery under a fresh name.
.github/actions/download-artifact-with-retry/action.yml New composite action for bounded download recovery, including -retry fallback.
Review details
  • Files reviewed: 61/61 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/actions/download-artifact-with-retry/action.yml Outdated
Comment thread openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts Outdated
Comment thread .github/workflows/populate-playwright-caches.yml Outdated
@harshach harshach added the skip-pr-checks Bypass PR metadata validation check label Sep 9, 2026
harshach and others added 24 commits September 14, 2026 07:05
chromium-01 failed waiting 15s for "Task resolved successfully". The
approval had worked -- the failure snapshot shows the article already
"Approved" -- so only the notification was missed.

The toast auto-dismisses, and `approveTaskFromDetails` awaits the
task-action response and then a possible confirmation modal before
returning. By the time an assertion placed after it starts looking, the
toast can have come and gone. Starting the wait first means it is already
polling while all of that happens.

Reuses `toastNotification` rather than building a second locator: its
`.first()` is deliberate (toasts stack, and other workers' sockets push
unrelated ones into the page), and a new positional locator would have
grown eslint-suppressions.json, which may only shrink. The promise is
caught and rethrown after the approve so a failure there surfaces as
itself rather than as an unhandled rejection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
main's #32252 unified Owner into a single core-components component,
which changed the DOM these specs reach for: the selectable-list option
is now `<button data-testid="owner-option">` inside an `<li>`, carrying
the `active` class itself, where it used to be a wrapper with a `title`
and a listitem role. Every content conflict is that change meeting this
branch's determinism work.

Resolutions:

- DataProductAndSubdomains, DomainUIInteractions, entity.ts — main's
  `owner-option` locator, since the listitem role no longer exists. Not
  main's five-attempt retry loop: this branch replaced that with a
  blocking waitForSearchIndexed plus an exact response matcher, which is
  strictly more deterministic. `maxRetries` went with the loop, so it is
  dropped rather than left unused.
- entity.ts additionally keeps this branch's `usersPanel` scope instead
  of main's page-wide locator — the Teams tab renders its own
  owner-option list that a page-wide one can reach into.
- OverviewPageObject — main's locator, and its placement after the search
  response and loader wait, because the list is only final then. The
  early getByTitle declaration is gone: the title attribute it matched no
  longer exists, and `active` moved onto the button it now targets.
- teams.png — main's baseline. Ours came from an earlier merge of main;
  main's was regenerated by #32252, the commit that changed how the page
  renders.

Users.spec.ts auto-merged but did not pass: main added a bare
`adminPage.reload()`, which this branch's om-playwright/
no-implicit-navigation-load rule rejects. Given an explicit
`waitUntil: 'domcontentloaded'`; the assertion on the destination state
was already on the next line.

impact-map.generated.json regenerated for the incoming specs.

Verified: lint:playwright 0 errors, src eslint clean on the merged
SelectableList, i18n in sync, no conflict markers, nothing unmerged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Twice on this branch a shard died on a timeout that named the wrong
thing. chromium-04 spent 167s waiting for a lineage tab that was never
going to render, and chromium-08 15s on a sidebar; in both cases the app
had already been replaced by its error boundary -- once by a chunk that
failed to link ("does not provide an export named 't'"), once by a lazy
element that resolved to undefined (React #306). Working that out took a
trace download and, for the first, pulling the built bundle apart.

The context fixture now watches every page in the context for those
signatures and attaches what it saw to the test. The listener is
deliberately narrow: only errors that mean the route is gone, not console
noise, 404s or React warnings. Checked against both real messages plus a
benign 404, a key-prop warning, and `#3060` -- which the word boundary
correctly leaves alone.

Recorded rather than thrown. Failing a test that otherwise passed would
change behaviour across every spec at once, and the cost here was never
detection -- it was diagnosis. This puts the cause next to the timeout it
produced.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UserTeamSelectableList.test.tsx stubs Popover out entirely --
`({children}) => <div>{children}</div>` -- and then asserts only that
`isOpen: true` reaches it. That assertion holds whether or not the popover
can render, which is how the bulk-edit owner picker regressed with the
unit tests green.

Two tests that can actually fail:

- a jsdom case rendering the real Popover, for the shape the bulk-edit
  grid uses (`popoverProps={{ open: true }}`, so open on first render),
  plus its negative;
- a browser case, since jsdom has no layout and react-aria's positioning
  is geometry-dependent. It builds a react-data-grid cell editor around
  the real core-components Popover with the same mounted-flag gate,
  triggerRef and an onOpenChange wired to close the editor.

Both pass today, and that is the point of recording them: this shape is
NOT what breaks the picker, so the next person does not spend the time I
did eliminating it. If either goes red the regression really is in the
Popover.

The browser bundle aliases react/react-dom to one copy. ui-core-components
is a yarn `link:` and otherwise resolves its own, which leaves every hook
reading a null dispatcher and nothing mounts at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#32252 replaced the app's OwnerAvatarStack with the core one. The app's
version wrapped every stacked avatar in a Link carrying data-testid
"owner-link" and an aria-label, with the owner-name test id nested inside;
the core AvatarGroup renders a bare span with only the name test id.

So an entity with two or more owners lost the ability to navigate to an
owner at all -- the avatars are not clickable -- and both shapes the suite
uses to find an owner stopped resolving: addMultiOwner's
`[data-testid="owner-link"]` -> name chain, and
`getByRole('link', { name: owner })`. One owner was unaffected, since that
path renders an OwnerChip, which still has the wrapper.

owner-chip.tsx already documents this contract for its own non-compact
branch ("owner-link -> name chains both resolve"); this restores it for the
stack. Verified by reverting the change and watching
ApiCollection "Owner Propagation" fail on the link assertion.
#32252 (862e2d9) removed clickActiveGridCell but left the call in
openOwnerPickerEditor. Playwright transpiles specs without type checking, so
instead of failing the build it threw a ReferenceError on every pass of the
retry loop -- straight into that loop's empty catch, which then pressed Escape
and tried again. The helper therefore never clicked the cell and never sent
Enter or F2, and all six attempts ended with the picker still closed:

  playwright/utils/importUtils.ts(431,13): error TS2552:
    Cannot find name 'clickActiveGridCell'. Did you mean
    'doubleClickActiveGridCell'?

The product was never broken. A DOM observer over the failing run shows the
picker opening the moment the cell is actually clicked (editor=true tabs=true),
then closing on the second press of a double click, which lands on body and
reads as a click outside the popover. That is why the restored helper clicks
once rather than reusing doubleClickActiveGridCell.

Verified against the production bundle the container serves, which is what CI
runs -- the dev server hides this behind a separate StrictMode-only fault.
The guard's own comment claimed only that the Popover was not at fault. The
cause is now known -- a deleted helper left a dangling call -- so say so.
The multiple-containers allow case read `.all()` on the container locators and
called `.isVisible()` on the result. `.all()` resolves against the DOM as it is
at that instant -- it has none of the auto-waiting `expect(locator)` gives you
-- so when the containers had not mounted yet the list came back empty,
`.some()` was false, and the assertion failed with no retry. That is
chromium-14's "Topic allow common operations permissions": the buttons are in
the failure snapshot, they just were not there yet when the check ran. The test
passes locally on the same build, which is the tell.

Retry the whole read via toPass so the assertion measures the settled page.

Leaves the deny case alone deliberately: `.every(v => !v)` is vacuously true on
the same empty list, so the race makes it pass early rather than fail. That is
a weak assertion rather than a flaky one, and tightening it needs a separate
look at whether the containers render at all under deny.
owners is stripped from the storage json for every entity --
EntityRepository.FIELDS_STORED_AS_RELATIONSHIPS carries it alongside href,
children, tags, domains, dataProducts, dataContract, followers, experts,
reviewers and certification -- so it is always rebuilt from the OWNS
relationship. editors, which KnowledgePageRepository derives the same way from
EDITED_BY, had no such protection: it is absent from that list and this
repository overrode nothing.

Nothing persists it today, but only because editors is missing from
KNOWLEDGE_PATCH_FIELDS, so the update path leaves it null at store time. That
is a property of a field list rather than a guarantee -- adding editors there
would silently start writing a snapshot of the relationship into the json, and
setFields hands the stored value back to any caller that does not request the
field.

A sweep of every jsonb column in a populated database backs this up: no entity
table stores owners except nine table_entity rows holding an empty array, and
entity_extension, which is version history and keeps the whole entity on
purpose.

The test fails without the override and covers owners too, so the base list
cannot quietly lose it either.
…e changing

Both header counts are system-wide totals. The test captured one before clicking
a folder and compared a later read against it, so any document another worker
deleted in between showed up as a failure of this test: chromium-24 read 77
before the click and 52 after, having lost 25 documents it never created.

The property being tested is that the folder-view header is not scoped by the
sidebar selection. Assert that directly -- it still counts the document outside
the folder as well as the one inside, both of which this test created and owns --
instead of re-checking a number it does not control.

The earlier comparison of the two headers to each other has the same defect, two
lines up, so re-read both counts on each attempt rather than measuring one
against a value captured a moment earlier.
chromium-09 spent its whole 60s timeout retrying a click that Playwright had
already resolved to the right button: the form's Save sits bottom-right, the
toast region is fixed bottom-center, and an error toast was parked on top of it
intercepting every attempt.

The toast was not this test's. It read

  Entity not found: glossary "PW%'0ac272da.Noblec831c3bf"

which the Glossary landing page raises while restoring a glossary a parallel
worker had already deleted. waitForToastStackToClear would not have helped --
that toast never leaves on its own, which is why the click burned the full
timeout instead of recovering.

dismissToasts closes what is on screen rather than waiting for it to go, and
lives next to the other toast helpers since any bottom-anchored button can be
covered this way.
TaskDetailPanel renders a comment row's edit/delete icons behind `isHovered`
(line 435), so they exist in the DOM only while the pointer is on the card. The
test hovered once and then clicked, which leaves a window: anything that
re-renders the comment list -- the refetch after the second comment lands, an
activity-feed push -- resets that state, unmounts the icon, and the click waits
on a locator that will never resolve. chromium-19 spent its full 60s timeout
there, and its failure screenshot shows the panel having re-rendered down to a
single comment card.

Hover and click as one retried unit so a re-render costs a retry instead of the
test. The click only runs after a fresh hover, and a successful click ends the
loop, so it cannot fire twice.
The multiple-containers deny case read `.all()` and asserted
`.every(visible => !visible)`. That is vacuously true on an empty list, so the
check passed whenever the containers had not mounted -- and measuring it across
fourteen entity types showed the list resolving to zero elements every single
time. The assertion has never once separated a denied page from an unrendered
one; it has been passing for free since it was written.

Anchor it on the containers, which render regardless of permission
(GlossaryTermsSection emits glossary-container from both of its branches), and
then assert the button is absent. Absence is the right test rather than
invisibility: TagsContainerV2 renders add-tag behind
`permission && isEmpty(tags)`, so under deny it is never in the DOM at all,
which the same measurement confirms.

Both assertions auto-wait, so this also drops the hand-rolled `.all()` read that
made the allow case need an explicit retry.

Verified two ways: the seventeen deny tests pass against the new assertion, and
pointing the button selector at the containers instead makes it fail with
"Expected: 0, Received: 16" -- so it can now actually fail.
…nothing

Three checks in this file could be satisfied by a page that had not rendered,
which is indistinguishable from the permission behaviour they claim to verify.
Measured across all fourteen entity types, under both allow and deny:

  deny / direct              target present in DOM: 0 everywhere
  deny / with-manage-button  manage-button present: 2 of 14 entities
  allow / with-manage-button manage-button present: 14 of 14 entities
  owner-label and Tier       present on every entity in both modes

`not.toBeVisible()` is also satisfied by an element that is simply absent, so
the deny/direct check could never tell denial from a page still loading. It now
asserts absence outright, which the measurement says is the real behaviour --
the affordance is not rendered rather than hidden.

The with-manage-button checks guarded on `isVisible()`, a point-in-time read,
and did nothing at all when it was false. Under deny that was twelve of the
fourteen entities: a menu is genuinely not offered there, which is a legitimate
denial, so its absence must not fail -- but the action must still be shown to be
absent, and now it is. Under allow the menu is always offered, so that side
requires it instead of skipping.

Both branches reach the assertion through openManageMenu, because the trigger is
a react-aria Dropdown and the two configs run back to back: the second arrived
with the menu already open, so a bare click would have closed it and the
assertion would have read a menu that was not on screen.

testCommonOperations now waits for the header these affordances live in before
running any of them, so "absent" means denied rather than not-yet-rendered.

Verified: the fourteen allow and fourteen deny tests pass, and pointing either
rewritten assertion at an element that does exist makes it fail with
"Expected: 0, Received: 1" -- so both can now actually fail.

Removing the `.first()` from deny/direct drops one recorded positional-locator
violation, so the suppressions baseline shrinks from 9 to 8.
Last of the deny branches that could pass by doing nothing.
`getByText('Usage').first()` with `not.toBeVisible()` is also satisfied by text
that has not rendered yet, so it could not tell a denied page from a loading
one. All four call sites -- table, database, dashboard and mlmodel -- pass this
config, and two of them reach it with no precondition beyond navigating to the
entity.

Measured on all four, in both modes:

  allow   Usage matches: 1, visible: 1
  deny    Usage matches: 0, visible: 0
  entity-header-title present in every case, both modes

So denial removes the label rather than hiding it, which makes absence the right
assertion, and the header is a sound point to take it from. Settle the page
first, since a table reaches this after switching to the profiler tab.

Verified: the entity-specific allow and deny tests pass, and pointing the
assertion at the header instead makes it fail with "Expected: 0, Received: 1",
so it can now actually fail.

Dropping the `.first()` clears another recorded positional-locator violation;
the suppressions baseline shrinks from 8 to 7.
The UI Checkstyle playwright step reported zero ESLint errors and failed on a
guardrail instead: corpus.test.mjs pins the suppressions totals by exact
equality, and fixing two positional-locator violations left the recorded number
stale.

  om-playwright/no-positional-locator: 1109 -> 1107

Those two are the `.first()` calls dropped from the deny branches of
checkElementVisibility while making those assertions able to fail; the pruned
eslint-suppressions.json went with them in 1c85a29 and 6ee6801. Lowering the
number here is what that test's own comment asks for -- a count going up would
mean a new violation was suppressed instead of fixed.

Verified with the command CI runs: 133 tests, 133 pass. Prettier reports no diff
across the changed playwright and core-components files, so nothing else in the
job was dirty.
…g nothing

Measured every remaining site first, settling the page before reading -- the
first pass sampled too early and reported absences that were really
not-yet-rendered:

  Tier / certification controls   present and visible on all 14, both modes
  their pickers, after a click    0 cards on all 14 under deny
  custom_properties tab           present and visible on all 14, both modes
  custom-properties-card          present in both modes
  its edit icons                  hundreds under allow, 0 under deny
  .react-flow on the Lineage tab  present under allow and under deny
  edit-lineage                    1 under allow, 0 under deny

Three `isVisible()` guards wrapped these checks, and unlike manage-button --
legitimately absent on 12 of 14 entities -- every one of these controls renders
in both modes. The guards could therefore only ever skip on a page that had not
finished rendering, which is indistinguishable from passing. They are now
required, and each check asserts absence, which is what denial actually
produces at all of these sites.

The Lineage checks anchor on the canvas, and the two permission-error helpers
settle before judging: both take the absence branch under allow, which an
unrendered panel satisfies just as well as a permitted one.

`type` was `string`, so a mistyped check fell through to `default` -- asserting
`not.toBeVisible()` on a test id nothing renders, a guaranteed silent pass. It
is a union now, and both defaults throw. The narrowing immediately caught that
testIdsConfigs was still inferred as `string`.

Verified: all 49 tests in the spec pass, and each rewritten assertion fails when
pointed at something that does exist -- tier picker, custom-property card and
edit-lineage all report "Expected: 0, Received: 1".

Dropping four `.first()` chains from the custom-property assertions shrinks the
positional-locator baseline 1107 -> 1103, with corpus.test.mjs lowered to match.
The plan step has been failing since 26ccb5f, so no Playwright shard has run
at all on the last two pushes -- the board was not red, it was empty:

  Atomic Playwright units exceed their lane's execution budget:
    chromium|Features/ContextCenterArticles.spec.ts|Context Center Articles (20.2m)

Forty-four tests in one top-level describe, which the planner sums into a single
atomic unit and weighs against COMMON_SHARD_BUDGET_MS (19m). The recent
full-suite timing history puts them at 23.3m together, so the unit cannot be
placed on any shard and the planner refuses the whole plan. This is the case
AUDITED_PARALLEL_SUITES exists for, and BulkImport was added to it for exactly
the same failure at 25.8m.

Splitting is safe here: the chromium project inherits fullyParallel, so these
tests already run across workers today, and every fixture the describe shares is
a module-scoped binding assigned in beforeAll from a uuid(), so a parallel unit
builds its own entities rather than racing for shared ones.

It is not free: the units land on 28 shards and beforeAll re-runs on each,
creating roughly fifteen entities every time. That is worth saying out loud
because the alternative is not a cheaper plan, it is no plan.

Verified by reproducing the failure locally against the real inputs -- the
timing-history artifact CI used plus a generated test list. Without the entry
the planner exits 1 with "Context Center Articles (23.3m)"; with it the planner
exits 0 and lays out 47 shards. The planner's own 125 tests still pass.

Noted while here: .github/playwright/timing-baseline.json still has this suite
under budget, so the committed fallback disagrees with measured reality.
The scoped-query test failed with

  strict mode violation: locator('.ontology-g6-container') resolved to 2 elements

because query mode mounts OntologyGraphG6 twice -- OntologyExplorer draws the
studio graph and OntologyQueryResults draws the SPARQL results -- and both carry
that class. readGraphEdges selected it bare, so it matched both.

Two RDF runs on this same commit disagreed, one passing and one failing, which
is the tell: whether both graphs are mounted at the moment of the read is a
race. The passing direction is not obviously better. The helper's wait uses
`document.querySelector`, which silently takes whichever element comes first, so
a run that got away with it may have read the explorer graph and found the edge
there -- the relation is projected into both -- and reported success for a graph
the test was not asking about.

Scope the read to the results container instead, which pins it to the graph
under test in both directions. The selector is a named export because the same
hazard applies to any caller reading a graph while the studio's own is on
screen; the other four callers run with a single graph mounted and keep the
default.

Not verified locally: the ontology lane needs an RDF backend and this stack has
none -- /system/config/rdf reports rdfEnabled false -- so this rests on the DOM
the failure printed plus the markup in OntologyQueryResults, and CI is the
check.
I broke this in 4e30000. Requiring certification-value and the
custom-properties tab was justified by measuring fourteen data-asset entity
types -- but testCommonOperations has a second caller I never measured,
ServiceEntityPermissions, and service pages carry neither control. Sixteen tests
failed, every service type in both modes:

  expect(locator).toBeVisible() failed
    Locator: locator('[data-testid="custom_properties"]')   entityPermissionUtils.ts:347
    Locator: getByTestId('certification-value')             entityPermissionUtils.ts:333

Measured across all eight service types, both modes: certification-value 0,
custom_properties 0, while owner-label and Tier are present -- so the header
anchor was fine and only these two assertions were wrong.

Branch on whether the surface offers the control, which is what the original
code did. The difference from the original is the anchor: it guarded on
`isVisible()` with nothing proving the page had rendered, so skipping and
passing were indistinguishable. The anchor now runs first, so a count of zero
is a real absence.

Tier stays required -- present on all fourteen entities and all eight services.

Verified both callers: ServiceEntityPermissions 47 passed, EntityPermissions 49
passed. The lesson is narrower than "measure first": measure every caller, which
is the same mistake manage-button nearly caused and this time I walked into.
The test scrolled the left hierarchy to the parent, expanded it, then asserted
the child node was visible without scrolling to the child. The hierarchy is an
infinite-scroll list, so expanding a node says nothing about whether its child
landed inside the rendered window.

That was survivable while the Context Center held few articles. It stopped being
survivable in the same run that first split this suite across 28 shards, each
re-running a beforeAll that creates its own articles: the list got long enough
that the child fell outside the window and the assertion found nothing.

scrollHierarchyToNode already walks an infinite-scroll list to a named node, and
the test already uses it for the parent. Use it for the child too.
… per spec

def99b5 made this suite schedulable by splitting it per spec. That worked --
44 units, plan exits 0 -- but the units landed on 28 shards, and this describe's
beforeAll creates roughly fifteen entities every time it runs. Twenty-eight
copies of that setup is a lot of churn to add to a shared server, and the same
run produced a hierarchy-pagination failure caused by exactly that: the Context
Center held enough articles that a child node fell outside the rendered window.

The suite's natural seams are already the right size. Measured from the same
timing history: the twenty top-level tests weigh 12.25m and the four nested
describes 3.7m, 2.5m, 2.5m and 2.3m -- every one under the 19m budget.
AUDITED_CHILD_SUITE_PARTITIONS cuts along exactly those seams.

Against the real CI inputs, same test list and same timing artifact:

  per spec         44 units on 28 shards, 47 shards total
  by child suite    5 units on  4 shards, 47 shards total

Same plan size, same schedulability, one seventh of the setup. The planner's own
125 tests still pass.
The PR had gone CONFLICTING, and a conflicting PR gets no pull_request runs at
all -- GitHub cannot build the merge commit those workflows test. Every run on
the branch was the pull_request_target gate variant with zero shards, so the
green Playwright check was a no-op and the last three fixes had never been
exercised.

Four conflicts:

build_playwright_shards.py -- main hit the same Context Center Articles budget
failure and fixed it the same way I first did, by adding the suite to
AUDITED_PARALLEL_SUITES. Kept this branch's planner instead: main's is behind it
here, with COMMON_MAX_SHARDS still at 28 where this branch needs 36, older
imports, and no AUDITED_CHILD_SUITE_PARTITIONS. Taking main's file made the
planner abort with "Lane chromium needs more than 28 shards". This branch
already covers the same suite through the child-suite partition, which costs 5
units on 4 shards rather than 44 on 28.

lineage.ts -- kept this branch's delete-edge verification and took main's wait
for the scene refetch. The two are complementary: main's keeps the next action
from racing the re-render, this branch's proves the DELETE named the edge the
test asked for.

BulkImport.spec.ts -- kept previewBulkImportChanges, which waits on the
validation response rather than a vendor loader class. Main's inline
validateSuccessfulImportStatus is redundant; the spec already calls it two lines
later with a row count.

ServiceForm.spec.ts -- neither side compiled: the auto-merge kept main's
caCertificateField declaration, so HEAD's fileInput1 and main's testCertPath
were both undeclared. Used main's locator with this branch's in-memory buffer,
which keeps the asserted file name under the test's control and needs no temp
file to write or clean up.

Verified: planner exits 0 at 47 shards with its 125 tests passing, playwright
guardrails 133 pass, lint 0 errors. The 100 tsc errors under playwright/ are the
pre-existing backlog and predate this merge on both sides.
Seven conflicts.

schemaChanges.sql (2.1.0 postgres) -- append-only file, both sides appended.
Kept both: this branch's audit_log_event.entity_fqn varchar->text widening and
main's LOWER(email)/LOWER(name) functional indexes. They are independent DDL.
The MySQL counterpart auto-merged correctly; main's functional indexes are
Postgres-only by design.

impact-map.generated.json -- generated, so regenerated rather than hand-merged,
and regenerated a second time after the lint fixes below changed the sources it
derives from. `generate_playwright_impact_map.py --check` now passes.

ServiceListing.spec.ts -- main extracted toggleServiceTypeFilter, this branch
tightened the response matcher to assert which service types the query_filter
actually carries. Took both: main's helper drives the UI, this branch's matcher
waits on the response. Main's helper is the better interaction -- its own
comment records that getByLabel, which this branch was using, is unreliable
under CI. Dropped the now-unused waitForAntdPopupToSettle import.

lineage/DataAssetLineage.spec.ts -- kept main's openImpactAnalysisTab import,
dropped performZoomOut: this branch replaced that call with fitToScreen, so it
has no remaining use and would have been an unused import.

polling.ts, CustomPropertiesPanel.spec.ts, TeamDetailsV1.test.tsx -- import
collisions where both sides added different symbols. Unioned after checking each
symbol is actually referenced.

Main's new specs violate six of this branch's playwright guardrails, which main
does not have. Suppressions may only shrink and corpus.test.mjs pins the totals,
so these are fixed rather than suppressed: five page.goto calls gained an
explicit waitUntil (each already asserts destination state), and
SuggestionsSliderPermissions moved off a status-filtered waitForResponse onto
waitForResponseWithStatus, which preserves the first HTTP failure.

Verified: playwright lint 0 errors, guardrails 133 pass, impact map drift-free,
TeamDetailsV1 15 tests pass, CI script tests 235 pass. Playwright tsc sits at 99
pre-existing errors, one fewer than before the merge. The two remaining
test_playwright_cache_assets failures are a local macOS artifact -- the script
runs fine standalone and this merge touches neither it nor its tests.
@sonarqubecloud

Copy link
Copy Markdown

One conflict, and both sides had independently made the same fix.

OntologyQueryRdf.spec.ts -- scoping readGraphEdges to the SPARQL results graph.
Query mode mounts OntologyGraphG6 twice, once for the explorer and once for the
results, and both carry .ontology-g6-container, so an unscoped read either trips
strict mode or silently takes whichever element comes first. Main inlined the
selector at the call site; this branch passes QUERY_RESULT_GRAPH_CONTAINER,
exported from ontologyStudio.ts next to the explanation of why it exists. The
two resolve to the same string, so this is presentation only -- kept the named
constant, since any future caller reading a graph while the studio's own is on
screen needs the same selector.

Verified: playwright lint 0 errors, guardrails 133 pass, impact map reports up
to date, CI script tests 235 pass, playwright tsc unchanged at its 99
pre-existing errors. The two test_playwright_cache_assets failures are the same
local macOS artifact as before -- untouched by this merge.
UI Checkstyle failed the src lint step on one error:

  LineageMap.component.tsx 815:63  Use <Tooltip> from
  @openmetadata/ui-core-components instead of raw title="" attributes
  openmetadata-ui-patterns/no-raw-title-attribute

The design-system Tooltip cannot be used at this call site. Breadcrumbs renders
a crumb's label inside an AriaLink whenever onAction is set and the crumb is not
the current one, and TooltipTrigger wraps any child that is not natively
focusable -- a span is not -- in an AriaButton. That is a button inside a link:
invalid HTML, and the breadcrumb would stop responding to presses. Breadcrumbs
truncates the label itself through maxItemWidth and offers no overflow tooltip
of its own, so deleting the title would drop the only way to read a clipped
crumb.

So this takes the narrow disable, matching the one exception the rule already
carries in TableAliases, where a native title is likewise the idiomatic overflow
affordance and react-aria's Tooltip does not fit the surrounding markup.

Also fixes the licence-header step, which was failing as a consequence rather
than on its own: it inserted headers into 0 files, then failed its
`git status --porcelain` check on the working tree the src step had left dirty.

Verified with CI's own sequence on the branch's 59 changed src files --
organize-imports, eslint --fix, prettier --write -- which now reports 198
problems, 0 errors and leaves the tree clean. i18n and app-docs regenerate with
no diff; playwright lint 0 errors, guardrails 133 pass, core-components lint
clean.
@gitar-bot

gitar-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 5 closed / 6 findings

Comprehensive Playwright coverage and CI race fixes addressing shared-helper defects and backend races. Results-JSON upload, DAG registration, retry toggles, stale-request handling, and pagination error recovery have been resolved. Consider scoping the selectOwnersOnTab owner-option locator to the active tab panel in importUtils.ts to match the deliberate scoping in entity.ts and avoid strict-mode violations when Users and Teams panels are mounted simultaneously.

💡 Quality: selectOwnersOnTab owner-option locator not scoped to active tab panel

📄 openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts:464-467 📄 openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts:308-314

In importUtils.ts selectOwnersOnTab, the option is matched page-wide via page.locator('[data-testid="owner-option"]').filter({ hasText: owner }), whereas the sibling helpers in entity.ts (e.g. addOwnerWithoutValidation) were deliberately scoped to owner-select-${type}-panel with the comment "a page-wide match is ambiguous." In fillUserAndTeamOwnerDetails both the Users and Teams tab panels can be mounted simultaneously, so a page-wide hasText (substring, case-insensitive) match risks a strict-mode violation or clicking the wrong panel if a user and team share overlapping display text. Scope the locator to getByTestId(\owner-select-${tab.toLowerCase()}-panel`)` for consistency and robustness.

✅ 5 closed
Edge Case: Results-JSON upload can now hard-fail PR shards on transient 403

📄 .github/workflows/playwright-e2e-reusable.yml:1833-1844
For non-merge_group (PR) runs, the results-JSON upload step lost continue-on-error: true, dropped its -retry fallback upload, and switched if-no-files-found from ignore to error. The deleted comment documented the exact failure this guarded against: a FinalizeArtifact 403 that reserves the name without publishing, so a retry under the same name then gets 409 (run 34244326002). With no continue-on-error, no retry, and error-on-missing, a single transient artifact-service failure now fails the shard job — a required PR check — with no recovery path. Consider keeping continue-on-error: true on this upload (the summary aggregation already tolerates missing shards) or restoring a bounded retry for PR runs.

Edge Case: DAG registration wait relies on cross-process clock agreement

📄 openmetadata-airflow-apis/openmetadata_managed_apis/operations/deploy.py:149 📄 openmetadata-airflow-apis/openmetadata_managed_apis/operations/deploy.py:179-193
_wait_for_dag_registration compares model.last_parsed_time >= parsed_after, where parsed_after is datetime.now(timezone.utc) captured in the API process while last_parsed_time is written by the separate Airflow 3 DAG processor component. If the processor's clock trails the API server's (or a fresh parse's timestamp otherwise lands before parsed_after), the loop never satisfies its condition and returns False after 60s, turning a successful deploy into an ApiResponse.server_error() even though the DAG parsed correctly. Consider anchoring the freshness check to the model row identity/version or a monotonic marker rather than an absolute wall-clock comparison across processes.

Edge Case: Retry on expand toggles collapse when child fetch is slow

📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/NestedChildrenUpdates.spec.ts:288-294
In expandNestedColumn the retried block clicks the expand icon and then asserts childRow is visible. expandIcon.click() toggles expansion, but it is not the last action — if the children fetch takes longer than the inner toBeVisible default (5s) the assertion throws and toPass re-runs the block, which clicks the icon again and now collapses the row. Successive retries alternate expand/collapse and only pass when a fetch happens to complete within one interval, making the helper slower and flakier than intended. Consider gating the click on current expansion state (skip the click when the row is already expanded) or asserting the expanded/aria-expanded state before deciding to click again.

Bug: Stale-request guards drop own-search resolver, hang promise

📄 openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts:256-270 📄 openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.test.ts:234-248
After the refactor each debounced invocation captures its own resolve locally (lines 247-248), but the .then guard if (request !== latestRequest) return; (258-260) and the .catch guard if (request === latestRequest) (276) still gate on global request order. When two searches are >300ms apart and both go in-flight, the older invocation's captured resolver is never called on completion/error, so its Promise never settles — directly contradicting the new comment "An in-flight response must settle its own search." I simulated both new tests (keeps overlapping responses with their own search and does not clear a newer search when an earlier request fails): previous never resolves and the await expect(previous).resolves... assertions hang and fail. Remove both guards so each response settles only its own captured resolver.

Edge Case: Pagination error is unrecoverable until hook remounts

📄 openmetadata-ui/src/main/resources/ui/src/hooks/knowledge-center/useKnowledgePageListing.ts:115-125 📄 openmetadata-ui/src/main/resources/ui/src/hooks/knowledge-center/useKnowledgePageListing.ts:82-85
In useKnowledgePageListing, once any fetch fails, error is set and fetchNextPage returns early forever (if (!enabled || error || !hasMore) return). A transient failure on a "load more" page therefore permanently freezes infinite scroll — further scrolling never retries, and error is only cleared when enabled/searchQuery change (i.e. a remount or new search), not on the next scroll attempt. Consider clearing error and allowing a retry on the next fetchNextPage (only short-circuiting the initial page-0 error), so a one-off network blip on a subsequent page doesn't kill pagination for the session.

🤖 Prompt for agents
Code Review: Comprehensive Playwright coverage and CI race fixes addressing shared-helper defects and backend races. Results-JSON upload, DAG registration, retry toggles, stale-request handling, and pagination error recovery have been resolved. Consider scoping the `selectOwnersOnTab` owner-option locator to the active tab panel in `importUtils.ts` to match the deliberate scoping in `entity.ts` and avoid strict-mode violations when Users and Teams panels are mounted simultaneously.

1. 💡 Quality: selectOwnersOnTab owner-option locator not scoped to active tab panel
   Files: openmetadata-ui/src/main/resources/ui/playwright/utils/importUtils.ts:464-467, openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts:308-314

   In importUtils.ts `selectOwnersOnTab`, the option is matched page-wide via `page.locator('[data-testid="owner-option"]').filter({ hasText: owner })`, whereas the sibling helpers in entity.ts (e.g. `addOwnerWithoutValidation`) were deliberately scoped to `owner-select-${type}-panel` with the comment "a page-wide match is ambiguous." In `fillUserAndTeamOwnerDetails` both the Users and Teams tab panels can be mounted simultaneously, so a page-wide `hasText` (substring, case-insensitive) match risks a strict-mode violation or clicking the wrong panel if a user and team share overlapping display text. Scope the locator to `getByTestId(\`owner-select-${tab.toLowerCase()}-panel\`)` for consistency and robustness.

Review coverage

Rules No rules evaluated

Functional validation Not enabled · Set up

Auto-approval Not enabled · Set up

Options

Display: compact → Counting what did not apply, without listing it.

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

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend safe to test Add this label to run secure Github workflows on PRs skip-pr-checks Bypass PR metadata validation check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Merge queue: recover transient artifacts and stabilize recurring E2E failures

7 participants