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

Code before:
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
import { expect, type Locator, type Page } from "@playwright/test";
import { BasePage } from "tests/e2e/pages/BasePage";

export class TaskInstancesPage extends BasePage {
  public static get taskInstancesUrl(): string {
    return "/task_instances";
  }

  public readonly taskInstancesTable: Locator;

  public constructor(page: Page) {
    super(page);
    this.taskInstancesTable = page.locator('table, div[role="table"]');
  }

  /**
   * Apply a filter by selecting a filter type and value
   */
  public async applyFilter(filterName: string, filterValue: string): Promise<void> {
    const addFilterButton = this.page.locator(
      'button:has-text("Filter"), button:has-text("Add Filter"), button[aria-label*="filter" i]',
    );

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

    const filterMenu = this.page.locator('[role="menu"], [role="menuitem"]');

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

    const filterOption = this.page.locator(`[role="menuitem"]:has-text("${filterName}")`).first();

    await expect(filterOption).toBeVisible({ timeout: 5000 });
    await filterOption.click();

    const filterPill = this.page.locator(`[role="combobox"], button:has-text("${filterName}:")`);

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

    const dropdown = this.page.locator('[role="option"], [role="listbox"]');

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


Review comment (suggestion):
DAG Runs [PR](https://github.com/apache/airflow/issues/59309) uses URL parameters for filtering which is simpler and more reliable.

Code after:
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
import { expect, type Locator, type Page } from "@playwright/test";
import { BasePage } from "tests/e2e/pages/BasePage";

export class TaskInstancesPage extends BasePage {
  public static get taskInstancesUrl(): string {
    return "/task_instances";
  }

  public readonly paginationNextButton: Locator;

  public readonly paginationPrevButton: Locator;

  public readonly taskInstancesTable: Locator;

  public constructor(page: Page) {
    super(page);
    this.paginationNextButton = page.locator('[data-testid="next"]');
    this.paginationPrevButton = page.locator('[data-testid="prev"]');
    this.taskInstancesTable = page.locator('table, div[role="table"]');
  }

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

    await this.paginationNextButton.click();

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

    await this.waitForTaskInstanceList();
  }

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

    await this.paginationPrevButton.click();