Skip to content

playwright(fix): fix unclosed browser contexts and page leaks in E2E tests#29694

Open
chirag-madlani wants to merge 10 commits into
mainfrom
fix-unclosed-browser-contexts
Open

playwright(fix): fix unclosed browser contexts and page leaks in E2E tests#29694
chirag-madlani wants to merge 10 commits into
mainfrom
fix-unclosed-browser-contexts

Conversation

@chirag-madlani

@chirag-madlani chirag-madlani commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Describe your changes:

Fixes #29701

I worked on fixing Playwright E2E test resource leaks because unclosed browser contexts and pages from failing tests were appearing in traces of unrelated tests, causing spurious failures and flakiness.

Changes made:

  • PersonaFlow.spec.ts — wrap userContext (browser.newContext()) in try/finally so it always closes even when assertions throw
  • LineagePipelineAnnotator.spec.ts — wrap beforeAll/afterAll API setup in try/finally so apiContext.dispose() + page.close() run even if an API call throws
  • DataAssetLineage.spec.ts — destructure and call afterAction() from getApiContext() in a finally block (was previously silently discarded, permanently leaking APIRequestContext)
  • Glossary.spec.ts — wrap deny-permission tests in try/finally for consumerAfterAction / cleanup / afterAction
  • GlossaryVersionPage.spec.ts — wrap glossary.delete + afterAction in finally
  • GlossaryRelationsGraph.spec.ts, GlossaryRelationsGraphPerf.spec.ts, GlossaryTermRelationsGraphNested.spec.ts — replace browser.newPage() + adminUser.login() per-test with test.use({ storageState }) + page fixture; eliminates auth overhead per test and removes the page-leak-on-failure risk entirely
  • eslint.config.mjs — adds a warn-level no-restricted-syntax rule on bare browser.newPage() in e2e spec files to surface the anti-pattern at code-review time without blocking multi-user tests that legitimately need a second page as a different user

Type of change:

  • Bug fix

High-level design:

Two root causes were addressed:

  1. Missing try/finally around cleanup calls — Any afterAction(), context.close(), or apiContext.dispose() placed as the last statement in a test body is unreachable when an earlier assertion throws. Wrapping the test body in try { ... } finally { cleanup } guarantees execution. Applied to PersonaFlow, LineagePipelineAnnotator, DataAssetLineage, Glossary, and GlossaryVersionPage.

  2. browser.newPage() + manual login inside test bodies — For tests that only need one admin perspective, this pattern re-runs a full login flow per test and leaks the page if the test fails before page.close(). The correct pattern is test.use({ storageState: 'playwright/.auth/admin.json' }) at file level and letting the page fixture manage the lifecycle. Applied to the three GlossaryRelations* specs.

The ESLint rule uses the selector CallExpression[callee.object.name='browser'][callee.property.name='newPage'][arguments.length=0] scoped to playwright/e2e/**/*.spec.* files only (excludes utils/ and auth.setup.ts). It is warn not error because multi-user tests legitimately call browser.newPage() to open a second page as a different user.

Tests:

Use cases covered

  • Browser contexts and API request contexts are always closed/disposed even when assertions fail mid-test
  • Admin-only glossary graph tests run without per-test login overhead
  • New ESLint rule warns on the browser.newPage() anti-pattern going forward

Playwright (UI) tests

  • All modified files are Playwright E2E tests — the fixes are the tests themselves

Manual testing performed

  1. Verified ESLint warn fires on TaskPermissions.spec.ts (legitimate multi-user, expected warning)
  2. Verified zero lint output on the three converted GlossaryRelations* specs
  3. Confirmed auth.setup.ts is excluded from the rule (glob matches *.spec.ts only)

UI screen recording / screenshots:

Not applicable.

Checklist:

  • I have read the CONTRIBUTING document.
  • I have commented on my code, particularly in hard-to-understand areas.
  • I have added tests (unit / integration / Playwright as applicable) and listed them above.

