48 lines
2.2 KiB
JavaScript
48 lines
2.2 KiB
JavaScript
|
|
// Critical-path smoke — the thin regression net described in docs/QA_PLAN.md §4.4.
|
||
|
|
// Keep these fast and stable; grow the suite whenever a manual QA pass finds a UI
|
||
|
|
// regression a click-test could have caught. Deeper flows (login -> pay bill ->
|
||
|
|
// reconcile) get their own spec once a seeded scratch DB + test creds are wired
|
||
|
|
// via a Playwright fixture — see e2e/README.md.
|
||
|
|
const { test, expect } = require('@playwright/test');
|
||
|
|
|
||
|
|
test.describe('login page', () => {
|
||
|
|
test('renders the sign-in form', async ({ page }) => {
|
||
|
|
await page.goto('/login');
|
||
|
|
|
||
|
|
await expect(page).toHaveTitle(/Bill Tracker/i);
|
||
|
|
await expect(page.getByRole('heading', { name: /sign in/i })).toBeVisible();
|
||
|
|
await expect(page.locator('#username')).toBeVisible();
|
||
|
|
await expect(page.locator('#password')).toBeVisible();
|
||
|
|
await expect(page.getByRole('button', { name: /sign in/i })).toBeVisible();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('rejects empty submit without a crash', async ({ page }) => {
|
||
|
|
const errors = [];
|
||
|
|
page.on('pageerror', (e) => errors.push(e));
|
||
|
|
await page.goto('/login');
|
||
|
|
|
||
|
|
await page.getByRole('button', { name: /sign in/i }).click();
|
||
|
|
// Still on the login page, no uncaught exception, no white screen.
|
||
|
|
await expect(page.getByRole('heading', { name: /sign in/i })).toBeVisible();
|
||
|
|
expect(errors, `uncaught page errors: ${errors.map(String).join('\n')}`).toHaveLength(0);
|
||
|
|
});
|
||
|
|
|
||
|
|
// Visual-regression baseline. First run writes the snapshot; later runs diff
|
||
|
|
// against it. Re-baseline intentionally with `npm run test:e2e:update`.
|
||
|
|
test('login page matches visual baseline', async ({ page }) => {
|
||
|
|
await page.goto('/login');
|
||
|
|
await expect(page.getByRole('heading', { name: /sign in/i })).toBeVisible();
|
||
|
|
await expect(page).toHaveScreenshot('login.png', { fullPage: true });
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test.describe('routing', () => {
|
||
|
|
test('unauthenticated deep link lands on login, not a blank page', async ({ page }) => {
|
||
|
|
await page.goto('/bogus-does-not-exist');
|
||
|
|
// Protected shell redirects an unauthenticated user to /login; either way the
|
||
|
|
// page must render real content, never a white screen.
|
||
|
|
await expect(page.locator('body')).not.toBeEmpty();
|
||
|
|
await expect(page).toHaveURL(/\/(login|bogus-does-not-exist)/);
|
||
|
|
});
|
||
|
|
});
|