import { Page } from "../fixtures/pages"; import { Page as PlaywrightPage, expect } from "@playwright/test"; import { UI_BASE_URL } from "../constants"; export const sidebarLink = (page: PlaywrightPage, name: string | RegExp) => page.getByRole("complementary").getByRole("link", { name, exact: true }); export async function clickSidebarLink(page: PlaywrightPage, name: string | RegExp, groupName?: string): Promise { const link = sidebarLink(page, name); if (groupName && !(await link.isVisible())) { const group = page.getByRole("complementary").getByRole("button", { name: groupName, exact: true }); await expect(group).toBeVisible(); if ((await group.getAttribute("aria-expanded")) === "false") { await group.click(); } } await link.click(); } export async function expectUiRoute(page: PlaywrightPage, segment: string): Promise { const root = (process.env.SERVER_ROOT_PATH ?? "").replace(/\/+$/, ""); const expected = new URL(`${root}/ui/${segment}`, UI_BASE_URL); await expect(page, `navigate to ${expected.pathname}`).toHaveURL( (url) => url.origin === expected.origin && url.pathname.replace(/\/+$/, "") === expected.pathname, ); } /** * Navigates to a specific page using the page query parameter. * Waits for the sidebar to be visible before returning. */ export async function navigateToPage(page: PlaywrightPage, pageEnum: Page): Promise { // A fresh deep-link can race the auth bootstrap: the app briefly treats the // session as anonymous, bounces through /ui/login, and lands back on the // default page with the ?page= param dropped. Re-issue the navigation until // the requested page sticks (auth is warm by the second load) so callers never // assert against the default page. for (let attempt = 0; attempt < 3; attempt++) { await page.goto(`/ui?page=${pageEnum}`); await page.waitForLoadState("networkidle"); const url = new URL(page.url()); const onLegacyRoot = url.pathname.replace(/\/+$/, "").endsWith("/ui"); if (!onLegacyRoot || url.searchParams.get("page") === pageEnum) { break; } } // Dismiss the "Quick feedback" popup if it appears await dismissFeedbackPopup(page); } /** * Dismiss the "Quick feedback" popup that may appear on any page. */ export async function dismissFeedbackPopup(page: PlaywrightPage): Promise { const dismissButton = page.getByText("Don't ask me again"); if (await dismissButton.isVisible({ timeout: 1_500 }).catch(() => false)) { await dismissButton.click(); // Wait for the popup to disappear await expect(dismissButton) .not.toBeVisible({ timeout: 2_000 }) .catch(() => {}); } } export async function setAriaHidden(page: PlaywrightPage, hidden: boolean): Promise { await page.getByRole("button", { name: /Account menu/i }).click(); const panel = page.getByTestId("sidebar-account-menu-panel"); await expect(panel).toBeVisible({ timeout: 5_000 }); const premium = await panel.getByText("Premium", { exact: true }).isVisible(); const toggle = panel.getByRole("switch", { name: "Toggle hide Aria Agent", exact: true }); if (premium) { await expect(toggle).toBeVisible(); if ((await toggle.getAttribute("aria-checked")) !== String(hidden)) { await toggle.click(); } await expect(toggle).toHaveAttribute("aria-checked", String(hidden)); } else { await expect(toggle).toHaveCount(0); } await page.keyboard.press("Escape"); await expect(panel).toBeHidden(); const aria = page.getByRole("banner").getByRole("button", { name: "Aria Agent", exact: true }); if (premium && hidden) { await expect(aria).toBeHidden(); } else { await expect(aria).toBeVisible(); } } /** * Click on a team ID in the table. Team IDs are rendered differently depending * on the component version — try button first (Tremor Button), fall back to * clickable span (Teams Typography.Text). */ export async function clickTeamId(page: PlaywrightPage, teamId: string): Promise { const cell = page.locator("td").filter({ hasText: teamId }).first(); await expect(cell).toBeVisible({ timeout: 10_000 }); await cell.click(); await expect(page.getByText("Back to Teams")).toBeVisible({ timeout: 10_000 }); } export async function openKeyDetail(page: PlaywrightPage, alias: string): Promise { await page.getByPlaceholder("Search by key alias or ID").fill(alias); const row = page.getByRole("row").filter({ hasText: alias }); await expect(row, `key row "${alias}" never appeared on the Virtual Keys page`).toBeVisible({ timeout: 15_000 }); await row.getByRole("button", { name: alias }).click(); await expect(page.getByText("Back to Keys"), `key detail for "${alias}" never opened`).toBeVisible({ timeout: 15_000, }); }