Summary by Gitar

  • Cleanup protection:
    • Wrapped afterEach hook in DataAssetLineage.spec.ts, LineageInteraction.spec.ts, and LineageControls.spec.ts with page.goto('about:blank') to clear memory and prevent canvas leaks.
  • Tag cleanup:
    • Removed PLAYWRIGHT_BASIC_TEST_TAG_OBJ from PlatformLineage.spec.ts, DataAssetLineage.spec.ts, and LineageInteraction.spec.ts suites, which may affect automated test selection in CI.

This will update automatically on new commits.

Greptile Summary

This PR fixes Playwright E2E test resource leaks — unclosed browser contexts, pages, and API request contexts that were contaminating traces of unrelated tests. Affected files receive try/finally wrapping around cleanup calls, and three GlossaryRelations* specs are refactored to use test.use({ storageState }) + the page fixture instead of per-test browser.newPage() + manual login.

  • Resource-leak fixes in PersonaFlow, LineagePipelineAnnotator, DataAssetLineage, Glossary, and GlossaryVersionPage spec files wrap cleanup calls in try/finally so apiContext.dispose() / page.close() / context.close() always execute even when assertions throw.
  • Fixture migration in GlossaryRelationsGraph, GlossaryRelationsGraphPerf, and GlossaryTermRelationsGraphNested replaces per-test page creation and login with the built-in page fixture, eliminating auth overhead and page-leak-on-failure risk.
  • ESLint guard (no-restricted-syntax, warn level) added to surface future browser.newPage() anti-patterns in spec files at review time.

Confidence Score: 5/5

Safe to merge — all changes are confined to E2E test files and ESLint config with no production code affected.

The changes correctly address the stated resource-leak patterns. Two test cleanup sequences remain partially unguarded (setup calls before try in GlossaryVersionPage, and sequential finally steps in Glossary), but these are test-infrastructure robustness gaps rather than correctness issues that would break application behavior or block other tests from running.

GlossaryVersionPage.spec.ts (setup calls before try) and Glossary.spec.ts (sequential cleanup in finally) have incomplete resource-safety, though both are incremental improvements over the pre-PR state.

Important Files Changed

Filename Overview
openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/GlossaryVersionPage.spec.ts Nested try/finally added for glossary.delete/afterAction; setup calls (getApiContext, glossary.create, glossary.patch) remain outside the try block, leaving apiContext exposed if setup fails.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts Deny-permission tests wrapped in try/finally; cleanup steps (consumerAfterAction, cleanup, afterAction) remain sequential inside the finally block — a throw in the first step still skips subsequent cleanup.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/LineagePipelineAnnotator.spec.ts beforeAll/afterAll now use nested try/finally (apiContext.dispose inside try, page.close in finally) — complete fix for both hooks.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts Test body now wrapped in try/finally ensuring userContext.close() always runs even when assertions throw.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Lineage/DataAssetLineage.spec.ts afterAction now destructured and called in finally; test.afterEach navigates to about:blank to prevent canvas memory leaks between tests.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Glossary/GlossaryRelationsGraph.spec.ts All per-test browser.newPage()+login replaced with test.use({ storageState }) and page fixture — cleanly eliminates page-leak-on-failure and auth overhead.
openmetadata-ui/src/main/resources/ui/playwright/eslint.config.mjs Adds warn-level no-restricted-syntax rule targeting bare browser.newPage() calls in spec files; glob correctly excludes utils/ and auth.setup.ts.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Test Body / Hook starts] --> B{try block}
    B --> C[Test assertions / API calls]
    C -->|assertion throws| D[finally block]
    C -->|passes| D
    D --> E{Nested try/finally\nfor cleanup step 1}
    E --> F[cleanup step 1\ne.g. consumerAfterAction / glossary.delete]
    F -->|throws| G[inner finally\ncleanup step 2 still runs]
    F -->|ok| G
    G --> H[cleanup step 2\ne.g. afterAction / apiContext.dispose]

    B2[Old pattern: sequential finally] --> C2[cleanup step 1]
    C2 -->|throws| Z[cleanup step 2 SKIPPED — resource leak]
    C2 -->|ok| D2[cleanup step 2]

    style Z fill:#f88,stroke:#c00
    style G fill:#8f8,stroke:#080
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"}}}%%
flowchart TD
    A[Test Body / Hook starts] --> B{try block}
    B --> C[Test assertions / API calls]
    C -->|assertion throws| D[finally block]
    C -->|passes| D
    D --> E{Nested try/finally\nfor cleanup step 1}
    E --> F[cleanup step 1\ne.g. consumerAfterAction / glossary.delete]
    F -->|throws| G[inner finally\ncleanup step 2 still runs]
    F -->|ok| G
    G --> H[cleanup step 2\ne.g. afterAction / apiContext.dispose]

    B2[Old pattern: sequential finally] --> C2[cleanup step 1]
    C2 -->|throws| Z[cleanup step 2 SKIPPED — resource leak]
    C2 -->|ok| D2[cleanup step 2]

    style Z fill:#f88,stroke:#c00
    style G fill:#8f8,stroke:#080
