Repository: apache/airflow
File: airflow-core/src/airflow/ui/tests/e2e/pages/XComsPage.ts (TypeScript)

Code before:
    }
  }

  public async verifyPagination(limit: number): Promise<void> {
    await this.navigateTo(`${XComsPage.xcomsUrl}?offset=0&limit=${limit}`);
    await this.page.waitForURL(/.*limit=/, { timeout: 10_000 });
    await this.page.waitForLoadState("networkidle");
    await this.xcomsTable.waitFor({ state: "visible", timeout: 10_000 });

    const dataLinks = this.xcomsTable.locator("a[href*='/dags/']");

    await expect(dataLinks.first()).toBeVisible({ timeout: 30_000 });

    const rows = this.xcomsTable.locator("tbody tr");

    expect(await rows.count()).toBeGreaterThan(0);

    const paginationNav = this.page.locator('nav[aria-label="pagination"], [role="navigation"]');

    await expect(paginationNav.first()).toBeVisible({ timeout: 10_000 });

    const page1Button = this.page.getByRole("button", { name: /page 1|^1$/ });

    await expect(page1Button.first()).toBeVisible({ timeout: 5000 });

    const page2Button = this.page.getByRole("button", { name: /page 2|^2$/ });
    const hasPage2 = await page2Button
      .first()
      .isVisible()
      .catch(() => false);

    if (hasPage2) {
      await page2Button.first().click();
      await this.page.waitForLoadState("networkidle");
      await this.xcomsTable.waitFor({ state: "visible", timeout: 10_000 });

      const dataLinksPage2 = this.xcomsTable.locator("a[href*='/dags/']");
      const noDataMessage = this.page.locator("text=/no.*data|no.*xcom|no.*results/i");

      await expect(dataLinksPage2.first().or(noDataMessage.first())).toBeVisible({ timeout: 30_000 });
    }
  }

  public async verifyXComDetailsDisplay(): Promise<void> {
    const firstRow = this.xcomsTable.locator("tbody tr").first();

    await expect(firstRow).toBeVisible({ timeout: 10_000 });

    const dagIdLink = firstRow.locator("a[href*='/dags/']").first();

    await expect(dagIdLink).toBeVisible();

Review comment (suggestion):
Consider using `[data-testid="next"]` / `[data-testid="prev"]` buttons if available, as they're more stable selectors than page number buttons.

Code after:
    await this.collapseAllButton.first().click();
    await this.page.waitForLoadState("networkidle");
  }

  public async verifyKeyPatternFiltering(keyPattern: string): Promise<void> {
    await this.navigate();
    await this.applyFilter("Key", keyPattern);

    await expect(async () => {
      const firstKeyCell = this.xcomsTable.locator("tbody tr").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();

    expect(rowCount).toBeGreaterThan(0);

    for (let i = 0; i < Math.min(rowCount, 3); i++) {
      const keyCell = rows.nth(i).locator("td").first();

      await expect(keyCell).toContainText(keyPattern, { ignoreCase: true });
    }
  }

  public async verifyPagination(limit: number): Promise<void> {
    await this.navigateTo(`${XComsPage.xcomsUrl}?offset=0&limit=${limit}`);
    await this.page.waitForURL(/.*offset=0.*limit=/, { timeout: 10_000 });
    await this.page.waitForLoadState("networkidle");
    await this.xcomsTable.waitFor({ state: "visible", timeout: 10_000 });

    const rows = this.xcomsTable.locator("tbody tr");

    expect(await rows.count()).toBeGreaterThan(0);

    await expect(this.paginationNextButton).toBeVisible({ timeout: 10_000 });
    await this.paginationNextButton.click();
    await this.page.waitForLoadState("networkidle");
    await this.xcomsTable.waitFor({ state: "visible", timeout: 10_000 });

    const urlPage2 = this.page.url();

    expect(urlPage2).toContain(`limit=${limit}`);
    expect(urlPage2).not.toContain("offset=0&");

    const rows2 = this.xcomsTable.locator("tbody tr");

    expect(await rows2.count()).toBeGreaterThan(0);

    await expect(this.paginationPrevButton).toBeVisible({ timeout: 5000 });