Ctrl K
ComunidadGuías…

e2e/issue-filter-advanced.spec.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { expect, test, type Page } from "@playwright/test";
import { createTestApi, loginAsDefault } from "./helpers";
import type { TestApiClient } from "./fixtures";

// Menu search, the due-date dimension and advanced mode, end to end: menu or
// panel → store → table query → SQL. SHOTS_DIR, when set, collects screenshots.

async function visibleIssueTitles(page: Page, titles: string[]) {
  const present: string[] = [];
  for (const title of titles) {
    if (await page.getByText(title, { exact: true }).first().isVisible()) {
      present.push(title);
    }
  }
  return present.sort();
}

async function shot(page: Page, name: string) {
  const dir = process.env.SHOTS_DIR;
  if (!dir) return;
  // Let menus and tooltips finish their enter/exit transitions.
  await page.waitForTimeout(400);
  await page.screenshot({ path: `${dir}/${name}.png` });
}

async function closeMenus(page: Page) {
  await page.keyboard.press("Escape");
  await page.keyboard.press("Escape");
}

function localDate(offsetDays: number) {
  const d = new Date();
  d.setDate(d.getDate() + offsetDays);
  const pad = (n: number) => String(n).padStart(2, "0");
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}

test.describe("Issue filter: search, due date, advanced", () => {
  let api: TestApiClient;
  const suffix = Date.now().toString(36);
  const urgentLate = `adv urgent late ${suffix}`;
  const urgentUndated = `adv urgent undated ${suffix}`;
  const lowLate = `adv low late ${suffix}`;
  const lowUndated = `adv low undated ${suffix}`;
  const mediumLate = `adv medium late ${suffix}`;
  const all = [urgentLate, urgentUndated, lowLate, lowUndated, mediumLate];

  test.beforeEach(async ({ page }) => {
    api = await createTestApi();
    const yesterday = localDate(-1);
    await api.createIssue(urgentLate, { priority: "urgent", due_date: yesterday });
    await api.createIssue(urgentUndated, { priority: "urgent" });
    await api.createIssue(lowLate, { priority: "low", due_date: yesterday });
    await api.createIssue(lowUndated, { priority: "low" });
    await api.createIssue(mediumLate, { priority: "medium", due_date: yesterday });
    await loginAsDefault(page);
  });

  test.afterEach(async () => {
    await api.cleanup();
  });

  test("search adds a value as a condition; due date filters by bucket", async ({ page }) => {
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([...all].sort());

    await page.getByRole("button", { name: "Filter", exact: true }).click();
    await page.getByRole("textbox", { name: "Filter by field or value…" }).fill("urg");
    const urgentRow = page.getByRole("menuitemcheckbox", { name: /Priority\s*is\s*Urgent/ });
    await expect(urgentRow).toBeVisible();
    await shot(page, "01-search");
    await urgentRow.click();
    await closeMenus(page);
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([urgentLate, urgentUndated].sort());

    await page.getByRole("button", { name: "1 filter" }).click();
    await page.getByRole("menuitem", { name: /^Due date/ }).click();
    await page.getByRole("menuitemcheckbox", { name: "Overdue" }).click();
    await shot(page, "02-due-date");
    await closeMenus(page);
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([urgentLate]);
  });

  test("advanced mode ORs inside a group and switches back only when simple can say it", async ({ page }) => {
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([...all].sort());

    // Start from a simple filter: due date is overdue.
    await page.getByRole("button", { name: "Filter", exact: true }).click();
    await page.getByRole("menuitem", { name: /^Due date/ }).click();
    await page.getByRole("menuitemcheckbox", { name: "Overdue" }).click();
    await closeMenus(page);
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([lowLate, mediumLate, urgentLate].sort());

    // Switching carries it over as the first rule.
    await page.getByRole("button", { name: "1 filter" }).click();
    await page.getByRole("menuitem", { name: /Switch to advanced filter/ }).click();
    const panel = page.getByTestId("advanced-filter-panel");
    await expect(panel).toBeVisible();
    await expect(panel.getByTestId("advanced-filter-row")).toHaveCount(1);
    await expect(panel.getByRole("button", { name: "Switch to simple filter" })).toBeEnabled();

    // AND (priority is urgent OR priority is low), as a group.
    await panel.getByRole("button", { name: "Add condition group" }).click();
    await page.getByRole("menuitem", { name: "Priority" }).click();
    const group = panel.getByTestId("advanced-filter-group");
    await expect(group).toBeVisible();
    await group.getByRole("button", { name: "Values" }).first().click();
    await page.getByRole("menuitemcheckbox", { name: "Urgent" }).click();
    await page.keyboard.press("Escape");
    await group.getByRole("button", { name: "Add condition" }).click();
    await page.getByRole("menuitem", { name: "Priority" }).click();
    await group.getByRole("button", { name: "Values" }).nth(1).click();
    await page.getByRole("menuitemcheckbox", { name: "Low" }).click();
    await page.keyboard.press("Escape");

    await expect(panel).toBeVisible();
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([lowLate, urgentLate].sort());
    const toSimple = panel.getByRole("button", { name: "Switch to simple filter" });
    await expect(toSimple).toBeDisabled();
    await shot(page, "03-advanced");
    await toSimple.locator("..").hover();
    const blockedReason = page.getByText(/Simple filters can't show groups/);
    await expect(blockedReason).toBeVisible();
    await panel.getByText("Advanced filter").hover();
    await expect(blockedReason).toBeHidden();

    // The trigger names the mode; the chip bar carries the tree as one chip.
    await page.keyboard.press("Escape");
    await expect(panel).toBeHidden();
    await expect(page.getByRole("button", { name: "Advanced filter" })).toBeVisible();
    await page.mouse.move(600, 400);
    await shot(page, "04-advanced-closed");

    // Delete the group: one overdue rule is a simple filter again.
    await page.getByRole("button", { name: "Advanced filter" }).click();
    await expect(panel).toBeVisible();
    await group.getByRole("button", { name: "Condition actions" }).last().click();
    await page.getByRole("menuitem", { name: "Delete group" }).click();
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([lowLate, mediumLate, urgentLate].sort());
    await panel.getByRole("button", { name: "Switch to simple filter" }).click();
    await expect(panel).toBeHidden();
    await expect(page.getByRole("button", { name: "1 filter" })).toBeVisible();
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([lowLate, mediumLate, urgentLate].sort());
  });

  test("custom properties are conditions like any other field", async ({ page }) => {
    const property = await api.createProperty(`Estimate ${suffix}`, "number");
    await page.reload();
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([...all].sort());

    await page.getByRole("button", { name: "Filter", exact: true }).click();
    await page.getByRole("menuitem", { name: /Switch to advanced filter/ }).click();
    const panel = page.getByTestId("advanced-filter-panel");
    await panel.getByRole("button", { name: "Add condition", exact: true }).click();
    await page.getByRole("menuitem", { name: "Priority" }).click();
    await panel.getByRole("button", { name: "Values" }).first().click();
    await page.getByRole("menuitemcheckbox", { name: "Urgent" }).click();
    await page.keyboard.press("Escape");

    await panel.getByRole("button", { name: "Add condition", exact: true }).click();
    await expect(page.getByRole("menuitem", { name: property.name })).toBeVisible();
    await shot(page, "05-advanced-field-picker");
    await page.getByRole("menuitem", { name: property.name }).click();
    await panel.getByRole("button", { name: "Values" }).nth(1).click();
    await page.getByRole("menuitemcheckbox", { name: "No value" }).click();
    await page.keyboard.press("Escape");

    const rows = panel.getByTestId("advanced-filter-row");
    await expect(rows).toHaveCount(2);
    await expect(rows.nth(1)).toContainText(property.name);
    await expect(panel.getByRole("button", { name: "Add property filter" })).toHaveCount(0);
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([urgentLate, urgentUndated].sort());
    await shot(page, "06-advanced-properties");

    await panel.getByRole("button", { name: "Clear all" }).click();
    await expect(rows).toHaveCount(0);
    await expect(panel.getByRole("button", { name: "Clear all" })).toBeDisabled();
    await expect.poll(() => visibleIssueTitles(page, all)).toEqual([...all].sort());
  });
});