Loading

Comments Outside Diff (2)

  1. openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/GlossaryVersionPage.spec.ts, line 51-55 (link)

    P2 Setup calls outside try still leak apiContext

    glossary.create() and glossary.patch() are called before the try block on lines 52–53. If either throws, afterAction() (which disposes apiContext) and glossary.delete() in the finally are never reached — the very leak this PR aims to fix still occurs for setup failures. Moving the try to wrap from await glossary.create(apiContext) through the end would close this gap. The same pattern exists in both Glossary.spec.ts deny-permission tests, where performAdminLogin, setupGlossaryDenyPermissionTest, and performUserLogin all run before their try blocks.

  2. openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/GlossaryVersionPage.spec.ts, line 141-144 (link)

    P2 Sequential finally cleanup — later steps are skipped if the first throws

    If glossary.delete(apiContext) throws (e.g., entity not found after a partial setup failure), afterAction() on the next line is never called, leaving apiContext undisposed. The same pattern appears in LineagePipelineAnnotator.spec.ts (apiContext.dispose() / page.close()) and Glossary.spec.ts (consumerAfterAction() / cleanup() / afterAction()). Wrapping each step with its own try/catch or using Promise.allSettled-style chaining would ensure all cleanup steps run regardless of individual failures.

Reviews (9): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

…tests

Scan all Playwright specs for resource leaks and fix them:

- PersonaFlow.spec.ts: wrap userContext (browser.newContext) in try/finally
- LineagePipelineAnnotator.spec.ts: wrap beforeAll/afterAll setup in try/finally
  so apiContext.dispose() + page.close() run even when an API call throws
- DataAssetLineage.spec.ts: destructure afterAction from getApiContext() and
  call it in a finally block (previously discarded, leaking APIRequestContext)
- Glossary.spec.ts: wrap deny-permission tests in try/finally for
  consumerAfterAction / cleanup / afterAction
- GlossaryVersionPage.spec.ts: wrap glossary.delete + afterAction in finally
- GlossaryRelationsGraph.spec.ts, GlossaryRelationsGraphPerf.spec.ts,
  GlossaryTermRelationsGraphNested.spec.ts: replace browser.newPage() +
  adminUser.login() per test with test.use({ storageState }) + page fixture —
  eliminates the auth-per-test overhead and removes the page-leak-on-failure risk

Also adds an ESLint warn rule on bare browser.newPage() in e2e spec files to
surface the anti-pattern at code-review time without blocking multi-user tests
that legitimately need a second page as a different user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 2, 2026 09:56
@chirag-madlani chirag-madlani requested a review from a team as a code owner July 2, 2026 09:56
@github-actions

github-actions Bot commented Jul 2, 2026

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 added safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Jul 2, 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.

Pull request overview

This PR hardens Playwright E2E specs against resource leaks (pages, browser contexts, and API request contexts) by ensuring cleanup runs even when assertions/setup fail, and by migrating single-user admin tests to the page fixture + storageState pattern to avoid manual browser.newPage() + login.

