From 0d065027c59c6115538d85b0aa91c59d58b3725c Mon Sep 17 00:00:00 2001 From: haseebmalik18 Date: Tue, 21 Apr 2026 19:25:36 -0400 Subject: [PATCH] Improve Playwright test patterns in xcoms page object #63966 --- .../airflow/ui/tests/e2e/pages/XComsPage.ts | 62 ++++++++----------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/XComsPage.ts b/airflow-core/src/airflow/ui/tests/e2e/pages/XComsPage.ts index d32d787b82ce6..a18b4a2fd7725 100644 --- a/airflow-core/src/airflow/ui/tests/e2e/pages/XComsPage.ts +++ b/airflow-core/src/airflow/ui/tests/e2e/pages/XComsPage.ts @@ -27,25 +27,27 @@ export class XComsPage extends BasePage { public readonly addFilterButton: Locator; public readonly collapseAllButton: Locator; public readonly expandAllButton: Locator; + public readonly tableRows: Locator; public readonly xcomsTable: Locator; public constructor(page: Page) { super(page); - this.addFilterButton = page.locator('[data-testid="add-filter-button"]'); - this.collapseAllButton = page.locator('[data-testid="collapse-all-button"]'); - this.expandAllButton = page.locator('[data-testid="expand-all-button"]'); - this.xcomsTable = page.locator('[data-testid="table-list"]'); + this.addFilterButton = page.getByTestId("add-filter-button"); + this.collapseAllButton = page.getByTestId("collapse-all-button"); + this.expandAllButton = page.getByTestId("expand-all-button"); + this.xcomsTable = page.getByTestId("table-list"); + this.tableRows = this.xcomsTable.locator("tbody tr"); } public async applyFilter(filterName: string, value: string): Promise { await expect(this.addFilterButton).toBeVisible({ timeout: 30_000 }); await this.addFilterButton.click(); - const filterMenu = this.page.locator('[role="menu"]'); + const filterMenu = this.page.getByRole("menu"); await expect(filterMenu).toBeVisible({ timeout: 30_000 }); - const filterOption = filterMenu.locator('[role="menuitem"]').filter({ hasText: filterName }); + const filterOption = filterMenu.getByRole("menuitem", { name: filterName }); await filterOption.click(); @@ -53,13 +55,13 @@ export class XComsPage extends BasePage { .locator("div") .filter({ hasText: `${filterName}:` }) .first(); - const filterInput = filterPill.locator("input"); + const filterInput = filterPill.getByRole("textbox"); await expect(filterInput).toBeVisible({ timeout: 30_000 }); await filterInput.fill(value); await filterInput.press("Enter"); - await expect(this.xcomsTable.locator("tbody tr").first()).toBeVisible({ timeout: 30_000 }); + await expect(this.tableRows.first()).toBeVisible({ timeout: 30_000 }); } public async navigate(): Promise { @@ -75,18 +77,17 @@ export class XComsPage extends BasePage { await this.applyFilter("DAG ID", dagDisplayNamePattern); await expect(async () => { - const firstLink = this.xcomsTable.locator("tbody tr").first().locator("a[href*='/dags/']").first(); + const firstLink = this.tableRows.first().locator("a[href*='/dags/']").first(); await expect(firstLink).toContainText(dagDisplayNamePattern, { ignoreCase: true }); }).toPass({ timeout: 30_000 }); - const rows = this.xcomsTable.locator("tbody tr"); - const rowCount = await rows.count(); + await expect(this.tableRows).not.toHaveCount(0); - expect(rowCount).toBeGreaterThan(0); + const rowCount = await this.tableRows.count(); for (let i = 0; i < Math.min(rowCount, 3); i++) { - const dagIdLink = rows.nth(i).locator("a[href*='/dags/']").first(); + const dagIdLink = this.tableRows.nth(i).locator("a[href*='/dags/']").first(); await expect(dagIdLink).toContainText(dagDisplayNamePattern, { ignoreCase: true }); } @@ -108,18 +109,17 @@ export class XComsPage extends BasePage { await this.applyFilter("Key", keyPattern); await expect(async () => { - const firstKeyCell = this.xcomsTable.locator("tbody tr").first().locator("td").first(); + const firstKeyCell = this.tableRows.first().locator("td").first(); await expect(firstKeyCell).toContainText(keyPattern, { ignoreCase: true }); }).toPass({ timeout: 30_000 }); - const rows = this.xcomsTable.locator("tbody tr"); - const rowCount = await rows.count(); + await expect(this.tableRows).not.toHaveCount(0); - expect(rowCount).toBeGreaterThan(0); + const rowCount = await this.tableRows.count(); for (let i = 0; i < Math.min(rowCount, 3); i++) { - const keyCell = rows.nth(i).locator("td").first(); + const keyCell = this.tableRows.nth(i).locator("td").first(); await expect(keyCell).toContainText(keyPattern, { ignoreCase: true }); } @@ -128,7 +128,7 @@ export class XComsPage extends BasePage { public async verifySortByColumn(columnName: string): Promise { await this.navigate(); - const columnHeader = this.xcomsTable.locator("thead th").filter({ hasText: columnName }); + const columnHeader = this.xcomsTable.getByRole("columnheader", { name: columnName }); await expect(columnHeader).toBeVisible({ timeout: 10_000 }); @@ -152,18 +152,13 @@ export class XComsPage extends BasePage { } public async verifyXComDetailsDisplay(): Promise { - const firstRow = this.xcomsTable.locator("tbody tr").first(); + const firstRow = this.tableRows.first(); await expect(firstRow).toBeVisible({ timeout: 10_000 }); const keyCell = firstRow.locator("td").first(); - await expect(async () => { - await expect(keyCell).toBeVisible(); - const text = await keyCell.textContent(); - - expect(text?.trim()).toBeTruthy(); - }).toPass({ timeout: 10_000 }); + await expect(keyCell).not.toHaveText("", { timeout: 10_000 }); const dagIdLink = firstRow.locator("a[href*='/dags/']").first(); @@ -185,24 +180,19 @@ export class XComsPage extends BasePage { const dataLinks = this.xcomsTable.locator("a[href*='/dags/']"); await expect(dataLinks.first()).toBeVisible({ timeout: 30_000 }); - expect(await dataLinks.count()).toBeGreaterThan(0); + await expect(dataLinks).not.toHaveCount(0); } public async verifyXComValuesDisplayed(): Promise { - const firstRow = this.xcomsTable.locator("tbody tr").first(); + const firstRow = this.tableRows.first(); await expect(firstRow).toBeVisible({ timeout: 10_000 }); const valueCell = firstRow.locator("td").last(); await expect(valueCell).toBeVisible(); - - await expect(async () => { - const textContent = await valueCell.textContent(); - const hasTextContent = (textContent?.trim().length ?? 0) > 0; - const hasWidgetContent = (await valueCell.locator("button, pre, code").count()) > 0; - - expect(hasTextContent || hasWidgetContent).toBeTruthy(); - }).toPass({ timeout: 10_000 }); + await expect( + valueCell.getByRole("button").or(valueCell.locator("pre")).or(valueCell.locator("code")), + ).toBeVisible({ timeout: 10_000 }); } }