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 3151443a167 Improve Playwright test patterns in backfill.spec.ts and
BackfillPage.ts (#63112)
3151443a167 is described below
commit 3151443a1672b9b2876c83647fe00cd058caee41
Author: Stats <[email protected]>
AuthorDate: Mon Mar 9 11:44:28 2026 +0900
Improve Playwright test patterns in backfill.spec.ts and BackfillPage.ts
(#63112)
---
.../src/airflow/ui/tests/e2e/pages/BackfillPage.ts | 55 ++++++++++++++--------
.../airflow/ui/tests/e2e/specs/backfill.spec.ts | 17 +++----
2 files changed, 42 insertions(+), 30 deletions(-)
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 288c3d40b58..56fdbd9f90d 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/BackfillPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/BackfillPage.ts
@@ -94,7 +94,7 @@ export class BackfillPage extends BasePage {
super(page);
this.triggerButton = page.getByTestId("trigger-dag-button");
// Chakra UI radio cards: target the label directly since <input> is
hidden.
- this.backfillModeRadio = page.locator('label:has-text("Backfill")');
+ this.backfillModeRadio = page.locator("label").getByText("Backfill", {
exact: true });
this.backfillFromDateInput = page.getByTestId("datetime-input").first();
this.backfillToDateInput = page.getByTestId("datetime-input").nth(1);
this.backfillRunButton = page.getByRole("button", { name: "Run Backfill"
});
@@ -374,10 +374,6 @@ export class BackfillPage extends BasePage {
return this.page.getByRole("button", { name: /filter table columns/i });
}
- public async getTableColumnCount(): Promise<number> {
- return this.backfillsTable.locator("thead th").count();
- }
-
public async navigateToBackfillsTab(dagName: string): Promise<void> {
await this.navigateTo(BackfillPage.getBackfillsUrl(dagName));
await expect(this.backfillsTable).toBeVisible({ timeout: 15_000 });
@@ -400,31 +396,50 @@ export class BackfillPage extends BasePage {
}
public async pauseBackfillViaApi(backfillId: number): Promise<boolean> {
- // Retry: the server may not have fully initialized the backfill yet.
- for (let attempt = 0; attempt < 5; attempt++) {
- const response = await
this.page.request.put(`${baseUrl}/api/v2/backfills/${backfillId}/pause`, {
- timeout: 30_000,
- });
+ let isPaused = false;
+
+ try {
+ // Retry: the server may not have fully initialized the backfill yet.
+ await expect
+ .poll(
+ async () => {
+ const response = await
this.page.request.put(`${baseUrl}/api/v2/backfills/${backfillId}/pause`, {
+ timeout: 30_000,
+ });
- if (response.ok()) {
- return true;
- }
+ if (response.ok()) {
+ isPaused = true;
- // 409 means the backfill already completed — not retriable.
- if (response.status() === 409) {
- return false;
- }
+ return true;
+ }
+
+ // 409 means the backfill already completed — not retriable.
+ if (response.status() === 409) {
+ isPaused = false;
- await this.page.waitForTimeout(2000);
+ return true;
+ }
+
+ return false;
+ },
+ {
+ intervals: [2000],
+ message: `Failed to pause backfill ${backfillId}`,
+ timeout: 10_000,
+ },
+ )
+ .toBeTruthy();
+ } catch {
+ return false;
}
- return false;
+ return isPaused;
}
public async selectReprocessBehavior(behavior: ReprocessBehaviorApi):
Promise<void> {
const label = REPROCESS_API_TO_UI[behavior];
- await this.page.locator(`label:has-text("${label}")`).first().click({
timeout: 5000 });
+ await this.page.locator("label").getByText(label, { exact: true }).click({
timeout: 5000 });
}
public async toggleColumn(columnName: string): Promise<void> {
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 8babe2f9513..b97d31d8b46 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
@@ -109,17 +109,18 @@ test.describe("Backfill", () => {
await backfillPage.navigateToBackfillsTab(testDagId);
- const initialColumnCount = await backfillPage.getTableColumnCount();
+ const tableHeaders = backfillPage.backfillsTable.locator("thead th");
+
+ await expect(tableHeaders).toHaveCount(7); // Initial state should have
7 columns
+ const initialColumnCount = await tableHeaders.count();
- expect(initialColumnCount).toBeGreaterThan(0);
await expect(backfillPage.getFilterButton()).toBeVisible();
await backfillPage.openFilterMenu();
const filterMenuItems = page.getByRole("menuitem");
- const filterMenuCount = await filterMenuItems.count();
- expect(filterMenuCount).toBeGreaterThan(0);
+ await expect(filterMenuItems).not.toHaveCount(0);
const firstMenuItem = filterMenuItems.first();
const columnToToggle = (await firstMenuItem.textContent())?.trim() ?? "";
@@ -131,9 +132,7 @@ test.describe("Backfill", () => {
await
expect(backfillPage.getColumnHeader(columnToToggle)).not.toBeVisible();
- const newColumnCount = await backfillPage.getTableColumnCount();
-
- expect(newColumnCount).toBeLessThan(initialColumnCount);
+ await expect(tableHeaders).toHaveCount(initialColumnCount - 1);
await backfillPage.openFilterMenu();
await backfillPage.toggleColumn(columnToToggle);
@@ -141,9 +140,7 @@ test.describe("Backfill", () => {
await expect(backfillPage.getColumnHeader(columnToToggle)).toBeVisible();
- const finalColumnCount = await backfillPage.getTableColumnCount();
-
- expect(finalColumnCount).toBe(initialColumnCount);
+ await expect(tableHeaders).toHaveCount(initialColumnCount);
});
});