Changes:

  • Added try/finally cleanup patterns across several specs to ensure afterAction(), context.close(), page.close(), and apiContext.dispose() run on failures.
  • Migrated Glossary Relations Graph specs from per-test browser.newPage() + manual admin login to test.use({ storageState }) + page fixture.
  • Added an ESLint warning rule for the browser.newPage() anti-pattern in E2E spec files.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/GlossaryVersionPage.spec.ts Adds cleanup finally for glossary version tests to avoid API context leaks.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Lineage/DataAssetLineage.spec.ts Ensures getApiContext()’s afterAction() is always executed via try/finally.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts Wrapes deny-permission flows in try/finally to ensure cleanup runs on assertion failures.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts Ensures browser.newContext() is closed via try/finally to prevent context leaks.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/LineagePipelineAnnotator.spec.ts Wraps beforeAll/afterAll setup & teardown with cleanup finally blocks to prevent page/api leaks.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Glossary/GlossaryTermRelationsGraphNested.spec.ts Switches to storageState + page fixture to avoid per-test pages and login overhead.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Glossary/GlossaryRelationsGraphPerf.spec.ts Switches to storageState + page fixture to eliminate manual page lifecycle handling and reduce auth overhead.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Glossary/GlossaryRelationsGraph.spec.ts Switches to storageState + page fixture to eliminate per-test browser.newPage() usage.
openmetadata-ui/src/main/resources/ui/eslint.config.mjs Adds a warn-level no-restricted-syntax rule to surface bare browser.newPage() in E2E specs.

Comment on lines 336 to +340
await user.login(userPage);

test.slow(true);

