Files

96 lines
4.6 KiB
TypeScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The account page in a browser (joint contract §12): a person exports their data -- every installed
// module's section in one file -- then deletes the account with the keyboard, and is signed out on
// every device; the browser modules' stored state is gone, and the same address signs up again as
// a new person with nothing. axe on each state the page shows.
import { readFileSync } from "node:fs"
import { expect, test } from "@playwright/test"
import { callRaw, makeOrganization, signIn, unique, violations } from "./support/app"
test("a person exports their data, then deletes the account and is signed out everywhere", async ({
page,
browser,
}) => {
const email = `${unique("leaver")}@example.test`
await signIn(page, email)
const { org } = await makeOrganization(page, "leaver")
// A second device of the same person.
const elsewhere = await browser.newContext()
const other = await elsewhere.newPage()
await signIn(other, email)
// What the browser modules keep for a signed-in person, as their providers store it.
await page.evaluate((slug) => {
sessionStorage.setItem(`conusai.guides.${slug}.offline`, "{}") // kit:guides
sessionStorage.setItem("conusai.support.session.v1:spk_e2e", "{}") // kit:support
sessionStorage.setItem(`unrelated.${slug}`, "kept")
}, org)
// The shell links to the page from every page.
await page.goto("/")
await page.getByRole("link", { name: "Account", exact: true }).click()
await expect(page).toHaveURL(/\/settings\/account$/)
await expect(page.getByRole("heading", { level: 1, name: "Your account" })).toBeVisible()
await expect(page.getByText(email, { exact: true })).toBeVisible()
await expect(page.getByText(/You are the only member of Org leaver-/)).toBeVisible()
expect(await violations(page)).toEqual([])
// Export: one JSON file, every section named.
const download = page.waitForEvent("download")
await page.getByRole("button", { name: "Export my data" }).click()
const file = await download
expect(file.suggestedFilename()).toMatch(/^account-export-\d{4}-\d{2}-\d{2}\.json$/)
const exported = JSON.parse(readFileSync(await file.path(), "utf8")) as {
format: string
complete: boolean
sections: Record<string, { status: string; data: unknown }>
}
expect(exported.format).toBe("conusai.account-export.v1")
expect(exported.complete).toBe(true)
expect(exported.sections.account).toMatchObject({
status: "ok",
data: { profile: { email }, organizations: [{ slug: org, role: "owner" }] },
})
// Offline (the fake platform), the services hold nothing of the person, and say so.
expect(exported.sections.guides).toEqual({ status: "ok", data: null }) // kit:guides
expect(exported.sections.support).toEqual({ status: "ok", data: null }) // kit:support
await expect(page.getByRole("status").filter({ hasText: "Your export is downloaded." })).toBeVisible()
// A wrong address is refused, and nothing happens.
const confirm = page.getByLabel("Type your e-mail address to confirm")
await confirm.fill("somebody@example.test")
await page.getByRole("button", { name: "Delete my account" }).click()
await expect(
page.getByRole("alert").filter({ hasText: "type the account's e-mail address to confirm" }),
).toBeVisible()
expect(await violations(page)).toEqual([])
// The address, and Enter: the keyboard alone deletes the account.
await confirm.fill(email)
await confirm.press("Enter")
await expect(
page.getByRole("status").filter({ hasText: "Your account is deleted, and you are signed out." }),
).toBeVisible()
expect(await violations(page)).toEqual([])
// The browser modules' lifecycle handlers ran: their keys are gone, nobody else's.
const left = await page.evaluate(() => Object.keys(sessionStorage).sort())
expect(left).toEqual([`unrelated.${org}`])
// Signed out here, and on the other device.
await page.goto("/settings/account")
await expect(page).toHaveURL(/\/auth\/sign-in/)
await other.goto("/")
await expect(other.getByRole("link", { name: "Sign in" }).first()).toBeVisible()
// The other device's cookie, sent to the API: its session is revoked, so it is nobody.
expect((await callRaw(other.request, "", "account.deletion.status", undefined)).status()).toBe(401)
await elsewhere.close()
// The same address is a new person now, with no organization.
await signIn(page, email)
await page.goto("/")
await expect(page.getByRole("heading", { level: 1, name: "Organizations" })).toBeVisible()
await expect(page.getByRole("link", { name: `Org ${org}` })).toHaveCount(0)
})