78 lines
3.5 KiB
JavaScript
78 lines
3.5 KiB
JavaScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
// Read-only browser smoke for a built image or a live deployment. No account is needed.
|
|
// pnpm exec playwright install chromium
|
|
// node scripts/verify-shell.mjs https://your-app.example.test
|
|
import { strict as assert } from "node:assert"
|
|
import { mkdir, writeFile } from "node:fs/promises"
|
|
import { join } from "node:path"
|
|
import { chromium, expect } from "@playwright/test"
|
|
|
|
const base = new URL(process.argv[2] ?? "http://127.0.0.1:3000")
|
|
assert(["http:", "https:"].includes(base.protocol), "expected an HTTP(S) app URL")
|
|
// Evidence is opt-in, local and bounded; never collect URLs, headers, cookies or bodies.
|
|
const evidence = process.env.CONUSAI_BROWSER_EVIDENCE_DIR
|
|
if (evidence) {
|
|
process.umask(0o077)
|
|
await mkdir(evidence, { mode: 0o700 })
|
|
}
|
|
const errors = []
|
|
const consoleErrors = []
|
|
const network = []
|
|
let passed = false
|
|
const browser = await chromium.launch()
|
|
try {
|
|
const context = await browser.newContext({ locale: "en-US" })
|
|
const page = await context.newPage()
|
|
page.setDefaultTimeout(15_000)
|
|
page.on("pageerror", (error) => errors.push(error.message.slice(0, 300)))
|
|
page.on("console", (message) => {
|
|
if (message.type() === "error" && consoleErrors.length < 100) consoleErrors.push(message.text().slice(0, 300))
|
|
})
|
|
page.on("response", (response) => {
|
|
if (!evidence || network.length >= 1000) return
|
|
const url = new URL(response.url())
|
|
network.push({
|
|
method: response.request().method(),
|
|
sameOrigin: url.origin === base.origin,
|
|
route: url.pathname.startsWith("/assets/") ? "asset" : url.pathname === "/" ? "shell" : "other",
|
|
status: response.status(),
|
|
})
|
|
})
|
|
const response = await page.goto(new URL("/", base).href, { waitUntil: "networkidle" })
|
|
assert.equal(response?.status(), 200)
|
|
if (evidence) await page.screenshot({ path: join(evidence, "initial.png"), fullPage: true })
|
|
const root = page.locator("html")
|
|
await page.getByRole("button", { name: "Switch to light theme", exact: true }).click()
|
|
await expect(root).toHaveClass("light")
|
|
await page.reload()
|
|
await expect(root).toHaveClass("light")
|
|
await page.getByRole("button", { name: "Lietuvių", exact: true }).click()
|
|
await expect(root).toHaveAttribute("lang", "lt")
|
|
await expect(page.getByRole("button", { name: "Perjungti į tamsią temą", exact: true })).toBeVisible()
|
|
await page.reload()
|
|
await expect(root).toHaveAttribute("lang", "lt")
|
|
await expect(root).toHaveClass("light")
|
|
if (evidence) await page.screenshot({ path: join(evidence, "lithuanian-light.png"), fullPage: true })
|
|
await page.getByRole("button", { name: "Perjungti į tamsią temą", exact: true }).click()
|
|
await expect(root).not.toHaveClass("light")
|
|
await page.getByRole("button", { name: "English", exact: true }).click()
|
|
await expect(root).toHaveAttribute("lang", "en")
|
|
await expect(page.getByRole("button", { name: "Switch to light theme", exact: true })).toBeVisible()
|
|
if (evidence) await page.screenshot({ path: join(evidence, "english-dark.png"), fullPage: true })
|
|
assert.deepEqual(errors, [], "browser runtime errors")
|
|
assert.deepEqual(consoleErrors, [], "browser console errors")
|
|
passed = true
|
|
console.log("verify-shell: theme and language switch both ways and persist across reloads")
|
|
} finally {
|
|
await browser.close()
|
|
if (evidence)
|
|
await writeFile(
|
|
join(evidence, "result.json"),
|
|
`${JSON.stringify({ passed, errors, consoleErrors, network }, null, 2)}\n`,
|
|
{
|
|
flag: "wx",
|
|
mode: 0o600,
|
|
},
|
|
)
|
|
}
|