Files
app/frontend/scripts/capture-screenshots.mjs
T
Melvin StroblandClaude Opus 5 0c3e6081f2 Green the lint gate and drop the local-stack host ports
- exclude app/flow from ruff and mypy: it is not importable as a package and
  nothing in the API reaches it, so 405 of the 412 findings were about code
  scheduled for restructuring (see NOTEPAD.md)
- fix the 7 real findings outside it: col(...) around created_at for the
  .desc() ordering, and a type: ignore that is no longer needed
- compose.local.yml resets the host ports compose.dev.yml publishes; the
  integrated stack is served entirely through Traefik, and a port already
  taken on the host used to fail the whole stack

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 15:52:57 +02:00

69 lines
2.5 KiB
JavaScript

/**
* Visual verification for the integrated local stack (root `make verify`).
*
* Logs into the dashboard with the bootstrap superuser and captures the app
* shell plus the website hero in both themes. This is the standard "did the
* change actually work" gate — look at the PNGs, do not just trust the build.
*
* Env (all set by the root Makefile):
* APP_URL, WEBSITE_URL, FIRST_SUPERUSER, FIRST_SUPERUSER_PASSWORD
* SCREENSHOT_DIR (default: ./screenshots)
*/
import { mkdir } from "node:fs/promises"
import { chromium } from "@playwright/test"
const APP_URL = process.env.APP_URL || "http://app.localhost"
const WEBSITE_URL = process.env.WEBSITE_URL || "http://localhost"
const EMAIL = process.env.FIRST_SUPERUSER
const PASSWORD = process.env.FIRST_SUPERUSER_PASSWORD
const OUT = process.env.SCREENSHOT_DIR || "screenshots"
if (!EMAIL || !PASSWORD) {
console.error(
"FIRST_SUPERUSER / FIRST_SUPERUSER_PASSWORD are unset — run from the root `make verify`.",
)
process.exit(1)
}
/** Force the theme through the same storage key the pre-paint script reads. */
async function withTheme(browser, theme) {
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
colorScheme: theme,
})
await context.addInitScript((t) => {
localStorage.setItem("fluksio-ui-theme", t)
}, theme)
return context
}
const browser = await chromium.launch()
for (const theme of ["light", "dark"]) {
const dir = `${OUT}/${theme}`
await mkdir(dir, { recursive: true })
const context = await withTheme(browser, theme)
const page = await context.newPage()
await page.goto(`${WEBSITE_URL}/`, { waitUntil: "networkidle" })
// networkidle fires before the staggered entrance animations settle, which
// would capture buttons mid-fade and make contrast look broken.
await page.waitForTimeout(1500)
await page.screenshot({ path: `${dir}/website-hero.png` })
await page.goto(`${APP_URL}/login`, { waitUntil: "networkidle" })
await page.screenshot({ path: `${dir}/app-login.png` })
await page.getByTestId("email-input").fill(EMAIL)
await page.getByTestId("password-input").fill(PASSWORD)
await page.getByRole("button", { name: /log in/i }).click()
await page.waitForURL(`${APP_URL}/`, { timeout: 15000 })
await page.waitForLoadState("networkidle")
await page.screenshot({ path: `${dir}/app-dashboard.png` })
await context.close()
console.log(` wrote ${dir}/{website-hero,app-login,app-dashboard}.png`)
}
await browser.close()