- changeEmail validates and refuses an address another account owns (users.email is UNIQUE; a collision was a 500 inside the settings form). - /app/[id] no longer redirects a 'running' row to itself; it renders what exists and says the run was interrupted. - ScoreBar / OptionRow / VerdictBlock survive null numbers from the model instead of throwing and losing the block (seen live: '[openui] runtime/render-error: Component ScoreBar render failed'). - drop the ad-hoc render-debug scripts now that Playwright covers the browser. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
27 lines
1.3 KiB
TypeScript
27 lines
1.3 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
test.use({ storageState: ".auth/user.json" });
|
|
|
|
for (const width of [320, 375, 414, 768]) {
|
|
test(`no horizontal scroll at ${width}px`, async ({ page }) => {
|
|
await page.setViewportSize({ width, height: 800 });
|
|
await page.goto("/app");
|
|
const over = await page.evaluate(() => document.documentElement.scrollWidth
|
|
- document.documentElement.clientWidth);
|
|
expect(over).toBeLessThanOrEqual(0);
|
|
const box = await page.getByRole("button", { name: "Analyse" }).boundingBox();
|
|
// .btn is 10px padding + 15px line = 35px at these widths; 32 leaves room for subpixel rounding
|
|
// while still failing a squashed or two-line control.
|
|
expect(box!.height).toBeGreaterThanOrEqual(32);
|
|
});
|
|
}
|
|
|
|
test("every settings field has a visible label", async ({ page }) => {
|
|
await page.goto("/settings");
|
|
// Scoped to the form: the rail's rename boxes are labelled for assistive tech with aria-label, not visibly.
|
|
const orphans = await page.evaluate(() => [...document.querySelectorAll("form input")]
|
|
.filter((el) => { const i = el as HTMLInputElement;
|
|
return i.type !== "hidden" && (!i.id || !document.querySelector(`label[for="${i.id}"]`)); })
|
|
.map((el) => (el as HTMLInputElement).name || (el as HTMLInputElement).type));
|
|
expect(orphans).toEqual([]);
|
|
});
|