@@ -0,0 +1,135 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// Route reachability and assistive navigation on the actual app, with simulated provider services.
|
||||
import { expect, test } from "@playwright/test"
|
||||
import { moduleSelected, OPTIONAL_MODULE_IDS } from "../modules/inclusion"
|
||||
import { modulePages } from "../modules/pages"
|
||||
import { makeOrganization, signIn, unique, violations } from "./support/app"
|
||||
|
||||
test("selected module routes, settings links and unknown pages have deliberate accessible outcomes", async ({
|
||||
page,
|
||||
}, info) => {
|
||||
test.setTimeout(120_000)
|
||||
const errors: string[] = []
|
||||
let expectedRefusal = false
|
||||
await signIn(page, `${unique("route-review")}@example.test`)
|
||||
const scope = await makeOrganization(page)
|
||||
const base = `/${scope.org}/${scope.workspace}/${scope.project}`
|
||||
await page.goto(`/${scope.org}/settings`)
|
||||
await page.getByRole("button", { name: "Reject analytics and replay", exact: true }).click()
|
||||
// The next hard navigation must not abort the previous document's dynamic bootstrap.
|
||||
await expect(page.locator('[data-guide="frame.person"]')).toBeEnabled()
|
||||
page.on("pageerror", (error) => errors.push(error.message))
|
||||
page.on("console", (message) => {
|
||||
if (!expectedRefusal && ["warning", "error"].includes(message.type())) errors.push(message.text())
|
||||
})
|
||||
const settings = await page
|
||||
.locator('[data-slot="settings-layout"] a[href]')
|
||||
.evaluateAll((nodes) => nodes.map((node) => node.getAttribute("href") ?? ""))
|
||||
const routes = [
|
||||
...new Set([
|
||||
`/${scope.org}`,
|
||||
`/${scope.org}/${scope.workspace}`,
|
||||
base,
|
||||
`/${scope.org}/${scope.workspace}/modules/notes`,
|
||||
...settings,
|
||||
...Object.keys(modulePages).map((id) => `${base}/${id}`),
|
||||
]),
|
||||
]
|
||||
const outcomes: Array<{ path: string; status: number }> = []
|
||||
for (const path of routes) {
|
||||
const response = await page.goto(path)
|
||||
outcomes.push({ path: path.replaceAll(scope.org, "<org>"), status: response?.status() ?? 0 })
|
||||
expect(response?.status(), path).toBe(200)
|
||||
await expect(page.locator("main h1").first()).toBeVisible()
|
||||
await expect(page.locator('[data-guide="frame.person"]')).toBeEnabled()
|
||||
expect(await violations(page), path).toEqual([])
|
||||
}
|
||||
expectedRefusal = true
|
||||
for (const id of [
|
||||
"missing-module",
|
||||
...OPTIONAL_MODULE_IDS.filter((id) => !moduleSelected(id) && !["guides", "support"].includes(id)),
|
||||
]) {
|
||||
const response = await page.goto(`${base}/${id}`)
|
||||
expect(response?.status(), id).toBe(404)
|
||||
await expect(page.getByRole("heading", { name: "Page not found", exact: true })).toBeVisible()
|
||||
expect(await violations(page), `refused ${id}`).toEqual([])
|
||||
}
|
||||
await info.attach("route-outcomes", {
|
||||
body: Buffer.from(JSON.stringify(outcomes, null, 2)),
|
||||
contentType: "application/json",
|
||||
})
|
||||
expect(errors).toEqual([])
|
||||
})
|
||||
|
||||
test("keyboard skips the frame and client navigation focuses the new page in both themes", async ({
|
||||
page,
|
||||
browserName,
|
||||
}, info) => {
|
||||
await signIn(page, `${unique("keyboard-review")}@example.test`)
|
||||
const scope = await makeOrganization(page)
|
||||
await page.goto(`/${scope.org}/${scope.workspace}`)
|
||||
await page.getByRole("button", { name: "Reject analytics and replay", exact: true }).click()
|
||||
for (const colorScheme of ["light", "dark"] as const) {
|
||||
await page.locator('[data-guide="frame.person"]').click()
|
||||
await page.getByRole("menuitemradio", { name: "System", exact: true }).click()
|
||||
await page.reload()
|
||||
await page.locator('[data-guide="frame.person"]').click()
|
||||
await page.getByRole("menuitemradio", { name: colorScheme === "light" ? "Light" : "Dark", exact: true }).click()
|
||||
// Explicit preference wins even when a hidden document control was initially following the device.
|
||||
await page.emulateMedia({ colorScheme: colorScheme === "light" ? "dark" : "light", reducedMotion: "reduce" })
|
||||
if (colorScheme === "light") await expect(page.locator("html")).toHaveClass(/light/)
|
||||
else await expect(page.locator("html")).not.toHaveClass(/light/)
|
||||
await page.reload()
|
||||
await page.locator("body").press(browserName === "webkit" ? "Alt+Tab" : "Tab")
|
||||
await expect(page.getByRole("link", { name: "Skip to content", exact: true })).toBeFocused()
|
||||
await page.keyboard.press("Enter")
|
||||
await expect(page.locator("main")).toBeFocused()
|
||||
const settings = page.getByRole("link", { name: "Organization settings", exact: true })
|
||||
if (colorScheme === "light") {
|
||||
let release = () => {}
|
||||
const paused = new Promise<void>((resolve) => {
|
||||
release = resolve
|
||||
})
|
||||
await page.route("**/*", async (route) => {
|
||||
if (route.request().resourceType() === "fetch" && route.request().method() === "GET") await paused
|
||||
await route.continue()
|
||||
})
|
||||
try {
|
||||
await settings.press("Enter")
|
||||
const skeleton = page.locator('[data-slot="page-skeleton"]')
|
||||
await expect(skeleton).toBeVisible()
|
||||
await expect(skeleton.getByRole("status")).toBeVisible()
|
||||
const bar = skeleton.locator('[data-slot="skeleton"]').first()
|
||||
await expect(bar).toBeVisible()
|
||||
const painted = await bar.evaluate((el) => {
|
||||
const style = getComputedStyle(el)
|
||||
return { background: style.backgroundColor, animation: style.animationName }
|
||||
})
|
||||
expect(painted.background).not.toBe("rgba(0, 0, 0, 0)")
|
||||
expect(painted.animation).toBe("none")
|
||||
await page.screenshot({ path: info.outputPath("settings-loading-reduced-motion.png") })
|
||||
} finally {
|
||||
release()
|
||||
await page.unrouteAll({ behavior: "wait" })
|
||||
}
|
||||
} else {
|
||||
await settings.press("Enter")
|
||||
}
|
||||
await expect(page.locator("main h1").first()).toBeFocused()
|
||||
for (const width of [360, 768, 1280]) {
|
||||
await page.setViewportSize({ width, height: 900 })
|
||||
const geometry = await page.evaluate(() => ({
|
||||
width: innerWidth,
|
||||
scroll: document.documentElement.scrollWidth,
|
||||
overflow: Array.from(document.querySelectorAll("*"))
|
||||
.map((el) => ({ tag: el.tagName, cls: String(el.className), right: el.getBoundingClientRect().right }))
|
||||
.filter((el) => el.right > innerWidth + 1)
|
||||
.slice(0, 12),
|
||||
}))
|
||||
expect(geometry.scroll, JSON.stringify(geometry)).toBeLessThanOrEqual(geometry.width)
|
||||
expect(await violations(page)).toEqual([])
|
||||
await page.screenshot({ path: info.outputPath(`settings-${colorScheme}-${width}.png`) })
|
||||
}
|
||||
await page.goto(`/${scope.org}/${scope.workspace}`)
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user