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

Code before:
    await this.page.waitForLoadState("networkidle");
    await this.taskInstancesTable.waitFor({ state: "visible", timeout: 10_000 });

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

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

    const rows = this.taskInstancesTable.locator('tbody tr:not(.no-data), div[role="row"]:not(:first-child)');

    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.taskInstancesTable.waitFor({ state: "visible", timeout: 10_000 });

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

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

  /**
   * Verify state filtering via URL parameters
   */
  public async verifyStateFiltering(expectedState: string): Promise<void> {
    await this.navigateTo(`${TaskInstancesPage.taskInstancesUrl}?task_state=${expectedState.toLowerCase()}`);
    await this.page.waitForURL(/.*task_state=.*/, { timeout: 15_000 });
    await this.page.waitForLoadState("networkidle");

    const dataLink = this.taskInstancesTable.locator("a[href*='/dags/']").first();

    await expect(dataLink).toBeVisible({ timeout: 30_000 });
    await expect(this.taskInstancesTable).toBeVisible();

    const rowsAfterFilter = this.taskInstancesTable.locator(

Review comment (suggestion):
 Silently passes if there is not enough data. I think we should not have this condition. As we are already creating data in beforeAll

Code after:

  /**
   * Click previous page button
   */
  public async clickPrevPage(): Promise<void> {
    const initialTaskInstanceIds = await this.getTaskInstanceIds();

    await this.paginationPrevButton.click();

    await expect
      .poll(() => this.getTaskInstanceIds(), { timeout: 10_000 })
      .not.toEqual(initialTaskInstanceIds);
    await this.waitForTaskInstanceList();
  }

  /**
   * Get all task instance identifiers from the current page
   */
  public async getTaskInstanceIds(): Promise<Array<string>> {
    await this.waitForTaskInstanceList();
    const taskLinks = this.taskInstancesTable.locator("a[href*='/dags/']");
    const texts = await taskLinks.allTextContents();

    return texts.map((text) => text.trim()).filter((text) => text !== "");
  }

  /**
   * Navigate to Task Instances page and wait for data to load
   */
  public async navigate(): Promise<void> {
    await this.navigateTo(TaskInstancesPage.taskInstancesUrl);
    await this.page.waitForURL(/.*task_instances/, { timeout: 15_000 });
    await this.taskInstancesTable.waitFor({ state: "visible", timeout: 10_000 });

    const dataLink = this.taskInstancesTable.locator("a[href*='/dags/']").first();
    const noDataMessage = this.page.locator('text="No Task Instances found"');

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

  /**
   * Verify state filtering via URL parameters
   */
  public async verifyStateFiltering(expectedState: string): Promise<void> {
    await this.navigateTo(`${TaskInstancesPage.taskInstancesUrl}?task_state=${expectedState.toLowerCase()}`);
    await this.page.waitForURL(/.*task_state=.*/, { timeout: 15_000 });
    await this.page.waitForLoadState("networkidle");

    const dataLink = this.taskInstancesTable.locator("a[href*='/dags/']").first();

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