This is an automated email from the ASF dual-hosted git repository.

choo121600 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new f90f1661795 Fix flaky E2E tests with API-based setup and data 
isolation (#64024)
f90f1661795 is described below

commit f90f16617957fc239fb9438fcb911e1ca03d073c
Author: Yeonguk Choo <[email protected]>
AuthorDate: Mon May 4 12:58:26 2026 +0900

    Fix flaky E2E tests with API-based setup and data isolation (#64024)
---
 airflow-core/src/airflow/ui/playwright.config.ts   |  11 -
 .../airflow/ui/tests/e2e/pages/AssetDetailPage.ts  |   8 +-
 .../airflow/ui/tests/e2e/pages/AssetListPage.ts    |  11 +-
 .../src/airflow/ui/tests/e2e/pages/BackfillPage.ts |  82 +-----
 .../ui/tests/e2e/pages/ConfigurationPage.ts        |   5 +-
 .../airflow/ui/tests/e2e/pages/ConnectionsPage.ts  |  23 +-
 .../src/airflow/ui/tests/e2e/pages/PluginsPage.ts  |   1 +
 .../airflow/ui/tests/e2e/pages/ProvidersPage.ts    |   7 +-
 .../ui/tests/e2e/pages/RequiredActionsPage.ts      |  16 +-
 .../src/airflow/ui/tests/e2e/pages/VariablePage.ts |   1 +
 .../src/airflow/ui/tests/e2e/specs/asset.spec.ts   |   1 -
 .../airflow/ui/tests/e2e/specs/backfill.spec.ts    |  73 +++--
 .../ui/tests/e2e/specs/configuration.spec.ts       |   1 -
 .../airflow/ui/tests/e2e/specs/connections.spec.ts | 309 ++++++++++-----------
 .../src/airflow/ui/tests/e2e/specs/plugins.spec.ts |   1 -
 .../src/airflow/ui/tests/e2e/specs/pools.spec.ts   |  90 +++---
 .../airflow/ui/tests/e2e/specs/providers.spec.ts   |   1 -
 .../ui/tests/e2e/specs/requiredAction.spec.ts      |  51 ++--
 .../airflow/ui/tests/e2e/specs/variable.spec.ts    | 174 ++++--------
 .../src/airflow/ui/tests/e2e/utils/test-helpers.ts |   8 +-
 20 files changed, 354 insertions(+), 520 deletions(-)

diff --git a/airflow-core/src/airflow/ui/playwright.config.ts 
b/airflow-core/src/airflow/ui/playwright.config.ts
index a7632fdf54f..301e59c01dc 100644
--- a/airflow-core/src/airflow/ui/playwright.config.ts
+++ b/airflow-core/src/airflow/ui/playwright.config.ts
@@ -110,17 +110,6 @@ export default defineConfig({
   retries: process.env.CI !== undefined && process.env.CI !== "" ? 4 : 0,
 
   testDir: "./tests/e2e/specs",
-  // TODO: Temporarily ignore flaky specs until stabilized
-  // See: #63036
-  testIgnore: [
-    "**/dag-runs-tab.spec.ts",
-    "**/dag-runs.spec.ts",
-    "**/dag-grid-view.spec.ts",
-    "**/task-logs.spec.ts",
-    "**/dag-tasks.spec.ts",
-    "**/variable.spec.ts",
-    "**/xcoms.spec.ts",
-  ],
 
   timeout: 60_000,
   use: {
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/AssetDetailPage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/AssetDetailPage.ts
index 33402a9dcde..432d700cafc 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/AssetDetailPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/AssetDetailPage.ts
@@ -60,13 +60,9 @@ export class AssetDetailPage extends BasePage {
    * Uses stable selectors based on text content and ARIA roles
    */
   private async verifyStatSection(labelText: string): Promise<void> {
-    const label = this.page.getByText(labelText, { exact: true });
+    const statContainer = this.page.getByTestId("stat").filter({ hasText: 
labelText });
 
-    await expect(label).toBeVisible();
-
-    // Find the button that follows the label in the same stat section
-    // Navigate to the label's parent container and find the first button 
within it
-    const statContainer = label.locator("xpath=./parent::*");
+    await expect(statContainer).toBeVisible();
     const button = statContainer.getByRole("button").first();
 
     await expect(button).toBeVisible();
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/AssetListPage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/AssetListPage.ts
index 19300a33024..58e0c75a7c3 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/AssetListPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/AssetListPage.ts
@@ -46,6 +46,7 @@ export class AssetListPage extends BasePage {
     await expect(async () => {
       await this.navigateTo("/assets");
       await this.page.waitForURL(/.*assets/, { timeout: 10_000 });
+      await this.waitForLoad();
     }).toPass({ intervals: [2000], timeout: 60_000 });
   }
 
@@ -66,15 +67,11 @@ export class AssetListPage extends BasePage {
 
   public async search(value: string): Promise<void> {
     await this.searchInput.fill(value);
-    await this.waitForTableData();
+    await this.waitForLoad();
   }
 
   public async waitForLoad(): Promise<void> {
-    await this.table.waitFor({ state: "visible", timeout: 30_000 });
-    await this.waitForTableData();
-  }
-
-  private async waitForTableData(): Promise<void> {
-    await expect(this.rows.locator("td a").first()).toBeVisible({ timeout: 
30_000 });
+    await expect(this.table).toBeVisible({ timeout: 10_000 });
+    await expect(this.rows.locator("td a").first()).toBeVisible({ timeout: 
10_000 });
   }
 }
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/BackfillPage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/BackfillPage.ts
index b73d78c24f5..425c5cb543e 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/BackfillPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/BackfillPage.ts
@@ -20,6 +20,12 @@ import { expect } from "@playwright/test";
 import type { Locator, Page } from "@playwright/test";
 import { testConfig } from "playwright.config";
 import { BasePage } from "tests/e2e/pages/BasePage";
+import {
+  apiCancelAllActiveBackfills,
+  apiCancelBackfill,
+  apiWaitForBackfillComplete,
+  apiWaitForNoActiveBackfill,
+} from "tests/e2e/utils/test-helpers";
 
 export const REPROCESS_API_TO_UI = {
   completed: "All Runs",
@@ -54,10 +60,6 @@ type BackfillApiResponse = {
   id: number;
 };
 
-type BackfillListApiResponse = {
-  backfills: Array<BackfillApiResponse>;
-};
-
 const {
   connection: { baseUrl },
 } = testConfig;
@@ -114,26 +116,11 @@ export class BackfillPage extends BasePage {
   }
 
   public async cancelAllActiveBackfillsViaApi(dagId: string): Promise<void> {
-    const response = await 
this.page.request.get(`${baseUrl}/api/v2/backfills?dag_id=${dagId}&limit=100`, {
-      timeout: 30_000,
-    });
-
-    expect(response.ok()).toBe(true);
-    const data = (await response.json()) as BackfillListApiResponse;
-
-    for (const backfill of data.backfills) {
-      if (backfill.completed_at === null) {
-        await this.cancelBackfillViaApi(backfill.id);
-      }
-    }
+    await apiCancelAllActiveBackfills(this.page, dagId);
   }
 
   public async cancelBackfillViaApi(backfillId: number): Promise<void> {
-    const response = await 
this.page.request.put(`${baseUrl}/api/v2/backfills/${backfillId}/cancel`, {
-      timeout: 30_000,
-    });
-
-    expect([200, 409]).toContain(response.status());
+    await apiCancelBackfill(this.page, backfillId);
   }
 
   public async clickCancelButton(): Promise<void> {
@@ -464,59 +451,10 @@ export class BackfillPage extends BasePage {
   }
 
   public async waitForBackfillComplete(backfillId: number, timeout: number = 
120_000): Promise<void> {
-    await expect
-      .poll(
-        async () => {
-          try {
-            const response = await 
this.page.request.get(`${baseUrl}/api/v2/backfills/${backfillId}`, {
-              timeout: 30_000,
-            });
-
-            if (!response.ok()) {
-              return false;
-            }
-            const data = (await response.json()) as BackfillApiResponse;
-
-            return data.completed_at !== null;
-          } catch {
-            return false;
-          }
-        },
-        {
-          intervals: [2000, 5000, 10_000],
-          message: `Backfill ${backfillId} did not complete within 
${timeout}ms`,
-          timeout,
-        },
-      )
-      .toBeTruthy();
+    await apiWaitForBackfillComplete(this.page, backfillId, timeout);
   }
 
   public async waitForNoActiveBackfillViaApi(dagId: string, timeout: number = 
120_000): Promise<void> {
-    await expect
-      .poll(
-        async () => {
-          try {
-            const response = await this.page.request.get(
-              `${baseUrl}/api/v2/backfills?dag_id=${dagId}&limit=100`,
-              { timeout: 30_000 },
-            );
-
-            if (!response.ok()) {
-              return false;
-            }
-            const data = (await response.json()) as BackfillListApiResponse;
-
-            return data.backfills.every((b) => b.completed_at !== null);
-          } catch {
-            return false;
-          }
-        },
-        {
-          intervals: [2000, 5000, 10_000],
-          message: `Active backfills for Dag ${dagId} did not clear within 
${timeout}ms`,
-          timeout,
-        },
-      )
-      .toBeTruthy();
+    await apiWaitForNoActiveBackfill(this.page, dagId, timeout);
   }
 }
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/ConfigurationPage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/ConfigurationPage.ts
index d6b74c030b5..7dd9cc27b68 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/ConfigurationPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/ConfigurationPage.ts
@@ -42,11 +42,12 @@ export class ConfigurationPage extends BasePage {
     await expect(async () => {
       await this.navigateTo("/configs");
       await this.page.waitForURL(/.*configs/, { timeout: 10_000 });
+      await this.waitForLoad();
     }).toPass({ intervals: [2000], timeout: 60_000 });
   }
 
   public async waitForLoad(): Promise<void> {
-    await this.table.waitFor({ state: "visible", timeout: 30_000 });
-    await this.rows.first().waitFor({ state: "visible", timeout: 30_000 });
+    await expect(this.table).toBeVisible({ timeout: 10_000 });
+    await expect(this.rows.first()).toBeVisible({ timeout: 10_000 });
   }
 }
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/ConnectionsPage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/ConnectionsPage.ts
index 20fbecb6d5a..77eef9f28b7 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/ConnectionsPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/ConnectionsPage.ts
@@ -270,22 +270,23 @@ export class ConnectionsPage extends BasePage {
       return [];
     }
 
+    const headerTexts = await this.page.locator("thead th").allTextContents();
+    const idColumnIndex = headerTexts.findIndex((text) => 
/connection\s*id/i.test(text));
+
+    if (idColumnIndex === -1) {
+      throw new Error(`"Connection ID" column not found in headers: 
${JSON.stringify(headerTexts)}`);
+    }
+
+    const rows = this.page.locator("tbody tr");
     const connectionIds: Array<string> = [];
 
     for (let i = 0; i < stableRowCount; i++) {
       try {
-        const row = rowLocator.nth(i);
-        const cells = row.locator("td");
-        const cellCount = await cells.count();
+        const cell = rows.nth(i).locator("td").nth(idColumnIndex);
+        const text = await cell.textContent({ timeout: 3000 });
 
-        if (cellCount > 1) {
-          // Second cell (after checkbox) contains the connection ID.
-          const idCell = cells.nth(1);
-          const text = await idCell.textContent({ timeout: 3000 });
-
-          if (text !== null && text.trim() !== "") {
-            connectionIds.push(text.trim());
-          }
+        if (text !== null && text.trim() !== "") {
+          connectionIds.push(text.trim());
         }
       } catch {
         continue;
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/PluginsPage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/PluginsPage.ts
index 636048b8949..806e07c88ac 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/PluginsPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/PluginsPage.ts
@@ -42,6 +42,7 @@ export class PluginsPage extends BasePage {
     await expect(async () => {
       await this.navigateTo("/plugins");
       await this.page.waitForURL(/.*plugins/, { timeout: 10_000 });
+      await this.waitForLoad();
     }).toPass({ intervals: [2000], timeout: 60_000 });
   }
 
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/ProvidersPage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/ProvidersPage.ts
index a894f225f34..156fe8c7e99 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/ProvidersPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/ProvidersPage.ts
@@ -63,7 +63,7 @@ export class ProvidersPage extends BasePage {
   public async navigate(): Promise<void> {
     await expect(async () => {
       await this.navigateTo("/providers");
-      await this.page.waitForURL(/.*providers/, { timeout: 10_000 });
+      await this.waitForLoad();
     }).toPass({ intervals: [2000], timeout: 60_000 });
   }
 
@@ -85,8 +85,7 @@ export class ProvidersPage extends BasePage {
   }
 
   public async waitForLoad(): Promise<void> {
-    await expect(this.table).toBeVisible({ timeout: 30_000 });
-    await expect(this.rows.first()).toBeVisible({ timeout: 30_000 });
-    await expect(this.packageLinkAt(0)).toBeVisible({ timeout: 30_000 });
+    await expect(this.table).toBeVisible({ timeout: 10_000 });
+    await expect(this.rows.first()).toBeVisible({ timeout: 10_000 });
   }
 }
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/RequiredActionsPage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/RequiredActionsPage.ts
index 99a77923b4c..d85800cc7ba 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/RequiredActionsPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/RequiredActionsPage.ts
@@ -18,9 +18,9 @@
  */
 import { expect, type APIRequestContext, type Locator, type Page } from 
"@playwright/test";
 import { testConfig } from "playwright.config";
+import { apiTriggerDagRun, waitForDagReady } from 
"tests/e2e/utils/test-helpers";
 
 import { BasePage } from "./BasePage";
-import { DagsPage } from "./DagsPage";
 
 export class RequiredActionsPage extends BasePage {
   public readonly actionsTable: Locator;
@@ -100,7 +100,7 @@ export class RequiredActionsPage extends BasePage {
       await expect(taskLocator).toBeVisible({ timeout: 20_000 });
       await this.page.mouse.move(0, 0);
       await taskLocator.click({ timeout: 5000 });
-    }).toPass({ intervals: [5000, 10_000, 15_000], timeout: 120_000 });
+    }).toPass({ intervals: [15_000], timeout: 120_000 });
   }
 
   private async clickRequiredActionLink(): Promise<void> {
@@ -228,13 +228,13 @@ export class RequiredActionsPage extends BasePage {
   }
 
   private async runHITLFlow(dagId: string, approve: boolean): Promise<string> {
-    const dagsPage = new DagsPage(this.page);
-
-    const dagRunId = await dagsPage.triggerDag(dagId);
+    const { baseUrl } = testConfig.connection;
 
-    if (dagRunId === null) {
-      throw new Error("Failed to trigger Dag - dagRunId is null");
-    }
+    await waitForDagReady(this.apiRequest, dagId);
+    await this.apiRequest.patch(`${baseUrl}/api/v2/dags/${dagId}`, {
+      data: { is_paused: false },
+    });
+    const { dagRunId } = await apiTriggerDagRun(this.apiRequest, dagId);
 
     await this.waitForDagRunState(dagId, dagRunId, "Running");
 
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/VariablePage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/VariablePage.ts
index 6db90a998aa..7fd574ed2ca 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/VariablePage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/VariablePage.ts
@@ -55,6 +55,7 @@ export class VariablePage extends BasePage {
     await expect(async () => {
       await this.navigateTo("/variables");
       await this.page.waitForURL(/.*variables/, { timeout: 10_000 });
+      await this.waitForLoad();
     }).toPass({ intervals: [2000], timeout: 60_000 });
   }
 
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
index f1f11eed513..f3708586e36 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
@@ -24,7 +24,6 @@ test.describe("Assets Page", () => {
   // assetData is triggered once per worker via beforeEach.
   test.beforeEach(async ({ assetData: _data, assetListPage }) => {
     await assetListPage.navigate();
-    await assetListPage.waitForLoad();
   });
 
   test("verify assets page heading", async ({ assetListPage }) => {
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/backfill.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/backfill.spec.ts
index f47d40e3171..6168d3afbdd 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/backfill.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/backfill.spec.ts
@@ -16,10 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { test, expect } from "@playwright/test";
-import { testConfig, AUTH_FILE } from "playwright.config";
-import { BackfillPage, REPROCESS_API_TO_UI } from 
"tests/e2e/pages/BackfillPage";
+import { testConfig } from "playwright.config";
+import { expect, test } from "tests/e2e/fixtures";
+import { REPROCESS_API_TO_UI } from "tests/e2e/pages/BackfillPage";
 import type { ReprocessBehaviorApi } from "tests/e2e/pages/BackfillPage";
+import {
+  apiCancelAllActiveBackfills,
+  apiCreateBackfill,
+  apiWaitForBackfillComplete,
+  waitForDagReady,
+} from "tests/e2e/utils/test-helpers";
 
 // Fixed past dates avoid non-determinism from relative date calculations.
 // Controls tests use wide, non-overlapping ranges so the scheduler cannot
@@ -53,44 +59,34 @@ test.describe("Backfill", () => {
       { behavior: "failed", dates: FIXED_DATES.set3 },
     ];
 
-    test.beforeAll(async ({ browser }) => {
+    test.beforeAll(async ({ authenticatedRequest }) => {
       test.setTimeout(300_000);
 
-      const context = await browser.newContext({ storageState: AUTH_FILE });
-      const page = await context.newPage();
-      const setupPage = new BackfillPage(page);
-
-      await setupPage.navigateToDagDetail(testDagId);
-      await setupPage.cancelAllActiveBackfillsViaApi(testDagId);
+      await waitForDagReady(authenticatedRequest, testDagId);
+      await authenticatedRequest.patch(`/api/v2/dags/${testDagId}`, {
+        data: { is_paused: false },
+      });
+      await apiCancelAllActiveBackfills(authenticatedRequest, testDagId);
 
       for (const config of backfillConfigs) {
-        const backfillId = await setupPage.createBackfill(testDagId, {
+        const backfillId = await apiCreateBackfill(authenticatedRequest, 
testDagId, {
           fromDate: config.dates.from,
           reprocessBehavior: config.behavior,
           toDate: config.dates.to,
         });
 
-        await setupPage.waitForBackfillComplete(backfillId);
+        await apiWaitForBackfillComplete(authenticatedRequest, backfillId, 
240_000);
       }
-
-      await context.close();
     });
 
-    test.afterAll(async ({ browser }) => {
-      const context = await browser.newContext({ storageState: AUTH_FILE });
-      const page = await context.newPage();
-      const cleanupPage = new BackfillPage(page);
-
-      await cleanupPage.cancelAllActiveBackfillsViaApi(testDagId);
-      await context.close();
+    test.afterAll(async ({ authenticatedRequest }) => {
+      await apiCancelAllActiveBackfills(authenticatedRequest, 
testDagId).catch(() => undefined);
     });
 
     for (const config of backfillConfigs) {
-      test.fixme(`verify backfill with 
'${REPROCESS_API_TO_UI[config.behavior]}' behavior`, async ({
-        page,
+      test(`verify backfill with '${REPROCESS_API_TO_UI[config.behavior]}' 
behavior`, async ({
+        backfillPage,
       }) => {
-        const backfillPage = new BackfillPage(page);
-
         await backfillPage.navigateToBackfillsTab(testDagId);
 
         const details = await backfillPage.getBackfillDetailsByDateRange({
@@ -106,9 +102,7 @@ test.describe("Backfill", () => {
       });
     }
 
-    test.fixme("Verify backfill table filters", async ({ page }) => {
-      const backfillPage = new BackfillPage(page);
-
+    test("Verify backfill table filters", async ({ backfillPage }) => {
       await backfillPage.navigateToBackfillsTab(testDagId);
 
       const tableHeaders = backfillPage.backfillsTable.locator("thead th");
@@ -120,7 +114,7 @@ test.describe("Backfill", () => {
 
       await backfillPage.openFilterMenu();
 
-      const filterMenuItems = page.getByRole("menuitem");
+      const filterMenuItems = backfillPage.page.getByRole("menuitem");
 
       await expect(filterMenuItems).not.toHaveCount(0);
 
@@ -130,7 +124,7 @@ test.describe("Backfill", () => {
       expect(columnToToggle).not.toBe("");
 
       await backfillPage.toggleColumn(columnToToggle);
-      await page.keyboard.press("Escape");
+      await backfillPage.page.keyboard.press("Escape");
 
       await 
expect(backfillPage.getColumnHeader(columnToToggle)).not.toBeVisible();
 
@@ -138,7 +132,7 @@ test.describe("Backfill", () => {
 
       await backfillPage.openFilterMenu();
       await backfillPage.toggleColumn(columnToToggle);
-      await page.keyboard.press("Escape");
+      await backfillPage.page.keyboard.press("Escape");
 
       await expect(backfillPage.getColumnHeader(columnToToggle)).toBeVisible();
 
@@ -151,9 +145,7 @@ test.describe("Backfill", () => {
 
     const testDagId = testConfig.testDag.id;
 
-    test("verify date range selection (start date, end date)", async ({ page 
}) => {
-      const backfillPage = new BackfillPage(page);
-
+    test("verify date range selection (start date, end date)", async ({ 
backfillPage }) => {
       await backfillPage.navigateToDagDetail(testDagId);
       await backfillPage.openBackfillDialog();
       await backfillPage.backfillFromDateInput.fill("2025-01-10T00:00");
@@ -168,19 +160,16 @@ test.describe("Backfill", () => {
 
     const testDagId = testConfig.testDag.id;
 
-    let backfillPage: BackfillPage;
-
-    test.beforeEach(async ({ page }) => {
-      backfillPage = new BackfillPage(page);
+    test.beforeEach(async ({ backfillPage }) => {
       await backfillPage.cancelAllActiveBackfillsViaApi(testDagId);
       await backfillPage.waitForNoActiveBackfillViaApi(testDagId, 30_000);
     });
 
-    test.afterEach(async () => {
+    test.afterEach(async ({ backfillPage }) => {
       await backfillPage.cancelAllActiveBackfillsViaApi(testDagId);
     });
 
-    test("verify pause and resume backfill", async () => {
+    test("verify pause and resume backfill", async ({ backfillPage }) => {
       const dates = FIXED_DATES.controls.resumePause;
 
       // Create + pause atomically to eliminate race with scheduler.
@@ -201,7 +190,7 @@ test.describe("Backfill", () => {
       await expect(backfillPage.unpauseButton).toBeVisible({ timeout: 10_000 
});
     });
 
-    test.fixme("verify cancel backfill", async () => {
+    test("verify cancel backfill", async ({ backfillPage }) => {
       const dates = FIXED_DATES.controls.cancel;
 
       // Create + pause atomically to eliminate race with scheduler.
@@ -219,7 +208,7 @@ test.describe("Backfill", () => {
       await expect(backfillPage.cancelButton).not.toBeVisible({ timeout: 
10_000 });
     });
 
-    test("verify cancelled backfill cannot be resumed", async () => {
+    test("verify cancelled backfill cannot be resumed", async ({ backfillPage 
}) => {
       const dates = FIXED_DATES.controls.cancelledNoResume;
 
       // Setup via API: create and cancel directly (UI cancel is tested above).
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/configuration.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/configuration.spec.ts
index b1aa8d1490a..fd35506aac0 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/configuration.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/configuration.spec.ts
@@ -21,7 +21,6 @@ import { expect, test } from "tests/e2e/fixtures";
 test.describe("Configuration Page", () => {
   test.beforeEach(async ({ configurationPage }) => {
     await configurationPage.navigate();
-    await configurationPage.waitForLoad();
   });
 
   test("verify configuration displays", async ({ configurationPage }) => {
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/connections.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/connections.spec.ts
index eb8ed665ce9..a58765ff8c1 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/connections.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/connections.spec.ts
@@ -16,189 +16,171 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { expect, test } from "@playwright/test";
-import { testConfig, AUTH_FILE } from "playwright.config";
-import { ConnectionsPage } from "tests/e2e/pages/ConnectionsPage";
+import { expect, test } from "tests/e2e/fixtures";
+import { uniqueRunId } from "tests/e2e/utils/test-helpers";
 
 test.describe("Connections Page - List and Display", () => {
-  let connectionsPage: ConnectionsPage;
-  const { baseUrl } = testConfig.connection;
-  const timestamp = Date.now();
-  const seedConnection = {
-    conn_type: "http",
-    connection_id: `list_seed_conn_${timestamp}`,
-    host: "seed.example.com",
-  };
-
-  test.beforeEach(({ page }) => {
-    connectionsPage = new ConnectionsPage(page);
-  });
+  let seedConnection: { conn_type: string; connection_id: string; host: string 
};
 
-  test.beforeAll(async ({ browser }) => {
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
+  test.beforeAll(async ({ authenticatedRequest }) => {
+    seedConnection = {
+      conn_type: "http",
+      connection_id: `list_seed_conn_${uniqueRunId("t")}`,
+      host: "seed.example.com",
+    };
 
-    const response = await page.request.post(`${baseUrl}/api/v2/connections`, {
+    const response = await authenticatedRequest.post(`/api/v2/connections`, {
       data: seedConnection,
       headers: { "Content-Type": "application/json" },
+      timeout: 30_000,
     });
 
     expect([200, 201, 409]).toContain(response.status());
-    await context.close();
   });
 
-  test.afterAll(async ({ browser }) => {
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
-
-    const response = await 
page.request.delete(`${baseUrl}/api/v2/connections/list_seed_conn_${timestamp}`);
+  test.afterAll(async ({ authenticatedRequest }) => {
+    const response = await authenticatedRequest.delete(
+      `/api/v2/connections/${seedConnection.connection_id}`,
+      { timeout: 30_000 },
+    );
 
-    expect([204, 404]).toContain(response.status());
-    await context.close();
+    if (response.status() !== 404 && !response.ok()) {
+      console.warn(`Cleanup failed for ${seedConnection.connection_id}: 
${response.status()}`);
+    }
   });
 
-  test("should display connections list page", async () => {
+  test("should display connections list page", async ({ connectionsPage }) => {
     await connectionsPage.navigate();
 
-    // Verify the page is loaded
     expect(connectionsPage.page.url()).toContain("/connections");
 
-    // Verify table or list is visible
     await expect(connectionsPage.connectionsTable).toBeVisible();
   });
 
-  test("should display connections with correct columns", async () => {
+  test("should display connections with correct columns", async ({ 
connectionsPage }) => {
     await connectionsPage.navigate();
 
-    // Check that we have at least one row
-    const count = await connectionsPage.getConnectionCount();
-
-    expect(count).toBeGreaterThan(0);
+    await expect
+      .poll(async () => connectionsPage.getConnectionCount(), { intervals: 
[1000], timeout: 15_000 })
+      .toBeGreaterThan(0);
 
-    // Verify column headers exist
     await expect(connectionsPage.connectionIdHeader).toBeVisible();
     await expect(connectionsPage.connectionTypeHeader).toBeVisible();
     await expect(connectionsPage.hostHeader).toBeVisible();
 
-    // Verify the seed connection is displayed in the list
     await connectionsPage.verifyConnectionInList(seedConnection.connection_id, 
seedConnection.conn_type);
   });
 
-  test("should have Add button visible", async () => {
+  test("should have Add button visible", async ({ connectionsPage }) => {
     await connectionsPage.navigate();
     await expect(connectionsPage.addButton).toBeVisible();
   });
 });
 
 test.describe("Connections Page - CRUD Operations", () => {
-  let connectionsPage: ConnectionsPage;
-  const { baseUrl } = testConfig.connection;
-  const timestamp = Date.now();
-
-  // Connection created via API in beforeAll - used for edit and display tests
-  const existingConnection = {
-    conn_type: "postgres",
-    connection_id: `existing_conn_${timestamp}`,
-    host: `existing-host-${timestamp}.example.com`,
-    login: `existing_user_${timestamp}`,
+  let existingConnection: { conn_type: string; connection_id: string; host: 
string; login: string };
+  let updatedConnection: {
+    conn_type: string;
+    description: string;
+    host: string;
+    login: string;
+    port: number;
   };
 
-  const updatedConnection = {
-    conn_type: "postgres",
-    description: `Updated test connection at ${new Date().toISOString()}`,
-    host: `updated-host-${timestamp}.example.com`,
-    login: `updated_user_${timestamp}`,
-    port: 5433,
-  };
+  const createdConnIds: Array<string> = [];
 
-  // Connection created via UI in test - used for create and delete tests
-  const newConnection = {
-    conn_type: "postgres",
-    connection_id: `new_conn_${timestamp}`,
-    description: `Test connection created at ${new Date().toISOString()}`,
-    extra: JSON.stringify({
-      options: "-c statement_timeout=5000",
-      sslmode: "require",
-    }),
-    host: `new-host-${timestamp}.example.com`,
-    login: `new_user_${timestamp}`,
-    password: `new_password_${timestamp}`,
-    port: 5432,
-    schema: "test_db",
-  };
+  test.beforeAll(async ({ authenticatedRequest }) => {
+    const timestamp = uniqueRunId("t");
 
-  test.beforeEach(({ page }) => {
-    connectionsPage = new ConnectionsPage(page);
-  });
+    existingConnection = {
+      conn_type: "postgres",
+      connection_id: `existing_conn_${timestamp}`,
+      host: `existing-host-${timestamp}.example.com`,
+      login: `existing_user_${timestamp}`,
+    };
 
-  test.beforeAll(async ({ browser }) => {
-    // Create existing connection via API for edit and display tests
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
+    updatedConnection = {
+      conn_type: "postgres",
+      description: `Updated test connection at ${new Date().toISOString()}`,
+      host: `updated-host-${timestamp}.example.com`,
+      login: `updated_user_${timestamp}`,
+      port: 5433,
+    };
 
-    await page.request.post(`${baseUrl}/api/v2/connections`, {
+    await authenticatedRequest.post(`/api/v2/connections`, {
       data: existingConnection,
       headers: { "Content-Type": "application/json" },
+      timeout: 30_000,
     });
-
-    await context.close();
   });
 
-  test.afterAll(async ({ browser }) => {
-    // Cleanup all test connections via API
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
-
-    for (const connId of [
-      existingConnection.connection_id,
-      newConnection.connection_id,
-      `temp_conn_${timestamp}_delete`,
-    ]) {
-      await page.request.delete(`${baseUrl}/api/v2/connections/${connId}`);
-    }
+  test.afterAll(async ({ authenticatedRequest }) => {
+    for (const connId of [existingConnection.connection_id, 
...createdConnIds]) {
+      const response = await 
authenticatedRequest.delete(`/api/v2/connections/${connId}`, {
+        timeout: 30_000,
+      });
 
-    await context.close();
+      if (response.status() !== 404 && !response.ok()) {
+        console.warn(`Cleanup failed for ${connId}: ${response.status()}`);
+      }
+    }
   });
 
-  test.fixme("should create a new connection and display it in list", async () 
=> {
-    test.setTimeout(120_000);
-    await connectionsPage.navigate();
+  test("should create a new connection and display it in list", async ({ 
connectionsPage }) => {
+    test.slow();
+
+    const connId = `new_conn_${uniqueRunId("create")}`;
+
+    createdConnIds.push(connId);
 
-    // Create connection via UI
+    const newConnection = {
+      conn_type: "postgres",
+      connection_id: connId,
+      description: `Test connection created at ${new Date().toISOString()}`,
+      extra: JSON.stringify({
+        options: "-c statement_timeout=5000",
+        sslmode: "require",
+      }),
+      host: `new-host-${connId}.example.com`,
+      login: `new_user_${connId}`,
+      password: `new_password_${connId}`,
+      port: 5432,
+      schema: "test_db",
+    };
+
+    await connectionsPage.navigate();
     await connectionsPage.createConnection(newConnection);
+
     const exists = await 
connectionsPage.connectionExists(newConnection.connection_id);
 
     expect(exists).toBeTruthy();
-    // Verify it appears in the list with correct type
     await connectionsPage.verifyConnectionInList(newConnection.connection_id, 
newConnection.conn_type);
   });
 
-  test.fixme("should edit an existing connection", async () => {
-    test.setTimeout(120_000);
+  test("should edit an existing connection", async ({ connectionsPage }) => {
+    test.slow();
     await connectionsPage.navigate();
 
-    // Verify connection exists before editing (created in beforeAll)
     const exists = await 
connectionsPage.connectionExists(existingConnection.connection_id);
 
     expect(exists).toBeTruthy();
 
-    // Edit the connection
     await connectionsPage.editConnection(existingConnection.connection_id, 
updatedConnection);
 
-    // Verify the connection still exists after editing
     const stillExists = await 
connectionsPage.connectionExists(existingConnection.connection_id);
 
     expect(stillExists).toBeTruthy();
   });
 
-  test("should delete a connection", async () => {
-    test.setTimeout(120_000);
+  test("should delete a connection", async ({ connectionsPage }) => {
+    test.slow();
+
+    const tempConnId = `temp_conn_${uniqueRunId("del")}`;
 
-    // Create a temporary connection for deletion test
     const tempConnection = {
       conn_type: "postgres",
-      connection_id: `temp_conn_${timestamp}_delete`,
-      host: `temp-host-${timestamp}.example.com`,
+      connection_id: tempConnId,
+      host: "temp-host.example.com",
       login: "temp_user",
       password: "temp_password",
     };
@@ -210,7 +192,6 @@ test.describe("Connections Page - CRUD Operations", () => {
 
     expect(exists).toBeTruthy();
 
-    // Delete the connection
     await connectionsPage.deleteConnection(tempConnection.connection_id);
 
     const stillExists = await 
connectionsPage.connectionExists(tempConnection.connection_id);
@@ -220,70 +201,68 @@ test.describe("Connections Page - CRUD Operations", () => 
{
 });
 
 test.describe("Connections Page - Search and Filter", () => {
-  let connectionsPage: ConnectionsPage;
-  const { baseUrl } = testConfig.connection;
-  const timestamp = Date.now();
-
-  const searchTestConnections = [
-    {
-      conn_type: "postgres",
-      connection_id: `production_search_${timestamp}`,
-      host: "prod-db.example.com",
-      login: "prod_user",
-    },
-    {
-      conn_type: "mysql",
-      connection_id: `staging_search_${timestamp}`,
-      host: "staging-db.example.com",
-      login: "staging_user",
-    },
-    {
-      conn_type: "http",
-      connection_id: `development_search_${timestamp}`,
-      host: "dev-api.example.com",
-      login: "dev_user",
-    },
-  ];
-
-  test.beforeEach(({ page }) => {
-    connectionsPage = new ConnectionsPage(page);
-  });
-
-  test.beforeAll(async ({ browser }) => {
-    // Create test connections
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
+  let searchTestConnections: Array<{
+    conn_type: string;
+    connection_id: string;
+    host: string;
+    login: string;
+  }>;
+
+  test.beforeAll(async ({ authenticatedRequest }) => {
+    const timestamp = uniqueRunId("t");
+
+    searchTestConnections = [
+      {
+        conn_type: "postgres",
+        connection_id: `production_search_${timestamp}`,
+        host: "prod-db.example.com",
+        login: "prod_user",
+      },
+      {
+        conn_type: "mysql",
+        connection_id: `staging_search_${timestamp}`,
+        host: "staging-db.example.com",
+        login: "staging_user",
+      },
+      {
+        conn_type: "http",
+        connection_id: `development_search_${timestamp}`,
+        host: "dev-api.example.com",
+        login: "dev_user",
+      },
+    ];
 
     for (const conn of searchTestConnections) {
-      const response = await 
page.request.post(`${baseUrl}/api/v2/connections`, {
+      const response = await authenticatedRequest.post(`/api/v2/connections`, {
         data: JSON.stringify(conn),
         headers: {
           "Content-Type": "application/json",
         },
+        timeout: 30_000,
       });
 
       expect([200, 201, 409]).toContain(response.status());
     }
   });
 
-  test.afterAll(async ({ browser }) => {
-    // Cleanup
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
-
+  test.afterAll(async ({ authenticatedRequest }) => {
     for (const conn of searchTestConnections) {
-      const response = await 
page.request.delete(`${baseUrl}/api/v2/connections/${conn.connection_id}`);
+      const response = await 
authenticatedRequest.delete(`/api/v2/connections/${conn.connection_id}`, {
+        timeout: 30_000,
+      });
 
-      expect([204, 404]).toContain(response.status());
+      if (response.status() !== 404 && !response.ok()) {
+        console.warn(`Cleanup failed for ${conn.connection_id}: 
${response.status()}`);
+      }
     }
   });
 
-  test("should filter connections by search term", async () => {
+  test("should filter connections by search term", async ({ connectionsPage }) 
=> {
     await connectionsPage.navigate();
 
-    const initialCount = await connectionsPage.getConnectionCount();
-
-    expect(initialCount).toBeGreaterThan(0);
+    await expect
+      .poll(async () => connectionsPage.getConnectionCount(), { intervals: 
[1000], timeout: 30_000 })
+      .toBeGreaterThan(0);
 
     const searchTerm = "production";
 
@@ -294,7 +273,6 @@ test.describe("Connections Page - Search and Filter", () => 
{
         async () => {
           const ids = await connectionsPage.getConnectionIds();
 
-          // Verify we have results AND they match the search term
           return ids.length > 0 && ids.every((id) => 
id.toLowerCase().includes(searchTerm.toLowerCase()));
         },
         { intervals: [500], timeout: 10_000 },
@@ -309,18 +287,24 @@ test.describe("Connections Page - Search and Filter", () 
=> {
     }
   });
 
-  test("should display all connections when search is cleared", async () => {
-    test.setTimeout(120_000);
+  test("should display all connections when search is cleared", async ({ 
connectionsPage }) => {
+    test.slow();
     await connectionsPage.navigate();
 
-    const initialCount = await connectionsPage.getConnectionCount();
+    let initialCount = 0;
 
-    expect(initialCount).toBeGreaterThan(0);
+    await expect
+      .poll(
+        async () => {
+          initialCount = await connectionsPage.getConnectionCount();
 
-    // Search for something
-    await connectionsPage.searchConnections("production");
+          return initialCount;
+        },
+        { intervals: [1000], timeout: 30_000 },
+      )
+      .toBeGreaterThan(0);
 
-    // Wait for search results
+    await connectionsPage.searchConnections("production");
     await expect
       .poll(
         async () => {
@@ -332,7 +316,6 @@ test.describe("Connections Page - Search and Filter", () => 
{
       )
       .toBe(true);
 
-    // Clear search
     await connectionsPage.searchConnections("");
 
     const finalCount = await connectionsPage.getConnectionCount();
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/plugins.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/plugins.spec.ts
index 6207a74fec9..7d1face995d 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/plugins.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/plugins.spec.ts
@@ -21,7 +21,6 @@ import { expect, test } from "tests/e2e/fixtures";
 test.describe("Plugins Page", () => {
   test.beforeEach(async ({ pluginsPage }) => {
     await pluginsPage.navigate();
-    await pluginsPage.waitForLoad();
   });
 
   test("verify plugins page heading is visible", async ({ pluginsPage }) => {
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/pools.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/pools.spec.ts
index 93574487557..dd97e4dd999 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/pools.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/pools.spec.ts
@@ -16,84 +16,94 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { test } from "@playwright/test";
-import { AUTH_FILE } from "playwright.config";
-import { PoolsPage } from "tests/e2e/pages/PoolsPage";
+import { expect, test } from "tests/e2e/fixtures";
+import { uniqueRunId } from "tests/e2e/utils/test-helpers";
 
 test.describe("Pools Page", () => {
   test.setTimeout(60_000);
 
-  let poolsPage: PoolsPage;
-  const testPoolName = `test_pool_${Date.now()}`;
+  let testPoolName: string;
   const testPoolSlots = 10;
   const testPoolDescription = "E2E test pool";
   const updatedSlots = 25;
-  const createDeletePoolName = `test_crud_pool_${Date.now()}`;
-  const createDeletePoolSlots = 5;
-
-  test.beforeAll(async ({ browser }) => {
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
-    const setupPoolsPage = new PoolsPage(page);
-
-    await setupPoolsPage.navigate();
-    await setupPoolsPage.createPool(testPoolName, testPoolSlots, 
testPoolDescription);
-
-    await context.close();
+  const createdPoolNames: Array<string> = [];
+
+  test.beforeAll(async ({ authenticatedRequest }) => {
+    testPoolName = `test_pool_${uniqueRunId("pool")}`;
+
+    const response = await authenticatedRequest.post("/api/v2/pools", {
+      data: {
+        description: testPoolDescription,
+        include_deferred: false,
+        name: testPoolName,
+        slots: testPoolSlots,
+      },
+      headers: { "Content-Type": "application/json" },
+    });
+
+    expect([200, 201, 409]).toContain(response.status());
   });
 
-  test.afterAll(async ({ browser }) => {
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
-
-    await 
page.request.delete(`/api/v2/pools/${encodeURIComponent(testPoolName)}`);
-    await 
page.request.delete(`/api/v2/pools/${encodeURIComponent(createDeletePoolName)}`);
-
-    await context.close();
-  });
+  test.afterAll(async ({ authenticatedRequest }) => {
+    for (const name of [testPoolName, ...createdPoolNames]) {
+      const response = await 
authenticatedRequest.delete(`/api/v2/pools/${encodeURIComponent(name)}`);
 
-  test.beforeEach(({ page }) => {
-    poolsPage = new PoolsPage(page);
+      if (response.status() !== 404 && !response.ok()) {
+        console.warn(`Pool cleanup failed for ${name}: ${response.status()}`);
+      }
+    }
   });
 
-  test("verify pools list displays with default_pool", async () => {
+  test("verify pools list displays with default_pool", async ({ poolsPage }) 
=> {
     await poolsPage.navigate();
     await poolsPage.verifyPoolsListDisplays();
   });
 
-  test("verify test pool exists in list", async () => {
+  test("verify test pool exists in list", async ({ poolsPage }) => {
     await poolsPage.navigate();
     await poolsPage.verifyPoolExists(testPoolName);
   });
 
-  test("verify pool slots display correctly", async () => {
+  test("verify pool slots display correctly", async ({ poolsPage }) => {
     await poolsPage.navigate();
     await poolsPage.verifyPoolSlots(testPoolName, testPoolSlots);
   });
 
-  test("edit pool slots", async () => {
+  test("edit pool slots", async ({ poolsPage }) => {
     await poolsPage.navigate();
     await poolsPage.editPoolSlots(testPoolName, updatedSlots);
     await poolsPage.navigate();
     await poolsPage.verifyPoolSlots(testPoolName, updatedSlots);
   });
 
-  test("verify pool usage displays", async () => {
+  test("verify pool usage displays", async ({ poolsPage }) => {
     await poolsPage.navigate();
     await poolsPage.verifyPoolUsageDisplays("default_pool");
   });
 
-  test("create a new pool", async () => {
+  test("create a new pool", async ({ poolsPage }) => {
+    const poolName = `test_crud_pool_${uniqueRunId("crud")}`;
+
+    createdPoolNames.push(poolName);
+
     await poolsPage.navigate();
-    await poolsPage.createPool(createDeletePoolName, createDeletePoolSlots, 
"Created pool");
+    await poolsPage.createPool(poolName, 5, "Created pool");
     await poolsPage.navigate();
-    await poolsPage.verifyPoolExists(createDeletePoolName);
+    await poolsPage.verifyPoolExists(poolName);
   });
 
-  test("delete pool", async () => {
+  test("delete pool", async ({ poolsPage }) => {
+    const poolName = `test_del_pool_${uniqueRunId("del")}`;
+
+    // Create via API so we have a pool to delete via UI
+    await poolsPage.page.request.post("/api/v2/pools", {
+      data: { description: "To be deleted", include_deferred: false, name: 
poolName, slots: 5 },
+      headers: { "Content-Type": "application/json" },
+    });
+
     await poolsPage.navigate();
-    await poolsPage.verifyPoolExists(createDeletePoolName);
-    await poolsPage.deletePool(createDeletePoolName);
-    await poolsPage.verifyPoolNotExists(createDeletePoolName);
+    await poolsPage.verifyPoolExists(poolName);
+    await poolsPage.deletePool(poolName);
+    await poolsPage.verifyPoolNotExists(poolName);
   });
 });
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/providers.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/providers.spec.ts
index bee7bd8d12e..c114908f469 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/providers.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/providers.spec.ts
@@ -21,7 +21,6 @@ import { expect, test } from "tests/e2e/fixtures";
 test.describe("Providers Page", () => {
   test.beforeEach(async ({ providersPage }) => {
     await providersPage.navigate();
-    await providersPage.waitForLoad();
   });
 
   test("verify providers page heading", async ({ providersPage }) => {
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/requiredAction.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/requiredAction.spec.ts
index e84e09edd9a..477461003c5 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/requiredAction.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/requiredAction.spec.ts
@@ -16,45 +16,40 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { expect, test } from "@playwright/test";
-import { AUTH_FILE, testConfig } from "playwright.config";
-
-import { RequiredActionsPage } from "../pages/RequiredActionsPage";
+import { testConfig } from "playwright.config";
+import { expect, test } from "tests/e2e/fixtures";
+import { apiDeleteDagRun, setupHITLFlowViaAPI } from 
"tests/e2e/utils/test-helpers";
 
 const hitlDagId = testConfig.testDag.hitlId;
 
+const beforeAllRunIds: Array<string> = [];
+
 test.describe("Verify Required Action page", () => {
   test.describe.configure({ mode: "serial" });
+  test.slow();
 
-  test.beforeAll(async ({ browser }) => {
-    test.setTimeout(400_000);
-
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-
-    const page = await context.newPage();
-    const requiredActionsPage = new RequiredActionsPage(page);
+  test.beforeAll(async ({ authenticatedRequest }) => {
+    test.setTimeout(600_000);
 
-    await requiredActionsPage.runHITLFlowWithApproval(hitlDagId);
-    await requiredActionsPage.runHITLFlowWithRejection(hitlDagId);
-
-    await context.close();
+    beforeAllRunIds.push(await setupHITLFlowViaAPI(authenticatedRequest, 
hitlDagId, true));
+    beforeAllRunIds.push(await setupHITLFlowViaAPI(authenticatedRequest, 
hitlDagId, false));
   });
 
-  test.fixme("Verify the actions list/table is displayed (or empty state if 
none)", async ({ page }) => {
-    const browsePage = new RequiredActionsPage(page);
+  test.afterAll(async ({ authenticatedRequest }) => {
+    for (const runId of beforeAllRunIds) {
+      await apiDeleteDagRun(authenticatedRequest, hitlDagId, runId).catch(() 
=> undefined);
+    }
+  });
 
-    await browsePage.navigateToRequiredActionsPage();
+  test("Verify the actions table displays with expected columns", async ({ 
page, requiredActionsPage }) => {
+    await requiredActionsPage.navigateToRequiredActionsPage();
 
-    await 
expect(browsePage.actionsTable.or(browsePage.emptyStateMessage)).toBeVisible();
+    await expect(requiredActionsPage.actionsTable).toBeVisible();
 
-    if (await browsePage.actionsTable.isVisible()) {
-      await expect(page.locator("th").filter({ hasText: "Dag ID" 
})).toBeVisible();
-      await expect(page.locator("th").filter({ hasText: "Task ID" 
})).toBeVisible();
-      await expect(page.locator("th").filter({ hasText: "Dag Run ID" 
})).toBeVisible();
-      await expect(page.locator("th").filter({ hasText: "Response created at" 
})).toBeVisible();
-      await expect(page.locator("th").filter({ hasText: "Response received at" 
})).toBeVisible();
-    } else {
-      await expect(browsePage.emptyStateMessage).toBeVisible();
-    }
+    await expect(page.locator("th").filter({ hasText: "Dag ID" 
})).toBeVisible();
+    await expect(page.locator("th").filter({ hasText: "Task ID" 
})).toBeVisible();
+    await expect(page.locator("th").filter({ hasText: "Dag Run ID" 
})).toBeVisible();
+    await expect(page.locator("th").filter({ hasText: "Response created at" 
})).toBeVisible();
+    await expect(page.locator("th").filter({ hasText: "Response received at" 
})).toBeVisible();
   });
 });
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/variable.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/variable.spec.ts
index d3498ab477b..37b73fe20d9 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/variable.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/variable.spec.ts
@@ -16,14 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { test, expect } from "@playwright/test";
-import { AUTH_FILE } from "playwright.config";
-
-import { VariablePage } from "../pages/VariablePage";
+import { expect, test } from "tests/e2e/fixtures";
+import { apiCreateVariable, apiDeleteVariable, uniqueRunId } from 
"tests/e2e/utils/test-helpers";
 
 test.describe("Variables Page", () => {
-  let variablesPage: VariablePage;
-  let createVariables = 3;
+  test.describe.configure({ mode: "serial" });
 
   const createdVariables: Array<{
     description: string;
@@ -31,61 +28,39 @@ test.describe("Variables Page", () => {
     value: string;
   }> = [];
 
-  test.beforeAll(async ({ browser }) => {
-    test.setTimeout(420_000); // for slower browsers
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
+  const importVarKeys: Array<string> = [];
 
-    variablesPage = new VariablePage(page);
+  test.beforeAll(async ({ authenticatedRequest }) => {
+    test.slow();
 
-    await variablesPage.navigate();
+    const prefix = uniqueRunId("e2e_var");
 
-    for (let i = 0; i < createVariables; i++) {
+    for (let i = 0; i < 3; i++) {
       const variable = {
         description: `description_${i}`,
-        key: `e2e_var_${Date.now()}_${i}_${Math.random().toString(36).slice(2, 
8)}`,
+        key: `${prefix}_${i}`,
         value: `value_${i}`,
       };
 
       createdVariables.push(variable);
-
-      // Wait for dialog backdrop to fully disappear
-      await expect(page.locator('[data-part="backdrop"]')).toHaveCount(0);
-
-      await variablesPage.addButton.click();
-
-      await expect(page.getByRole("heading", { name: /add/i })).toBeVisible({ 
timeout: 20_000 });
-
-      await page.getByLabel(/key/i).fill(variable.key);
-      await page.getByLabel(/value/i).fill(variable.value);
-
-      if (variable.description) {
-        await page.getByLabel(/description/i).fill(variable.description);
-      }
-
-      await page.getByRole("button", { name: /save/i }).click();
-
-      await expect(variablesPage.rowByKey(variable.key)).toHaveCount(1);
+      await apiCreateVariable(authenticatedRequest, {
+        description: variable.description,
+        key: variable.key,
+        value: variable.value,
+      });
     }
-
-    await context.close();
   });
 
-  test.beforeEach(async ({ page }) => {
-    variablesPage = new VariablePage(page);
-    await variablesPage.navigate();
-    await variablesPage.waitForLoad();
+  test.beforeEach(async ({ variablePage }) => {
+    await variablePage.navigate();
   });
 
-  test("verify variables table displays", async () => {
-    await expect(variablesPage.table).toBeVisible();
-
-    const rowCount = await variablesPage.tableRows.count();
-
-    expect(rowCount).toBeGreaterThan(0);
+  test("verify variables table displays", async ({ variablePage }) => {
+    await expect(variablePage.table).toBeVisible();
+    await expect(variablePage.tableRows).not.toHaveCount(0);
   });
 
-  test("verify search filters", async () => {
+  test("verify search filters", async ({ variablePage }) => {
     const target = createdVariables.at(0);
 
     expect(target).toBeDefined();
@@ -96,12 +71,12 @@ test.describe("Variables Page", () => {
 
     const targetKey = target.key;
 
-    await variablesPage.search(targetKey);
-    await expect(variablesPage.tableRows).toHaveCount(1);
-    await expect(variablesPage.rowByKey(targetKey)).toBeVisible();
+    await variablePage.search(targetKey);
+    await expect(variablePage.tableRows).toHaveCount(1);
+    await expect(variablePage.rowByKey(targetKey)).toBeVisible();
   });
 
-  test("verify editing a variable", async ({ page }) => {
+  test("verify editing a variable", async ({ page, variablePage }) => {
     const target = createdVariables.at(1);
 
     expect(target).toBeDefined();
@@ -110,20 +85,24 @@ test.describe("Variables Page", () => {
       throw new Error("No variable available for edit test");
     }
 
-    await variablesPage.rowByKey(target.key).getByRole("button", { name: 
/edit/i }).click();
+    await variablePage.rowByKey(target.key).getByRole("button", { name: 
/edit/i }).click();
 
     await expect(page.getByRole("heading", { name: /edit/i })).toBeVisible();
 
-    await page.getByLabel(/description/i).fill("updated via e2e");
+    const descriptionInput = page.getByLabel(/description/i);
+
+    await descriptionInput.fill("updated via e2e");
+    await expect(descriptionInput).toHaveValue("updated via e2e");
+
     await page.getByRole("button", { name: /save/i }).click();
 
-    await variablesPage.waitForLoad();
+    await variablePage.waitForLoad();
 
-    await expect(variablesPage.rowByKey(target.key)).toContainText("updated 
via e2e");
+    await expect(variablePage.rowByKey(target.key)).toContainText("updated via 
e2e");
   });
 
-  test("verify deleting the variable", async ({ page }) => {
-    const targetKey = `delete_test_${Date.now()}`;
+  test("verify deleting the variable", async ({ page, variablePage }) => {
+    const targetKey = uniqueRunId("delete_test");
 
     const response = await page.request.post("/api/v2/variables", {
       data: {
@@ -136,14 +115,13 @@ test.describe("Variables Page", () => {
     expect(response.ok()).toBeTruthy();
     expect(response.status()).toBe(201);
 
-    await variablesPage.navigate();
-    await variablesPage.waitForLoad();
+    await variablePage.navigate();
 
-    await variablesPage.search(targetKey);
-    await expect(variablesPage.tableRows).toHaveCount(1, { timeout: 10_000 });
-    await expect(variablesPage.rowByKey(targetKey)).toHaveCount(1, { timeout: 
10_000 });
+    await variablePage.search(targetKey);
+    await expect(variablePage.tableRows).toHaveCount(1, { timeout: 10_000 });
+    await expect(variablePage.rowByKey(targetKey)).toHaveCount(1, { timeout: 
10_000 });
 
-    await variablesPage.selectRow(targetKey);
+    await variablePage.selectRow(targetKey);
     await page.getByRole("button", { name: /^delete$/i }).click();
 
     const dialog = page.getByRole("dialog");
@@ -156,11 +134,14 @@ test.describe("Variables Page", () => {
 
     await dialog.getByRole("button", { name: /yes,\s*delete/i }).click();
 
-    await expect(variablesPage.rowByKey(targetKey)).toHaveCount(0);
+    await expect(variablePage.rowByKey(targetKey)).toHaveCount(0);
   });
 
-  test("verify importing variables using Import Variables button", async ({ 
page }) => {
-    const uniqueId = `${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
+  test("verify importing variables using Import Variables button", async ({ 
page, variablePage }) => {
+    const uniqueId = uniqueRunId("import");
+
+    // Pre-register before UI interactions so afterAll cleans up even on test 
failure.
+    importVarKeys.push(`import_var_${uniqueId}_1`, `import_var_${uniqueId}_2`);
 
     const importPayload = {
       [`import_var_${uniqueId}_1`]: {
@@ -173,7 +154,7 @@ test.describe("Variables Page", () => {
       },
     };
 
-    await variablesPage.page.getByRole("button", { name: /import variables/i 
}).click();
+    await variablePage.page.getByRole("button", { name: /import variables/i 
}).click();
 
     const dialog = page.getByRole("dialog");
 
@@ -189,10 +170,10 @@ test.describe("Variables Page", () => {
 
     await dialog.getByRole("button", { name: /import/i }).click();
 
-    await variablesPage.waitForLoad();
+    await variablePage.waitForLoad();
 
     for (const key of Object.keys(importPayload)) {
-      await expect(variablesPage.rowByKey(key)).toHaveCount(1);
+      await expect(variablePage.rowByKey(key)).toHaveCount(1);
 
       const variable = importPayload[key];
 
@@ -208,59 +189,22 @@ test.describe("Variables Page", () => {
     }
   });
 
-  test.afterAll(async ({ browser }) => {
-    test.setTimeout(300_000);
-    if (createdVariables.length === 0) {
-      return;
-    }
-
-    const context = await browser.newContext({ storageState: AUTH_FILE });
-    const page = await context.newPage();
+  test.afterAll(async ({ authenticatedRequest }) => {
+    test.slow();
 
-    variablesPage = new VariablePage(page);
-
-    await variablesPage.navigate();
-    await variablesPage.waitForLoad();
-
-    const keysToDelete: Array<string> = [];
-
-    for (const variable of createdVariables) {
-      const row = variablesPage.rowByKey(variable.key);
-
-      if ((await row.count()) > 0) {
-        await variablesPage.selectRow(variable.key);
-        keysToDelete.push(variable.key);
-      }
-    }
-
-    if (keysToDelete.length === 0) {
-      await context.close();
+    // Merge all tracked keys: some may not be in createdVariables if a test 
failed early.
+    const allKeys = [...new Set([...createdVariables.map((v) => v.key), 
...importVarKeys])];
 
+    if (allKeys.length === 0) {
       return;
     }
 
-    await page.getByRole("button", { name: /^delete$/i }).click();
-
-    const dialog = page.getByRole("dialog");
-
-    await expect(
-      dialog.getByRole("heading", {
-        name: new RegExp(`delete\\s+${keysToDelete.length}\\s+variable`, "i"),
-      }),
-    ).toBeVisible();
-
-    const codeBlock = dialog.locator("code");
-
-    for (const key of keysToDelete) {
-      await expect(codeBlock).toContainText(key);
-    }
-
-    await dialog.getByRole("button", { name: /yes,\s*delete/i }).click();
-
-    for (const key of keysToDelete) {
-      await expect(variablesPage.rowByKey(key)).toHaveCount(0);
+    for (const key of allKeys) {
+      try {
+        await apiDeleteVariable(authenticatedRequest, key);
+      } catch {
+        // Ignore 404 or other errors - variable may not have been created
+      }
     }
-
-    await context.close();
   });
 });
diff --git a/airflow-core/src/airflow/ui/tests/e2e/utils/test-helpers.ts 
b/airflow-core/src/airflow/ui/tests/e2e/utils/test-helpers.ts
index 82e3d641b2d..178a2f3caa0 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/utils/test-helpers.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/utils/test-helpers.ts
@@ -502,14 +502,8 @@ export async function apiDeleteDagRun(source: RequestLike, 
dagId: string, runId:
  * running tasks during deletion, then delete with one retry on timeout.
  */
 export async function safeCleanupDagRun(source: RequestLike, dagId: string, 
runId: string): Promise<void> {
-  const request = getRequestContext(source);
-
   try {
-    await request.patch(`${baseUrl}/api/v2/dags/${dagId}/dagRuns/${runId}`, {
-      data: { state: "failed" },
-      headers: { "Content-Type": "application/json" },
-      timeout: 10_000,
-    });
+    await apiSetDagRunState(source, { dagId, runId, state: "failed" });
   } catch {
     // Run may already be terminal or deleted — ignore.
   }

Reply via email to