await test.step('Admin creates a persona and sets the default persona', async () => {
await navigateToPersonaSettings(adminPage);
await adminPage.getByTestId('add-persona-button').click();

await validateFormNameFieldInput({
page: adminPage,
value: PERSONA_DETAILS.name,
fieldName: 'Name',
fieldSelector: '[data-testid="name"]',
errorDivSelector: '#name_help',
});

await adminPage
.getByTestId('displayName')
.fill(PERSONA_DETAILS.displayName);

await adminPage.locator(descriptionBox).fill(PERSONA_DETAILS.description);

const userListResponse = adminPage.waitForResponse(
'/api/v1/users?limit=*&isBot=false*'
);
await adminPage.getByTestId('add-users').click();
await userListResponse;

await waitForAllLoadersToDisappear(adminPage);

const searchUser = adminPage.waitForResponse(
`/api/v1/search/query?q=*${encodeURIComponent(
user.responseData.displayName
)}*`
);
await adminPage
.getByTestId('searchbar')
.fill(user.responseData.displayName);
await searchUser;

await adminPage
.getByRole('listitem', { name: user.responseData.displayName })
.click();
await adminPage.getByTestId('selectable-list-update-btn').click();

await adminPage.getByRole('button', { name: 'Create' }).click();

await navigateToPersonaSettings(adminPage);

await waitForAllLoadersToDisappear(adminPage, 'skeleton-card-loader');

const personaResponse = adminPage.waitForResponse(
`/api/v1/personas/name/${encodeURIComponent(
try {
Comment on lines 51 to +55
const { afterAction, apiContext } = await getApiContext(page);
await glossary.create(apiContext);
await glossary.patch(apiContext, GLOSSARY_PATCH_PAYLOAD);

await test.step('Version changes', async () => {
await glossary.visitPage(page);

await page.click('[data-testid="version-button"]');

await expect(
page
.getByTestId('asset-description-container')
.getByTestId('markdown-parser')
.locator('span')
.filter({ hasText: 'Description' })
).toBeVisible();

await expect(
page.locator(
'.diff-added [data-testid="tag-PersonalData.SpecialCategory"]'
)
).toBeVisible();

await expect(
page.locator('.diff-added [data-testid="tag-PII.Sensitive"]')
).toBeVisible();
});

await test.step('Should display the owner & reviewer changes', async () => {
await glossary.visitPage(page);

await expect(page.getByTestId('version-button')).toHaveText(/0.2/);

await addMultiOwner({
page,
ownerNames: [user.getUserDisplayName()],
activatorBtnDataTestId: 'add-owner',
resultTestId: 'glossary-right-panel-owner-link',
endpoint: EntityTypeEndpoint.Glossary,
isSelectableInsideForm: true,
type: 'Users',
try {
Comment on lines +125 to +128
await page.reload();
await page.click('[data-testid="version-button"]');
await versionPageResponse;

Comment on lines +135 to +138
} finally {
await glossary.delete(apiContext);
await afterAction();
}
Comment on lines 52 to +56
await redirectToHomePage(page);
const token = await getToken(page);
const apiContext = await getAuthContext(token);

table = new TableClass();
await table.create(apiContext);
dbServiceFqn = table.serviceResponseData.fullyQualifiedName ?? '';

const msName = `pw-kafka-${uuid()}`;
const msResp = await apiContext
.post('/api/v1/services/messagingServices', {
data: {
name: msName,
serviceType: 'Kafka',
connection: {
config: { type: 'Kafka', bootstrapServers: 'localhost:9092' },
try {
Comment on lines 135 to +139
await redirectToHomePage(page);
const token = await getToken(page);
const apiContext = await getAuthContext(token);

await table.delete(apiContext);
await apiContext.delete(
`/api/v1/services/messagingServices/name/${encodeURIComponent(
messagingServiceFqn
)}?recursive=true&hardDelete=true`
);
await apiContext.delete(
`/api/v1/services/pipelineServices/name/${encodeURIComponent(
pipelineServiceFqn
)}?recursive=true&hardDelete=true`
);

await apiContext.dispose();
await page.close();
try {
…nally cleanup chains

- Replace second await on resolved versionPageResponse promise with new
  waitForResponse registrations in Glossary and GlossaryTerm tests so the
  test actually waits for the second network request.
- Nest glossary.delete() inside try/finally so afterAction() always runs
  even when delete throws (GlossaryVersionPage — three tests affected).
- Nest apiContext.dispose() inside try/finally in LineagePipelineAnnotator
  beforeAll/afterAll so page.close() always runs even when dispose throws.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🔴 Playwright Results — 1 failure(s), 20 flaky

✅ 4485 passed · ❌ 1 failed · 🟡 20 flaky · ⏭️ 37 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 438 0 4 16
🟡 Shard 2 806 0 4 8
✅ Shard 3 809 0 0 7
🟡 Shard 4 808 0 4 5
🔴 Shard 5 856 1 2 0
🟡 Shard 6 768 0 6 1

Genuine Failures (failed on all attempts)

Pages/ExploreBrowse.spec.ts › service type drill-down disables unrelated roots and query-panel Clear resets it (shard 5)
�[31mTest timeout of 180000ms exceeded.�[39m
🟡 20 flaky test(s) (passed on retry)
  • Features/Glossary/GlossaryPagination.spec.ts › should check for nested glossary term search (shard 1, 1 retry)
  • Pages/Lineage/DataAssetLineage.spec.ts › verify create lineage for entity - Stored Procedure (shard 1, 1 retry)
  • Pages/SearchSettings.spec.ts › Preview config reflects reverted n-gram weight after save (shard 1, 2 retries)
  • Flow/SearchRBAC.spec.ts › the browse tree only shows the asset-type categories a user can access (shard 1, 1 retry)
  • Features/BulkEditEntity.spec.ts › Glossary (shard 2, 1 retry)
  • Features/BulkImport.spec.ts › Database Schema (shard 2, 1 retry)
  • Features/ContextCenterPermission.spec.ts › user with view-only permission cannot see restore or delete actions on an archived document (shard 2, 1 retry)
  • Features/GlobalPageSize.spec.ts › Page size should persist across different pages (shard 2, 1 retry)
  • Pages/CustomProperties.spec.ts › Enum (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Should verify property name is visible for apiCollection in right panel (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Set & Update all CP types on apiEndpoint (shard 4, 1 retry)
  • Pages/DataContractsSemanticRules.spec.ts › Validate Description Rule Is_Set (shard 4, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should verify deleted user not visible in owner selection for pipeline (shard 5, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should allow Data Steward to edit tags for searchIndex (shard 6, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Import partial success - some terms pass, some fail (shard 6, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Glossary CSV import preserves typed relations (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify Impact Analysis service filter selection (shard 6, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab is NOT visible for pipelineService in platform lineage (shard 6, 1 retry)
  • Pages/UserDetails.spec.ts › Admin user can edit teams from the user profile (shard 6, 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

…text in GlossaryRelations* beforeAll

performAdminLogin does a full browser UI login (navigate to /signin, fill
credentials) and was hitting the 60-second beforeAll timeout in loaded CI
environments. getDefaultAdminAPIContext creates a context from the pre-saved
storageState without any UI interaction, which is both faster and more robust.

Affected: GlossaryRelationsGraph, GlossaryRelationsGraphPerf,
GlossaryTermRelationsGraphNested.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 2, 2026 15:38

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.

…ard 1 OOM

PlatformLineage, DataAssetLineage, and LineageInteraction were tagged
@basic (routing them to the Basic project on shard 1, 3 parallel workers).
Canvas-heavy lineage tests running concurrently with Docker overhead was
causing OOM → SIGTERM (exit 143) on shard 1. Remove the tag so these tests
return to the chromium shards (2–6) where load is distributed 1/5 each.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ree canvas memory

After a ReactFlow lineage test completes, the canvas nodes/edges/SVG stay
resident in Chrome until the context closes. Adding an afterEach that
navigates to about:blank triggers React to unmount the lineage graph and
lets Chrome GC the canvas objects before the next test loads a new page.
Applied to the three heaviest lineage files (LineageInteraction,
DataAssetLineage, LineageControls).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 3, 2026 10:13

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.

chirag-madlani and others added 2 commits July 3, 2026 20:22
…DOM detachment

After #29642 (keep Explore browse tree expanded), ExploreTree.tsx conditionally
skips setIsLoading on browse selections, making the loader-based strategy in
expandTreeNode unreliable. The child nodes then briefly detach/reattach during
the tree's setTreeData reconciliation, causing the serviceTitle click to fail
with "element was detached from the DOM" on all 3 retries.

Switch expandTreeNode to the same response-based pattern used by
expandServiceInExploreTree / expandDatabaseInExploreTree: set up waitForResponse
before the switcher click so the await anchors on the actual data fetch
completing, after which tree nodes are stable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eInteraction

Reverts the PLAYWRIGHT_BASIC_TEST_TAG_OBJ removal from these two files
(originally removed in 2b47c00 to fix shard 1 OOM).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 3, 2026 15:01

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.

@gitar-bot

gitar-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 1 resolved / 1 findings

Eliminates E2E test resource leaks by wrapping cleanup routines in try/finally blocks and refactoring auth patterns to use managed fixtures. Addresses resource setup leaking on failure by enforcing cleaner lifecycle management.

✅ 1 resolved
Quality: Resource setup outside try/finally still leaks on failure

📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/GlossaryVersionPage.spec.ts:51-55 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts:334-340
In several of the fixed specs, the resource that the new finally block cleans up is acquired (and initial API calls made) before the try block, leaving a residual leak window:

  • GlossaryVersionPage.spec.ts (~line 51-53): getApiContext(page) + glossary.create() + glossary.patch() run before the try {. If create/patch throws, the finally { glossary.delete; afterAction } is never reached, so the APIRequestContext still leaks — the exact class of bug this PR targets.
  • PersonaFlow.spec.ts (~line 334-336): browser.newContext() + userContext.newPage() + user.login(userPage) run before the try. If login() throws, userContext.close() in the finally never runs and the context leaks.

These are narrower windows than the ones fixed, but to fully close the leak the acquisition should be inside the try (or the try should start immediately after the context/page handle is obtained but before the first fallible call). Since these are test-only files and the failure window is small, severity is minor.

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

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 skip-pr-checks Bypass PR metadata validation check UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

playwright: fix unclosed browser contexts and page leaks in E2E tests

2 participants