diff --git a/.github/instructions/frontend-test.instructions.md b/.github/instructions/frontend-test.instructions.md index c74b7cea95..5add9ec224 100644 --- a/.github/instructions/frontend-test.instructions.md +++ b/.github/instructions/frontend-test.instructions.md @@ -445,6 +445,8 @@ Files excluded from coverage: `main.tsx`, `vite-env.d.ts`, `services/api.ts` (th - E2E tests live in `frontend/e2e/`. - They test full user flows against a running backend. +- Check user-visible failures: missing or clipped content, unreachable controls, broken actions, scrolling, and focus. +- Do not lock tests to exact coordinates, animation frames, or cosmetic sizes. Keep size checks for explicit usability requirements, such as minimum touch targets. - Do NOT mock APIs in E2E tests — they exercise the real stack. - Run with `npm run test:e2e` (headless) or `npm run test:e2e:headed`. diff --git a/frontend/e2e/accessibility.spec.ts b/frontend/e2e/accessibility.spec.ts index 667c73be33..5b4a667ff3 100644 --- a/frontend/e2e/accessibility.spec.ts +++ b/frontend/e2e/accessibility.spec.ts @@ -401,29 +401,3 @@ test.describe("Accessibility", () => { await expectTourContainedAndActionable(page, "desktop"); }); }); - -test.describe("Visual Consistency", () => { - test("should render without layout shifts", async ({ page }) => { - await page.goto("/"); - - // Wait for initial render then navigate to chat to measure the chat ribbon - await expect(page.getByTitle("Technique.execute")).toBeVisible(); - await page.getByTitle("Technique.execute").click(); - const anchor = page.getByTestId("new-attack-btn"); - await expect(anchor).toBeVisible(); - - // Take measurements - const initialBox = await anchor.boundingBox(); - - // Wait a moment for any delayed renders - await page.waitForTimeout(500); - - // Verify position hasn't changed - const finalBox = await anchor.boundingBox(); - - if (initialBox && finalBox) { - expect(finalBox.x).toBe(initialBox.x); - expect(finalBox.y).toBe(initialBox.y); - } - }); -}); diff --git a/frontend/e2e/chat.spec.ts b/frontend/e2e/chat.spec.ts index ebdb50212b..2229d97e71 100644 --- a/frontend/e2e/chat.spec.ts +++ b/frontend/e2e/chat.spec.ts @@ -226,7 +226,7 @@ test.describe("Chat Functionality", () => { await expect(badge).toContainText(/gpt-4o-mock/); }); - test("should overlay conversations without shrinking mobile chat and restore focus", async ({ page }) => { + test("should open mobile conversations and restore focus on close", async ({ page }) => { await page.route(/\/api\/attacks\/[^/]+\/conversations/, async (route) => { await route.fulfill({ status: 200, @@ -252,30 +252,14 @@ test.describe("Chat Functionality", () => { await expect(getMessageByText(page, "Start a mobile conversation")).toBeVisible(); await page.setViewportSize({ width: 390, height: 844 }); - const chatArea = page.getByTestId("chat-area"); const toggleButton = page.getByRole("button", { name: "Toggle conversations panel" }); await expect(toggleButton).toBeEnabled(); - const beforeOpen = await chatArea.boundingBox(); await toggleButton.click(); const drawer = page.getByRole("dialog", { name: "Attack Conversations" }); await expect(drawer).toBeVisible(); - const afterOpen = await chatArea.boundingBox(); - const viewport = page.viewportSize(); - if (!beforeOpen || !afterOpen || !viewport) { - throw new Error("Expected chat and drawer layout bounds"); - } - - expect(Math.abs(afterOpen.width - beforeOpen.width)).toBeLessThanOrEqual(1); - await expect.poll(async () => { - const drawerBounds = await drawer.boundingBox(); - return drawerBounds ? drawerBounds.x : -1; - }).toBeGreaterThanOrEqual(0); - await expect.poll(async () => { - const drawerBounds = await drawer.boundingBox(); - return drawerBounds ? drawerBounds.x + drawerBounds.width : Number.POSITIVE_INFINITY; - }).toBeLessThanOrEqual(viewport.width); + await expect(drawer).toBeInViewport({ ratio: 1 }); await page.keyboard.press("Escape"); await expect(drawer).not.toBeVisible(); diff --git a/frontend/e2e/config.spec.ts b/frontend/e2e/config.spec.ts index d9519ef5e5..0096e2c49d 100644 --- a/frontend/e2e/config.spec.ts +++ b/frontend/e2e/config.spec.ts @@ -560,13 +560,7 @@ test.describe("Responsive Target Registry", () => { await page.getByRole("button", { name: /new target/i }).click(); const dialog = page.getByRole("dialog"); - const dialogBox = await dialog.boundingBox(); - if (!dialogBox) { - throw new Error("Expected the Create Target dialog to be visible"); - } - if (viewport.name === "desktop") { - expect(dialogBox.width).toBeLessThanOrEqual(640); - } + await expect(dialog).toBeVisible(); await selectTargetType(page, dialog, "RoundRobinTarget"); const addTargetSelect = dialog.locator("select"); @@ -610,7 +604,7 @@ test.describe("Responsive Target Registry", () => { ); }); - test(`should fit and align filter selections at ${viewport.name} width`, async ({ + test(`should fit and reset filter selections at ${viewport.name} width`, async ({ page, }) => { await page.setViewportSize({ @@ -654,25 +648,11 @@ test.describe("Responsive Target Registry", () => { expect(widths.scrollWidth).toBeLessThanOrEqual(widths.clientWidth); } - // Wrapped dropdowns start under the first one, and the reset button centers on the first row. - const filterBoxes = await page - .getByTestId("target-filters") - .getByRole("combobox") - .evaluateAll((filters) => - filters.map((filter) => { - const box = filter.parentElement?.getBoundingClientRect(); - return { left: box?.left ?? 0, centerY: box ? box.top + box.height / 2 : 0 }; - }) - ); - expect(filterBoxes).toHaveLength(4); - for (const { left } of filterBoxes) { - expect(left).toBeGreaterThanOrEqual(filterBoxes[0].left); - } - const resetBox = await page.getByTestId("target-reset-filters-btn").boundingBox(); - if (!resetBox) { - throw new Error("Expected a visible reset button"); + await page.getByRole("button", { name: "Reset all filters", exact: true }).click(); + for (const { filterName, summary } of selections) { + await expect(page.getByRole("combobox", { name: filterName, exact: true })).not.toHaveValue(summary); } - expect(Math.abs(resetBox.y + resetBox.height / 2 - filterBoxes[0].centerY)).toBeLessThanOrEqual(1); + await expect(page.getByTestId("target-row-filter-chat")).toBeVisible(); const config = page.getByTestId("target-config"); const configWidth = await config.evaluate((element) => ({ diff --git a/frontend/e2e/datasets.spec.ts b/frontend/e2e/datasets.spec.ts index 25b9c2d744..8ce147de91 100644 --- a/frontend/e2e/datasets.spec.ts +++ b/frontend/e2e/datasets.spec.ts @@ -91,9 +91,7 @@ async function expectLongLabelStaysInsideCard(page: Page): Promise { range.selectNodeContents(textHost); const text = range.getBoundingClientRect(); const cardBox = article.getBoundingClientRect(); - const badgeBox = textHost.getBoundingClientRect(); return { - cardWidth: cardBox.width, cardRight: cardBox.right, cardLeft: cardBox.left, cardTop: cardBox.top, @@ -102,18 +100,15 @@ async function expectLongLabelStaysInsideCard(page: Page): Promise { textRight: text.right, textTop: text.top, textBottom: text.bottom, - badgeHeight: badgeBox.height, scrollWidth: textHost.scrollWidth, clientWidth: textHost.clientWidth, }; }, LONG_HARM_CATEGORY); - expect(metrics.cardWidth).toBeLessThanOrEqual(360); expect(metrics.textLeft).toBeGreaterThanOrEqual(metrics.cardLeft - 1); expect(metrics.textRight).toBeLessThanOrEqual(metrics.cardRight + 1); expect(metrics.textTop).toBeGreaterThanOrEqual(metrics.cardTop - 1); expect(metrics.textBottom).toBeLessThanOrEqual(metrics.cardBottom + 1); - expect(metrics.badgeHeight).toBeGreaterThan(20); expect(metrics.scrollWidth).toBeLessThanOrEqual(metrics.clientWidth + 1); } @@ -131,11 +126,10 @@ test.describe("dataset catalog layout", () => { await expect(cards).toHaveCount(2); const [first, second] = await cards.evaluateAll((articles: Element[]) => articles.map((article: Element) => { - const { left, right, top } = article.getBoundingClientRect(); - return { left, right, top }; + const { left, right } = article.getBoundingClientRect(); + return { left, right }; }), ); - expect(first.top).toBe(second.top); expect(second.left).toBeGreaterThan(first.right); }); @@ -454,7 +448,6 @@ test.describe("dataset example table", () => { range.selectNodeContents(textHost); const text = range.getBoundingClientRect(); const cellBox = host.getBoundingClientRect(); - const badgeBox = textHost.getBoundingClientRect(); return { textLeft: text.left, textRight: text.right, @@ -464,7 +457,6 @@ test.describe("dataset example table", () => { cellRight: cellBox.right, cellTop: cellBox.top, cellBottom: cellBox.bottom, - badgeHeight: badgeBox.height, scrollWidth: textHost.scrollWidth, clientWidth: textHost.clientWidth, }; @@ -474,14 +466,11 @@ test.describe("dataset example table", () => { expect(metrics.textRight).toBeLessThanOrEqual(metrics.cellRight + 1); expect(metrics.textTop).toBeGreaterThanOrEqual(metrics.cellTop - 1); expect(metrics.textBottom).toBeLessThanOrEqual(metrics.cellBottom + 1); - expect(metrics.badgeHeight).toBeGreaterThan(20); expect(metrics.scrollWidth).toBeLessThanOrEqual(metrics.clientWidth + 1); const tableLayout = await table.evaluate((host) => ({ - columnWidth: host.querySelector("th")?.getBoundingClientRect().width ?? 0, wrapperWidth: host.parentElement?.clientWidth ?? 0, scrollWidth: host.parentElement?.scrollWidth ?? 0, })); - expect(tableLayout.columnWidth).toBeGreaterThanOrEqual(140); expect(tableLayout.scrollWidth).toBeGreaterThan(tableLayout.wrapperWidth); await table.getByRole("button", { name: "Show alpha" }).click(); await expect(page.getByRole("region", { name: "Example detail" })).toBeVisible(); @@ -501,18 +490,13 @@ test.describe("dataset example table", () => { const termBox = term.getBoundingClientRect(); const valueBox = value.getBoundingClientRect(); return { - termLeft: termBox.left, termBottom: termBox.bottom, - valueLeft: valueBox.left, valueTop: valueBox.top, - valueWidth: valueBox.width, clientWidth: value.clientWidth, scrollWidth: value.scrollWidth, }; }); expect(dimensions.valueTop).toBeGreaterThanOrEqual(dimensions.termBottom); - expect(dimensions.valueLeft).toBeCloseTo(dimensions.termLeft, 0); - expect(dimensions.valueWidth).toBeGreaterThanOrEqual(250); expect(dimensions.scrollWidth).toBeLessThanOrEqual(dimensions.clientWidth + 1); } expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(390); diff --git a/frontend/e2e/labels-operation-picker.spec.ts b/frontend/e2e/labels-operation-picker.spec.ts index 15621580b7..bc21922b92 100644 --- a/frontend/e2e/labels-operation-picker.spec.ts +++ b/frontend/e2e/labels-operation-picker.spec.ts @@ -10,7 +10,6 @@ import { READY_RUNTIME, waitForAppReady } from "./_runtime"; // These tests measure the rendered box in a real browser. // --------------------------------------------------------------------------- -const LIST_MAX_HEIGHT = 240; const LONG_OPERATION = "op_2026_08_a_very_long_operation_name_that_would_be_clipped"; function operations(count: number): string[] { @@ -194,29 +193,6 @@ test.describe("operation picker placement", () => { await expect(popover.getByRole("button", { name: "Add", exact: true })).toBeVisible(); }); - test("caps the list height and anchors it to the input", async ({ page }) => { - await page.setViewportSize({ width: 1280, height: 800 }); - await setupMocks(page, operations(60)); - const listbox = await openOperationPicker(page); - - const box = (await listbox.boundingBox())!; - const input = (await page - .getByTestId("edit-label-operation") - .boundingBox())!; - - expect(box.height).toBeLessThanOrEqual(LIST_MAX_HEIGHT); - // Opens below the input and stays attached to it. - expect(box.y).toBeGreaterThanOrEqual(input.y + input.height); - expect(box.y - (input.y + input.height)).toBeLessThan(16); - - // The options that do not fit are reachable by scrolling, not lost. - const scroll = await listbox.evaluate((el) => ({ - scrollHeight: el.scrollHeight, - clientHeight: el.clientHeight, - })); - expect(scroll.scrollHeight).toBeGreaterThan(scroll.clientHeight); - }); - test("keeps the list on screen when it opens above the input", async ({ page, }) => { @@ -239,11 +215,8 @@ test.describe("operation picker placement", () => { .boundingBox())!; const viewport = page.viewportSize()!; - expect(input.y).toBeGreaterThanOrEqual(LIST_MAX_HEIGHT); - expect(viewport.height - (input.y + input.height)).toBeLessThan(LIST_MAX_HEIGHT); expect(box.y).toBeLessThan(input.y); expect(box.y + box.height).toBeLessThanOrEqual(input.y); - expect(input.y - (box.y + box.height)).toBeLessThan(16); expect(box.y).toBeGreaterThanOrEqual(0); expect(box.y + box.height).toBeLessThanOrEqual(viewport.height); }); @@ -290,17 +263,15 @@ test.describe("operation picker placement", () => { expect(overflow.scrollWidth).toBeLessThanOrEqual(overflow.clientWidth); }); - test("shrinks below the cap when the window is too short for it", async ({ + test("keeps the list on screen in a short window", async ({ page, }) => { - // Shorter than the 240px cap, so a flat cap would hang off the screen. const viewportHeight = 200; await page.setViewportSize({ width: 1280, height: viewportHeight }); await setupMocks(page, operations(60)); const listbox = await openOperationPicker(page); const box = (await listbox.boundingBox())!; - expect(box.height).toBeLessThan(LIST_MAX_HEIGHT); expect(box.y).toBeGreaterThanOrEqual(0); expect(box.y + box.height).toBeLessThanOrEqual(viewportHeight); }); @@ -593,7 +564,7 @@ test.describe("finishing an edit another way", () => { await expect(page.getByTestId("edit-label-operator")).toHaveValue("alice"); }); - test("keeps compact metadata inputs visible and creation first on mobile", async ({ + test("keeps metadata inputs visible and creation first on mobile", async ({ page, }) => { await page.setViewportSize({ width: 360, height: 800 }); @@ -604,8 +575,6 @@ test.describe("finishing an edit another way", () => { const operation = page.getByRole("combobox", { name: "Operation" }); await expect(operator).toBeVisible(); await expect(operation).toBeVisible(); - expect(await operator.evaluate(input => input.parentElement?.getBoundingClientRect().width)).toBeLessThanOrEqual(100); - expect(await operation.evaluate(input => input.parentElement?.getBoundingClientRect().width)).toBeLessThanOrEqual(140); await operation.click(); await expect(page.getByRole("option").first()).toHaveText("New operation…"); await operation.fill("missing"); diff --git a/frontend/e2e/registry.spec.ts b/frontend/e2e/registry.spec.ts index 7ee3085052..46ec84786c 100644 --- a/frontend/e2e/registry.spec.ts +++ b/frontend/e2e/registry.spec.ts @@ -499,16 +499,6 @@ test.describe("Converter Registry", () => { await expect(dialog.getByLabel("Registry name")).toHaveValue(""); }); - test("uses the available viewport height for the converter type list", async ({ page }) => { - await page.getByRole("button", { name: "New Converter" }).click(); - await page.getByRole("combobox", { name: "Converter type" }).click(); - - const listbox = page.getByRole("listbox"); - await expect(listbox).toBeVisible(); - const bounds = await listbox.boundingBox(); - expect(bounds?.height).toBeGreaterThan(300); - }); - for (const { name, viewport, deviceScaleFactor } of [ { name: "wide short screen", viewport: { width: 1920, height: 600 }, deviceScaleFactor: 1 }, { name: "scaled laptop", viewport: { width: 1280, height: 640 }, deviceScaleFactor: 1.5 }, @@ -517,56 +507,17 @@ test.describe("Converter Registry", () => { test.describe(name, () => { test.use({ viewport, deviceScaleFactor }); - test("keeps a long converter list vertical, stable, and scrollable", async ({ page }) => { + test("reaches and selects the last converter in a long list", async ({ page }) => { await page.getByRole("button", { name: "New Converter" }).click(); const trigger = page.getByRole("combobox", { name: "Converter type" }); await trigger.click(); const listbox = page.getByRole("listbox"); await expect(listbox).toBeVisible(); - const anchor = await trigger.evaluate((button) => { - const { x, y, width, height } = button.parentElement!.getBoundingClientRect(); - return { x, y, width, height }; - }); - await expect.poll(async () => { - const box = (await listbox.boundingBox())!; - return Math.abs(box.x - anchor.x); - }).toBeLessThanOrEqual(1); - - // Sample successive frames: a single bounding box can miss position oscillation. - const samples = await listbox.evaluate(async (element) => { - const bounds = []; - for (let frame = 0; frame < 60; frame++) { - await new Promise((resolve) => requestAnimationFrame(() => resolve())); - const { x, y, width, height } = element.getBoundingClientRect(); - bounds.push({ x, y, width, height }); - } - return bounds; - }); - - for (const box of samples) { - expect(Math.abs(box.x - anchor.x)).toBeLessThanOrEqual(1); - expect(Math.abs(box.width - anchor.width)).toBeLessThanOrEqual(1); - expect(box.height).toBeGreaterThan(0); - expect(box.x).toBeGreaterThanOrEqual(-1); - expect(box.y).toBeGreaterThanOrEqual(-1); - expect(box.x + box.width).toBeLessThanOrEqual(viewport.width + 1); - expect(box.y + box.height).toBeLessThanOrEqual(viewport.height + 1); - expect( - box.y >= anchor.y + anchor.height - 1 - || box.y + box.height <= anchor.y + 1, - ).toBe(true); - expect(Math.abs(box.y - samples[0].y)).toBeLessThanOrEqual(1); - expect(Math.abs(box.height - samples[0].height)).toBeLessThanOrEqual(1); - } - - const scroll = await listbox.evaluate((element) => ({ - height: element.clientHeight, - contentHeight: element.scrollHeight, - })); - expect(scroll.contentHeight).toBeGreaterThan(scroll.height); + await expect(listbox).toBeInViewport({ ratio: 1 }); const lastOption = listbox.getByRole("option").last(); - await lastOption.scrollIntoViewIfNeeded(); + await expect(lastOption).not.toBeInViewport(); + await trigger.press("End"); await expect(lastOption).toBeInViewport(); await lastOption.click(); await expect(listbox).toBeHidden(); diff --git a/frontend/e2e/touch-targets.spec.ts b/frontend/e2e/touch-targets.spec.ts index dc6eed9e55..d473600f2d 100644 --- a/frontend/e2e/touch-targets.spec.ts +++ b/frontend/e2e/touch-targets.spec.ts @@ -8,7 +8,6 @@ import { TOUR_STEPS } from "../src/components/Tour/tourSteps"; const TOUR_STEP_COUNT = TOUR_STEPS.length; const MOBILE_VIEWPORT = { width: 390, height: 844 }; -const DESKTOP_VIEWPORT = { width: 1280, height: 800 }; const MINIMUM_TOUCH_TARGET_SIZE = 44; const LONG_SCORE_VALUE = "a".repeat(200); @@ -463,23 +462,6 @@ async function expectMinimumTouchTargets(locator: Locator): Promise { } } -async function expectCompactDesktopTarget(locator: Locator): Promise { - await expect(locator).toBeVisible(); - const box = await locator.boundingBox(); - if (!box) { - throw new Error("Expected a visible desktop target"); - } - expect(box.height).toBeLessThan(MINIMUM_TOUCH_TARGET_SIZE); -} - -async function expectCompactDesktopTargets(locator: Locator): Promise { - const count = await locator.count(); - expect(count).toBeGreaterThan(0); - for (let index = 0; index < count; index += 1) { - await expectCompactDesktopTarget(locator.nth(index)); - } -} - async function expectNoDocumentOverflow(page: Page): Promise { const dimensions = await page.evaluate(() => ({ viewportWidth: document.documentElement.clientWidth, @@ -492,20 +474,6 @@ async function expectNoDocumentOverflow(page: Page): Promise { expect(dimensions.bodyWidth).toBeLessThanOrEqual(dimensions.viewportWidth); } -async function startChatWithMessages(page: Page): Promise { - await page.getByRole("button", { name: "Registry", exact: true }).click(); - await expect(page.getByText("gpt-4o-mobile", { exact: true })).toBeVisible(); - await page.getByRole("combobox", { name: "Default objective target", exact: true }).selectOption({ index: 1 }); - await page.getByRole("button", { name: "Technique.execute", exact: true }).click(); - await page.getByTestId("chat-input").fill( - "Assess this deterministic mobile prompt" - ); - await page.getByTestId("send-message-btn").click(); - await expect( - page.getByText("Deterministic assistant response for touch-target tests.") - ).toBeVisible(); -} - test.beforeEach(async ({ page }) => { await installTouchTargetMocks(page); }); @@ -866,100 +834,3 @@ test.describe("Mobile touch targets", () => { ); }); }); - -test("preserves compact desktop controls and existing sidebar dimensions", async ({ - page, -}) => { - await page.setViewportSize(DESKTOP_VIEWPORT); - await page.goto("/"); - await expect(page.getByTestId("home-view")).toBeVisible(); - - const sidebarHome = page.getByRole("button", { - name: "Home", - exact: true, - }); - const sidebarBox = await sidebarHome.boundingBox(); - expect(sidebarBox?.width).toBe(44); - expect(sidebarBox?.height).toBe(44); - - await expectCompactDesktopTarget(page.getByTestId("labels-icon-btn")); - await expectCompactDesktopTarget( - page.getByTestId("home-configure-target-btn") - ); - await expectCompactDesktopTarget( - page.getByTestId("home-open-attack-home-attack-001") - ); - - await page - .getByRole("button", { name: "Registry", exact: true }) - .click(); - await expect(page.getByText("gpt-4o-mobile", { exact: true })).toBeVisible(); - await expectCompactDesktopTarget( - page.getByRole("button", { name: "Refresh", exact: true }) - ); - await expectCompactDesktopTarget(page.getByRole("combobox", { name: "Filter by type:", exact: true })); - await expectCompactDesktopTarget(page.getByRole("combobox", { name: "Filter by input:", exact: true })); - await expectCompactDesktopTarget(page.getByRole("combobox", { name: "Filter by output:", exact: true })); - await expectCompactDesktopTarget(page.getByRole("combobox", { name: "Filter by capability:", exact: true })); - await page.getByRole("combobox", { name: "Filter by capability:", exact: true }).click(); - await expectCompactDesktopTargets(page.getByRole("menuitemcheckbox")); - await page.keyboard.press("Escape"); - await expect(page.getByRole("menuitemcheckbox")).toHaveCount(0); - await expectCompactDesktopTarget(page.getByRole("button", { name: "Reset all filters", exact: true })); - await expectCompactDesktopTarget( - page.getByRole("combobox", { name: "Default objective target", exact: true }) - ); - await expectCompactDesktopTarget( - page.getByRole("combobox", { name: "Default adversarial target", exact: true }) - ); - await expectCompactDesktopTarget( - page.getByRole("button", { name: "Expand inner targets" }) - ); - - await page.goto("/config"); - await page.getByRole("tab", { name: "Initializers", exact: true }).click(); - await expectCompactDesktopTarget( - page.getByRole("button", { name: "Browse available initializers" }) - ); - - await startChatWithMessages(page); - await expectCompactDesktopTarget(page.getByTestId("toggle-panel-btn")); - await expectCompactDesktopTarget( - page.getByRole("button", { name: "Attach files" }) - ); - await expectCompactDesktopTarget(page.getByTestId("chat-input")); - await expectCompactDesktopTarget(page.getByTestId("copy-to-input-btn-1")); - - await page.getByTestId("toggle-panel-btn").click(); - await expect(page.getByTestId("conversation-panel")).toBeVisible(); - await expectCompactDesktopTarget(page.getByTestId("close-panel-btn")); - await expectCompactDesktopTarget( - page.getByTestId("star-btn-mobile-conversation-001") - ); - await page.getByTestId("close-panel-btn").click(); - - await page.getByRole("button", { name: "Home", exact: true }).click(); - await page.getByTestId("start-tour").click(); - await expect(page.getByText(`1 of ${TOUR_STEP_COUNT}`)).toBeVisible(); - await expectCompactDesktopTarget( - page.getByRole("button", { name: "Close", exact: true }) - ); - await expectCompactDesktopTarget( - page.getByRole("button", { name: "Next", exact: true }) - ); - - await page.goto("/history"); - await expect(page.getByTestId("refresh-btn")).toBeVisible(); - await expectCompactDesktopTarget(page.getByTestId("refresh-btn")); - await expectCompactDesktopTarget(page.getByTestId("attack-type-filter")); - const openAttack = page.getByTestId("open-attack-home-attack-001"); - await openAttack.scrollIntoViewIfNeeded(); - await expectCompactDesktopTarget(openAttack); - await expectCompactDesktopTarget(page.getByTestId("next-page-btn")); - - await page.goto("/datasets"); - await expect(page.getByRole("heading", { name: "Datasets" })).toBeVisible(); - await expectCompactDesktopTarget(page.getByRole("combobox", { name: "Modalities" })); - await expectCompactDesktopTarget(page.getByRole("combobox", { name: "Harm categories" })); - await expectCompactDesktopTarget(page.getByRole("combobox", { name: "Sort datasets" })); -});