// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0 // The app frame in a browser: it stays mounted while a person moves, so an open right panel stays open; Settings // is one page of sections by permission, and it renames the organization and sets its language; the theme can // follow the device; the layout comes back from its cookies; and a phone gets the sidebar in a sheet. import { expect, type Page, test } from "@playwright/test" import { moduleSelected } from "../modules/inclusion" import { call, makeOrganization, openPersonMenu, openScope, signIn, unique, violations } from "./support/app" /** Marks the frame's element: a remount, or a full load, loses the mark. */ const markFrame = (page: Page): Promise => page.locator('[data-slot="app-frame"]').evaluate((frame) => { ;(frame as HTMLElement & { kept?: boolean }).kept = true }) const frameKept = (page: Page): Promise => page.locator('[data-slot="app-frame"]').evaluate((frame) => (frame as HTMLElement & { kept?: boolean }).kept === true) test("the frame stays mounted across pages, and the Agent stays open with what the person was writing", async ({ page, }) => { await signIn(page, `${unique("owner")}@example.test`) const { org, workspace, project } = await makeOrganization(page) await page.goto(`/${org}/${workspace}/${project}/knowledge`) await markFrame(page) await page.getByRole("button", { name: "Agent" }).click() const panel = page.locator('[data-slot="side-panel"]') await expect(panel.getByRole("heading").first()).toBeVisible() const composer = panel.getByRole("textbox") const workingIn = panel.locator(".kit-assistant-context") await expect(workingIn).toHaveText("Working in Review") await composer.fill("What is left to do?") expect(await violations(page)).toEqual([]) // the page fills the centre beside the panel const centre = page.getByRole("main") expect((await centre.boundingBox())?.width).toBeGreaterThan(400) // A project section is a link in the sidebar. await page.getByRole("navigation", { name: "Main navigation" }).getByRole("link", { name: "Documents" }).click() await expect(page).toHaveURL(new RegExp(`/${project}/documents$`)) await expect(composer).toHaveValue("What is left to do?") // the sidebar await page .getByRole("navigation", { name: "Main navigation" }) .getByRole("link", { name: "Workspace overview" }) .click() await expect(page).toHaveURL(new RegExp(`/${org}/${workspace}$`)) await expect(composer).toHaveValue("What is left to do?") // the open panel follows to the workspace's own conversation, with no project open await expect(workingIn).toHaveText("Working in Main") // a plain link of a page await page.getByRole("link", { name: "Review" }).first().click() await expect(page).toHaveURL(new RegExp(`/${workspace}/${project}$`)) await expect(composer).toHaveValue("What is left to do?") await expect(workingIn).toHaveText("Working in Review") expect(await frameKept(page)).toBe(true) }) test("the Agent is on the projects list, working in the workspace", async ({ page }) => { await signIn(page, `${unique("owner")}@example.test`) const { org, workspace } = await makeOrganization(page) await page.goto(`/${org}/${workspace}`) await page.getByRole("button", { name: "Agent" }).click() const panel = page.locator('[data-slot="side-panel"]') await expect(panel.locator(".kit-assistant-context")).toHaveText("Working in Main") await expect(panel.getByRole("textbox")).toBeEditable() expect(await violations(page)).toEqual([]) }) test("organization settings, the workspace and a project remain connected by navigation", async ({ page, }, testInfo) => { const errors: string[] = [] page.on("pageerror", (error) => errors.push(error.message)) page.on("console", (message) => { if (message.type() === "error") errors.push(message.text()) }) page.on("response", (response) => { if (response.status() >= 400) errors.push(`${response.status()} ${new URL(response.url()).pathname}`) }) await signIn(page, "ada@example.test") await page.goto("/acme-review/general") await page.getByRole("link", { name: "Organization settings", exact: true }).click() const sections = page.getByRole("navigation", { name: "Settings sections" }) await expect(sections.getByRole("link").first()).toBeVisible() const destinations = await sections .getByRole("link") .evaluateAll((links) => links.map((link) => ({ href: link.getAttribute("href") ?? "", label: link.textContent?.trim() ?? "" })), ) expect(destinations.length).toBeGreaterThan(1) for (const { href, label } of destinations) { await sections.getByRole("link", { name: label, exact: true }).click() await expect(page).toHaveURL(new RegExp(`${href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`)) await expect(page.getByRole("heading", { name: "Settings", exact: true })).toBeVisible() await expect(sections.getByRole("link", { name: label, exact: true })).toHaveAttribute("aria-current", "page") } await page .getByRole("navigation", { name: "Main navigation" }) .getByRole("link", { name: "Workspace overview" }) .click() await expect(page).toHaveURL(/\/acme-review\/general$/) await page.getByRole("link", { name: "Team notes", exact: true }).click() await expect(page).toHaveURL(/\/acme-review\/general\/notes$/) await expect(page.getByRole("main")).toBeVisible() await page.screenshot({ path: testInfo.outputPath("project-navigation.png") }) expect(errors).toEqual([]) }) test("breadcrumbs distinguish repeated slugs at different scope levels", async ({ page }) => { await signIn(page, `${unique("owner")}@example.test`) const slug = unique("scope") await call(page, "tenancy.org.create", { slug, name: "Organization" }) await call(page, "tenancy.workspace.create", { org: slug, slug, name: "Workspace", visibility: "org" }) await call(page, "tenancy.project.create", { org: slug, workspace: slug, slug, name: "Project", moduleKey: "notes", }) await page.goto(`/${slug}/${slug}/${slug}/notes`) const breadcrumb = page.getByRole("navigation", { name: "Breadcrumb" }) await expect(breadcrumb.getByRole("link", { name: "Workspace" })).toHaveAttribute("href", `/${slug}/${slug}`) await expect(breadcrumb.getByRole("link", { name: "Project", exact: true })).toHaveAttribute( "href", `/${slug}/${slug}/${slug}`, ) await breadcrumb.getByRole("link", { name: "Workspace" }).click() await expect(page).toHaveURL(new RegExp(`/${slug}/${slug}$`)) await openScope(page) await page.getByRole("menuitemradio", { name: "General", exact: true }).click() await expect(page).toHaveURL(new RegExp(`/${slug}/general$`)) await openScope(page) await page.getByRole("menuitemradio", { name: "Workspace", exact: true }).click() await expect(page).toHaveURL(new RegExp(`/${slug}/${slug}$`)) }) test("a newly created workspace can be selected and switched back", async ({ page }, testInfo) => { await signIn(page, `${unique("owner")}@example.test`) const { org, workspace } = await makeOrganization(page) await page.goto(`/${org}/${workspace}`) await page.goto(`/${org}/workspaces`) await page.getByRole("button", { name: "Create a workspace", exact: true }).click() await page.screenshot({ path: testInfo.outputPath("workspace-create-desktop.png") }) await page.setViewportSize({ width: 375, height: 812 }) await expect(page.getByRole("dialog")).toBeVisible() await page.screenshot({ path: testInfo.outputPath("workspace-create-mobile.png") }) await page.setViewportSize({ width: 1280, height: 800 }) await page.getByLabel("Name", { exact: true }).fill("Research") await page.getByLabel("Short name").fill("research") await page.getByRole("button", { name: "Create", exact: true }).click() await expect(page.getByRole("link", { name: "Research", exact: true })).toBeVisible() await openScope(page) await page.getByRole("menuitemradio", { name: "Research", exact: true }).click() await expect(page).toHaveURL(new RegExp(`/${org}/research$`)) await expect(page.locator('[data-guide="frame.scope"]')).toContainText("Research") await openScope(page) await page.getByRole("menuitemradio", { name: "Main", exact: true }).click() await expect(page).toHaveURL(new RegExp(`/${org}/${workspace}$`)) await expect(page.locator('[data-guide="frame.scope"]')).toContainText("Main") }) test("organization, workspace and settings breadcrumbs follow the route hierarchy", async ({ page }) => { await signIn(page, "ada@example.test") await page.goto("/acme-review") await expect(page.getByRole("heading", { name: "Workspaces", level: 1 })).toBeVisible() const list = page.getByRole("grid", { name: "Workspaces" }) await expect(list.getByRole("link", { name: "General", exact: true })).toBeVisible() await list.getByRole("link", { name: "General", exact: true }).click() await expect(page).toHaveURL(/\/acme-review\/general$/) const breadcrumb = page.getByRole("navigation", { name: "Breadcrumb" }) await expect(breadcrumb.getByRole("link", { name: "Acme Review", exact: true })).toHaveCount(0) await expect(breadcrumb.locator('[aria-current="page"]')).toHaveText("General") await page.getByRole("link", { name: "Organization settings", exact: true }).click() await expect(breadcrumb.getByRole("link", { name: "Acme Review", exact: true })).toBeVisible() await expect(breadcrumb.getByRole("link", { name: "General", exact: true })).toHaveCount(0) }) test("account settings follow the last accessible workspace", async ({ page }) => { await signIn(page, `${unique("owner")}@example.test`) const { org, workspace } = await makeOrganization(page) await page.goto(`/${org}/${workspace}`) await expect .poll(async () => (await page.context().cookies()).find((cookie) => cookie.name === "app_workspace")?.value) .toBe(encodeURIComponent(`${org}/${workspace}`)) await page.goto("/settings/account") await expect(page).toHaveURL(new RegExp(`/${org}/settings/account$`)) await expect(page.getByRole("heading", { name: "Your account", exact: true })).toBeVisible() await expect(page.locator('[data-slot="app-frame"]')).toBeVisible() }) test("member roles use the same select control as invitations", async ({ browser, page }, testInfo) => { await signIn(page, `${unique("owner")}@example.test`) const { org } = await makeOrganization(page) const email = `${unique("member")}@example.test` const invite = await call<{ token: string }>(page, "tenancy.member.invite", { org, email, role: "viewer" }) const context = await browser.newContext({ baseURL: new URL(page.url()).origin }) const member = await context.newPage() await signIn(member, email) await call(member, "tenancy.invitation.accept", { org, token: invite.token }) await context.close() await page.goto(`/${org}/members`) const row = page.getByRole("row").filter({ hasText: email }) const role = row.getByRole("button", { name: /New role for/ }) const inviteRole = page.getByRole("button", { name: /Role$/ }).last() await expect(role).toBeVisible() const form = page.locator(".kit-tenancy-invite form") const emailField = form.getByLabel("E-mail address") const formRole = form.getByRole("button", { name: /Role/ }) const emailBox = await emailField.boundingBox() const formRoleBox = await formRole.boundingBox() expect(Math.abs((emailBox?.y ?? 0) - (formRoleBox?.y ?? 0))).toBeLessThan(2) const save = row.getByRole("button", { name: /Save/ }) expect((await role.boundingBox())?.height).toBe((await save.boundingBox())?.height) expect((await role.boundingBox())?.height).toBeGreaterThanOrEqual(24) const nameCell = await row.getByRole("rowheader").boundingBox() const roleControl = await role.boundingBox() expect( Math.abs((nameCell?.y ?? 0) + (nameCell?.height ?? 0) / 2 - (roleControl?.y ?? 0) - (roleControl?.height ?? 0) / 2), ).toBeLessThan(2) expect((await role.boundingBox())?.height).toBeLessThanOrEqual((await inviteRole.boundingBox())?.height ?? 0) await page.screenshot({ path: testInfo.outputPath("members-selects.png"), fullPage: true }) await role.focus() await page.keyboard.press("Enter") await page.getByRole("option", { name: "Editor", exact: true }).click() await row.getByRole("button", { name: /Save/ }).click() await expect(row.getByRole("cell", { name: "Editor", exact: true })).toBeVisible() await page.setViewportSize({ width: 375, height: 812 }) await page.reload() const mobileForm = page.locator(".kit-tenancy-invite form") const mobilePositions = await mobileForm.evaluate((form) => { const email = form.querySelector('input[name="email"]')?.getBoundingClientRect() const role = form.querySelector('[data-slot="select-trigger"]')?.getBoundingClientRect() return { emailBottom: email?.bottom ?? 0, roleTop: role?.top ?? 0 } }) expect(mobilePositions.roleTop).toBeGreaterThan(mobilePositions.emailBottom) const mobileRole = page .getByRole("row") .filter({ hasText: email }) .getByRole("button", { name: /New role for/ }) await mobileRole.scrollIntoViewIfNeeded() const membersRegion = page.getByRole("region", { name: "Members of the organization" }) expect(await membersRegion.evaluate((region) => region.scrollWidth <= region.clientWidth)).toBe(true) await mobileRole.click() await expect(page.getByRole("option", { name: "Viewer", exact: true })).toBeVisible() await page.screenshot({ path: testInfo.outputPath("members-selects-mobile.png"), fullPage: true }) await page.keyboard.press("Escape") await expect(mobileRole).toBeFocused() expect(await violations(page)).toEqual([]) }) test("a refused owner role change stays clear and contained in its table row", async ({ page }, testInfo) => { const email = `${unique("owner")}@example.test` await signIn(page, email) const { org } = await makeOrganization(page) await page.goto(`/${org}/members`) const row = page.getByRole("row").filter({ hasText: email }) const role = row.getByRole("button", { name: /New role for/ }) await role.click() await page.getByRole("option", { name: "Editor", exact: true }).click() await row.getByRole("button", { name: /Save/ }).click() const alert = row.getByRole("alert") await expect(alert).toContainText("last owner") await expect(role).toHaveAttribute("aria-describedby", (await alert.getAttribute("id")) ?? "") expect(await violations(page)).toEqual([]) await page.screenshot({ path: testInfo.outputPath("members-role-error.png"), fullPage: true }) }) test("invitation actions remain aligned in a table cell", async ({ page }, testInfo) => { await signIn(page, `${unique("owner")}@example.test`) const { org } = await makeOrganization(page) const email = `${unique("invite")}@example.test` await call(page, "tenancy.member.invite", { org, email, role: "viewer" }) await page.goto(`/${org}/invitations`) const row = page.getByRole("row").filter({ hasText: email }) const resend = row.getByRole("button", { name: /New link/ }) const revoke = row.getByRole("button", { name: /Revoke/ }) await expect(resend).toBeVisible() await expect(revoke).toBeVisible() expect(await resend.locator("xpath=ancestor::td").evaluate((cell) => getComputedStyle(cell).display)).toBe( "table-cell", ) expect((await resend.boundingBox())?.height).toBe((await revoke.boundingBox())?.height) expect(await violations(page)).toEqual([]) await page.screenshot({ path: testInfo.outputPath("invitation-actions.png"), fullPage: true }) }) test("custom role forms use kit selects for creation and reassignment", async ({ page }, testInfo) => { await signIn(page, `${unique("owner")}@example.test`) const { org } = await makeOrganization(page) await page.goto(`/${org}/roles`) const errors: string[] = [] page.on("pageerror", (error) => errors.push(error.message)) await expect(page.getByRole("textbox", { name: "Name", exact: true })).toHaveCount(0) await page.getByRole("button", { name: "Create a role", exact: true }).click() const name = page.getByRole("textbox", { name: "Name", exact: true }) const create = page.getByRole("button", { name: "Create the role" }) await create.click() await expect(name).toBeFocused() await expect(name).toHaveAttribute("aria-invalid", "true") await name.fill("Review readers") await page.getByRole("button", { name: /Copy the permissions of/ }).click() await page.getByRole("option", { name: "Administrator", exact: true }).click() await create.click() const dialog = page.getByRole("alertdialog") await expect(dialog).toBeVisible() await expect(dialog.getByRole("button", { name: "Cancel" })).toBeFocused() expect(await violations(page)).toEqual([]) await page.keyboard.press("Escape") await expect(dialog).toBeHidden() await expect(create).toBeFocused() await page.keyboard.press("Escape") await expect(page.getByRole("alertdialog")).toBeVisible() await page.getByRole("alertdialog").getByRole("button", { name: /Keep/ }).click() await expect(name).toHaveValue("Review readers") await page.setViewportSize({ width: 375, height: 812 }) await name.scrollIntoViewIfNeeded() await page.screenshot({ path: testInfo.outputPath("role-form-mobile.png"), fullPage: true }) expect(await name.evaluate((field) => field.getBoundingClientRect().right <= innerWidth)).toBe(true) await page.setViewportSize({ width: 1280, height: 800 }) await page.screenshot({ path: testInfo.outputPath("role-form-desktop.png"), fullPage: true }) expect(errors).toEqual([]) await page.getByRole("button", { name: /Where it can be assigned/ }).click() await page.getByRole("option", { name: "Workspace", exact: true }).click() await page.getByRole("button", { name: /Copy the permissions of/ }).click() await page.getByRole("option", { name: "Viewer", exact: true }).click() await page.getByRole("button", { name: "Create the role" }).click() await expect(page.getByRole("heading", { name: "Review readers" })).toBeVisible() const boxes = page.locator(".kit-tenancy-matrix input[type=checkbox]") const dimensions = await boxes.evaluateAll((controls) => controls.map((control) => { const box = control.getBoundingClientRect() return { width: box.width, height: box.height, padding: getComputedStyle(control).padding } }), ) expect(dimensions.length).toBeGreaterThan(0) for (const box of dimensions) { expect(box.width).toBe(box.height) expect(box.padding).toBe("0px") } await expect(page.locator(".kit-tenancy-matrix input[type=checkbox]:disabled").first()).toBeDisabled() const permission = page.locator('.kit-tenancy-matrix li[data-permission="notes.note.read"] input') await permission.focus() await expect(permission).toBeFocused() const checked = await permission.isChecked() await page.keyboard.press("Space") await expect(permission).toBeChecked({ checked: !checked }) await page.keyboard.press("Space") await expect(permission).toBeChecked({ checked }) await page.screenshot({ path: testInfo.outputPath("role-permission-checkboxes.png"), fullPage: true }) await page.getByRole("button", { name: /Move its members to/ }).click() await page.getByRole("option", { name: "Editor", exact: true }).click() await page.getByRole("button", { name: "Delete the role" }).click() await expect(page.getByRole("alertdialog")).toContainText("Editor") await page.getByRole("button", { name: "Delete it" }).click() await expect(page).toHaveURL(new RegExp(`/${org}/roles$`)) }) test("a confirmed sensitive role opens its full-page editor", async ({ page }) => { await signIn(page, `${unique("owner")}@example.test`) const { org } = await makeOrganization(page) await page.goto(`/${org}/roles`) await page.getByRole("button", { name: "Create a role", exact: true }).click() await page.getByRole("textbox", { name: "Name", exact: true }).fill("Operations") await page.getByRole("button", { name: /Copy the permissions of/ }).click() await page.getByRole("option", { name: "Administrator", exact: true }).click() await page.getByRole("button", { name: "Create the role", exact: true }).click() await page.getByRole("alertdialog").getByRole("button", { name: "Create it", exact: true }).click() await expect(page.getByRole("heading", { name: "Operations", exact: true })).toBeVisible() await expect(page.locator('[data-slot="form-panel"]')).toHaveCount(0) }) test("an excluded Documents route shows the kit not-found state", async ({ page }) => { test.skip(moduleSelected("documents"), "This checks the default-off configuration") await signIn(page, `${unique("owner")}@example.test`) const { org, workspace, project } = await makeOrganization(page) const response = await page.goto(`/${org}/${workspace}/${project}/documents`) expect(response?.status()).toBe(404) await expect(page.getByRole("heading", { name: "Page not found", exact: true })).toBeVisible() await expect(page.locator(".app-shell-header")).toBeHidden() }) test("a record in the panel of the real frame: its address, the Help button, and Escape back to the row", async ({ page, }) => { await signIn(page, `${unique("owner")}@example.test`) const scope = await makeOrganization(page) const { noteId } = await call<{ noteId: string }>(page, "notes.note.create", { ...scope, title: "Plan", body: "b" }) const list = `/${scope.org}/${scope.workspace}/${scope.project}/notes` await page.goto(list) await markFrame(page) await page.getByRole("rowheader", { name: "Plan" }).click() await expect(page).toHaveURL(new RegExp(`\\?record=${noteId}$`)) const panel = page.locator('[data-slot="side-panel"]') await expect(panel.getByLabel("Title")).toHaveValue("Plan") // Help is a second occupant, and the record keeps its place await page.getByRole("button", { name: "Help" }).click() await expect(panel.getByRole("tab", { name: "Support" })).toBeVisible() await expect(panel.getByRole("button", { name: /launch|chat/i })).toHaveCount(0) await panel.getByRole("tab", { name: "Plan" }).click() await expect(panel.getByLabel("Title")).toHaveValue("Plan") // no launcher floats over the page await expect(page.locator(".support-launcher, [data-support-launcher]")).toHaveCount(0) expect(await frameKept(page)).toBe(true) }) test("Settings has one row of sections, filtered by permission", async ({ browser, page }) => { await signIn(page, `${unique("owner")}@example.test`) const { org } = await makeOrganization(page, "settings") await page.goto(`/${org}`) await page.getByRole("link", { name: "Organization settings", exact: true }).click() await expect(page).toHaveURL(new RegExp(`/${org}/settings$`)) const sections = page.getByRole("navigation", { name: "Settings sections" }) await expect(sections.getByRole("link", { name: "General", exact: true })).toBeVisible() await expect(sections.getByRole("link", { name: "Workspaces", exact: true })).toBeVisible() await expect(sections.getByRole("link", { name: "Account", exact: true })).toBeVisible() await expect(sections.getByRole("list")).toHaveCount(0) expect(await violations(page)).toEqual([]) // the organization's name and language, saved through tenancy.org.update, and the frame follows await sections.getByRole("link", { name: "General" }).click() await expect(page.getByLabel("Name", { exact: true })).toBeVisible() await page.getByLabel("Name", { exact: true }).fill("Renamed") await page.getByRole("button", { name: /Default language/ }).click() await page.getByRole("option", { name: "Lietuvių" }).click() await page.getByRole("button", { name: "Save" }).click() await expect(page.getByText("Saved", { exact: true })).toBeVisible() await expect(page.locator('[data-guide="frame.scope"]')).toContainText("Renamed") await page.reload() await expect(page.getByLabel("Name", { exact: true })).toHaveValue("Renamed") expect(await violations(page)).toEqual([]) // a new member starts in the organization's language const invited = `${unique("invited")}@example.test` const { token } = await call<{ token: string }>(page, "tenancy.member.invite", { org, email: invited, role: "member", }) const member = await (await browser.newContext({ baseURL: new URL(page.url()).origin })).newPage() await signIn(member, invited) await member.goto(`/invitations/${org}/${token}`) await member .getByRole("button", { name: "Priimti kvietimą" }) .or(member.getByRole("button", { name: "Accept the invitation" })) .click() await member.waitForURL(new RegExp(`/${org}/[a-z0-9-]+$`)) await member.goto(`/${org}`) await expect(member.locator("html")).toHaveAttribute("lang", "lt") // a member sees what they may: the personal section, and no General await member.goto(`/${org}/settings/account`) const theirs = member.getByRole("navigation", { name: "Nustatymų skyriai" }) await expect(theirs.getByRole("link", { name: "Bendrieji" })).toHaveCount(0) await member.context().close() }) test("the theme can follow the device, from the first paint", async ({ page }) => { await signIn(page, `${unique("owner")}@example.test`) const { org } = await makeOrganization(page) await page.emulateMedia({ colorScheme: "light" }) await page.goto(`/${org}`) const root = page.locator("html") await expect(root).not.toHaveClass(/light/) await openPersonMenu(page) await page.getByRole("menuitemradio", { name: "System" }).click() await expect(root).toHaveClass(/light/) await page.reload() await expect(root).toHaveClass(/light/) await page.emulateMedia({ colorScheme: "dark" }) await expect(root).not.toHaveClass(/light/) await page.reload() await expect(root).not.toHaveClass(/light/) expect(await violations(page)).toEqual([]) }) test("the person's menu and the page's foot hold what the header held", async ({ page }) => { await signIn(page, `${unique("owner")}@example.test`) const { org } = await makeOrganization(page) await page.goto(`/${org}`) await expect(page.locator(".app-shell-header")).toBeHidden() // The privacy choices are two steps away, in the person's menu, and not a button at the foot of every page. await expect(page.getByRole("main").getByRole("button", { name: "Privacy choices" })).toHaveCount(0) await openPersonMenu(page) await page.getByRole("menuitem", { name: "Privacy choices" }).click() await expect(page.getByRole("region", { name: "Privacy choices" })).toBeVisible() await openPersonMenu(page) await expect(page.getByRole("menuitem", { name: "Sign out" })).toBeVisible() await page.getByRole("menuitem", { name: "Sign out" }).click() await expect(page).toHaveURL(/\/auth\/signed-out/) }) test("the sidebar comes back as it was left, and a phone gets it in a sheet", async ({ page }) => { await signIn(page, `${unique("owner")}@example.test`) const { org, workspace } = await makeOrganization(page) await page.goto(`/${org}/${workspace}`) const toggle = page.locator('[data-guide="frame.sidebar"]') await expect .poll(async () => (await page.context().cookies()).find((c) => c.name === "app_workspace")?.value) .toBe(encodeURIComponent(`${org}/${workspace}`)) await page.getByRole("link", { name: "Organization settings", exact: true }).click() await expect(page).toHaveURL(new RegExp(`/${org}/settings(?:/account)?$`)) await page .getByRole("navigation", { name: "Main navigation" }) .getByRole("link", { name: "Workspace overview" }) .click() await expect(page).toHaveURL(new RegExp(`/${org}/${workspace}$`)) await toggle.click() await expect .poll(async () => (await page.context().cookies()).find((c) => c.name === "app_sidebar")?.value) .toBe("closed") await page.reload() await expect(page.locator('[data-slot="sidebar"][data-state="collapsed"]').first()).toBeVisible() await page.setViewportSize({ width: 375, height: 812 }) await page.reload() await page.waitForLoadState("networkidle") await toggle.click() const sheet = page.getByRole("dialog") await expect(sheet.getByRole("link", { name: "Workspace overview" })).toBeVisible() expect(await violations(page)).toEqual([]) // Leaving Settings must not redirect back while its layout is still mounted. await sheet.getByRole("link", { name: "Organization settings", exact: true }).click() await expect(page).toHaveURL(new RegExp(`/${org}/settings(?:/account)?$`)) await toggle.click() await page.getByRole("dialog").getByRole("link", { name: "Workspace overview" }).click() await expect(page).toHaveURL(new RegExp(`/${org}/${workspace}$`)) await expect(page.getByRole("heading", { name: "Projects" })).toBeVisible() await expect(page.getByRole("dialog")).toHaveCount(0) await toggle.click() await page.getByRole("dialog").getByRole("link", { name: "Organization settings", exact: true }).press("Enter") await expect(page).toHaveURL(new RegExp(`/${org}/settings(?:/account)?$`)) await toggle.click() await page.getByRole("dialog").getByRole("link", { name: "Workspace overview" }).press("Enter") await expect(page).toHaveURL(new RegExp(`/${org}/${workspace}$`)) }) test("the workspace picker can create and switch projects from the keyboard", async ({ page }) => { await signIn(page, "ada@example.test") const workspace = "/acme-review/general" await page.goto(workspace) // Its name holds what it shows (WCAG 2.5.3): the words with no project chosen, the project's name once one is. const picker = page.getByRole("button", { name: /^(Choose a project$|Switch project: )/ }) await expect(page.getByRole("button", { name: "Choose a project", exact: true })).toBeEnabled() await expect(picker).toBeEnabled() await picker.press("Enter") await page.getByRole("menuitem", { name: "Create a project" }).press("Enter") await expect(page).toHaveURL(`${workspace}/modules?create=true`) await page.reload() const slug = unique("navigation") await page.getByRole("textbox", { name: "Name", exact: true }).fill("Navigation test") await page.getByRole("textbox", { name: "Short name", exact: true }).fill(slug) await page.getByRole("button", { name: "Create", exact: true }).click() await expect(page).toHaveURL(`${workspace}/${slug}`) await expect(page.getByRole("link", { name: "Project overview" })).toHaveAttribute("aria-current", "page") await expect(page.getByRole("button", { name: "Switch project: Navigation test", exact: true })).toBeEnabled() await picker.press("Enter") await page.getByRole("menuitemradio", { name: "Team notes" }).press("Enter") await expect(page).toHaveURL(`${workspace}/notes`) await page.goBack() await expect(page).toHaveURL(`${workspace}/${slug}`) await page.goForward() await expect(page).toHaveURL(`${workspace}/notes`) })