Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions e2e/smoke.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { test, expect } from '@playwright/test';

/**
* Smoke fail-fast test
*
* One-shot infrastructure check that runs BEFORE every other Playwright project
* via the `smoke` project + `dependencies: ['smoke']` wiring in playwright.config.ts.
*
* Purpose:
* Detect the catastrophic, "nothing will pass" failures (dev server down,
* broken build, routing wedged) and abort the suite in <2 minutes instead of
* running ~40 minutes of redundant red across hundreds of dependent tests.
*
* Scope (intentionally narrow):
* - Standalone — no auth, no storageState, no backend dependency beyond the
* login page being reachable. Composes cleanly with the auth setup project
* (PR #12); both can run as dependencies of `chromium`/`ci` in any order.
* - Tests only the most fundamental contract: dev server serves the React
* app and the auth-gated router redirects unauthenticated users to /login.
*
* Failure semantics:
* - retries: 0 (configured at the project level) — flakes are not tolerated
* here; if smoke is flaky, fix smoke. A pass means downstream tests have a
* chance; a fail means they have no chance, so Playwright skips them.
*/
test.describe('Smoke', () => {
test('dev server serves the app and routing redirects to /login', async ({ page }) => {
// Hitting the root should bounce an unauthenticated user to /login.
// This single navigation proves three things at once:
// 1. The dev server is reachable on baseURL (http://localhost:5173).
// 2. Vite served the React bundle (HTML + JS).
// 3. The router booted and the auth gate redirect ran.
await page.goto('/');

await expect(page).toHaveURL(/\/login$/, { timeout: 10_000 });

// And the login page actually rendered — not a blank white screen from a
// hydration crash. getByLabel matches the <Label htmlFor="email">Email</Label>
// pair in src/features/auth/LoginPage.tsx.
await expect(page.getByLabel(/email/i)).toBeVisible({ timeout: 5_000 });
});
});
36 changes: 30 additions & 6 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ const isCI = !!process.env.CI;
* B&R Capital Real Estate Analytics Dashboard
*
* Projects:
* - "chromium" (default): Local development — all browsers, no retries, HTML reporter
* - "ci": CI pipeline — chromium only, 1 retry, 30s timeout, JSON + HTML reporters
* - "smoke": One-shot infrastructure check — dev server up, app renders,
* routing works. Runs FIRST. If it fails, downstream projects are skipped
* and the suite exits in <2 min instead of ~40 min of redundant red.
* - "setup": Authenticates a browser context and saves storage state to
* e2e/.auth/admin.json so subsequent UI tests don't bounce to /login.
* Runs after smoke so a broken dev server fails before we waste time
* trying to log in.
* - "chromium" (default): Local development — depends on smoke + setup.
* - "ci": CI pipeline — chromium only, depends on smoke + setup, 1 retry,
* 30s timeout, JSON + HTML reporters.
*
* Usage:
* Local: npx playwright test
* CI: npx playwright test --project=ci
* Smoke: npx playwright test --project=smoke
*/
export default defineConfig({
testDir: './e2e',
Expand All @@ -34,11 +43,24 @@ export default defineConfig({
},

projects: [
/* Setup project — runs once before browser tests to authenticate and
* persist storage state to e2e/.auth/admin.json. */
/* Smoke fail-fast project — runs FIRST. If dev server / build / routing
* is broken, downstream projects are skipped and the suite exits fast.
* Standalone: no auth, no storageState, no backend dependency beyond
* the login page being reachable. */
{
name: 'smoke',
testMatch: /smoke\.spec\.ts/,
retries: 0,
use: { ...devices['Desktop Chrome'] },
},

/* Setup project — runs after smoke. Authenticates a browser context
* and persists storage state to e2e/.auth/admin.json so subsequent UI
* tests don't bounce to /login. */
{
name: 'setup',
testMatch: /global\.setup\.ts/,
dependencies: ['smoke'],
},

/* Local development project — runs by default when no --project is specified */
Expand All @@ -48,7 +70,8 @@ export default defineConfig({
...devices['Desktop Chrome'],
storageState: 'e2e/.auth/admin.json',
},
dependencies: ['setup'],
testIgnore: /(smoke|global\.setup)\.spec\.ts/,
dependencies: ['smoke', 'setup'],
},

/* CI project — single browser, stricter timeouts, retries on failure */
Expand All @@ -60,7 +83,8 @@ export default defineConfig({
/* CI-specific: capture video on first retry for debugging */
video: 'on-first-retry',
},
dependencies: ['setup'],
testIgnore: /(smoke|global\.setup)\.spec\.ts/,
dependencies: ['smoke', 'setup'],
retries: 1,
timeout: 30_000,
},
Expand Down
Loading