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

Code before:
    const rowCount = await rows.count();

    expect(rowCount).toBeGreaterThan(0);

    for (let i = 0; i < Math.min(rowCount, 3); i++) {
      const dagIdLink = rows.nth(i).locator("a[href*='/dags/']").first();
      const dagDisplayName = await dagIdLink.textContent();

      expect(dagDisplayName?.toLowerCase()).toContain(dagDisplayNamePattern.toLowerCase());
    }
  }

  public async verifyExpandCollapse(): Promise<void> {
    await this.navigate();

    await expect(this.expandAllButton.first()).toBeVisible({ timeout: 5000 });
    await this.expandAllButton.first().click();
    await this.page.waitForTimeout(500);

    await expect(this.collapseAllButton.first()).toBeVisible({ timeout: 5000 });
    await this.collapseAllButton.first().click();
    await this.page.waitForTimeout(500);
  }

  public async verifyKeyPatternFiltering(keyPattern: string): Promise<void> {
    await this.navigateTo(`${XComsPage.xcomsUrl}?key_pattern=${encodeURIComponent(keyPattern)}`);
    await this.page.waitForURL(/.*key_pattern=.*/, { timeout: 15_000 });
    await this.page.waitForLoadState("networkidle");

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

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

    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();
      const keyText = await keyCell.textContent();

      expect(keyText?.toLowerCase()).toContain(keyPattern.toLowerCase());
    }
  }

  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");

Review comment (suggestion):
We are using URL parameters directly here. I think we can use UI interaction create to simuate real user experience

Code after:

    await filterInput.waitFor({ state: "visible", timeout: 5000 });
    await filterInput.fill(value);
    await filterInput.press("Enter");
    await this.page.waitForLoadState("networkidle");
  }

  public async navigate(): Promise<void> {
    await this.navigateTo(XComsPage.xcomsUrl);
    await this.page.waitForURL(/.*xcoms/, { timeout: 15_000 });
    await this.xcomsTable.waitFor({ state: "visible", timeout: 10_000 });
    await this.page.waitForLoadState("networkidle");
  }

  public async verifyDagDisplayNameFiltering(dagDisplayNamePattern: string): Promise<void> {
    await this.navigate();
    await this.applyFilter("DAG ID", dagDisplayNamePattern);

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

    expect(rowCount).toBeGreaterThan(0);

    for (let i = 0; i < Math.min(rowCount, 3); i++) {
      const dagIdLink = rows.nth(i).locator("a[href*='/dags/']").first();

      await expect(dagIdLink).toContainText(dagDisplayNamePattern, { ignoreCase: true });
    }
  }

  public async verifyExpandCollapse(): Promise<void> {
    await this.navigate();

    await expect(this.expandAllButton.first()).toBeVisible({ timeout: 5000 });
    await this.expandAllButton.first().click();
    await this.page.waitForLoadState("networkidle");

    await expect(this.collapseAllButton.first()).toBeVisible({ timeout: 5000 });
    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);