mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 22:01:11 +00:00
38eb87fb08
Scaffolds the Blackbox test project per AZ-456 / environment.md across
the three profiles:
- fast : Vitest 3.x + jsdom + MSW 2.x + RTL/jest-dom; tests/setup.ts
boots the MSW Node server with onUnhandledRequest:'error',
afterEach resets handlers, clears bearer + navigate-to-login
spy. Default handlers ship for every suite service plus OWM
and tile stand-ins. Fixtures mirror seed_* in test-data.md.
- e2e : Playwright ^1.49 with chromium + firefox projects against the
suite docker-compose stack; owm-stub + tile-stub Bun servers,
playwright-runner image, seeds.sql for the test-db.
- static: scripts/run-tests.sh extended — tsc --noEmit (test config),
vite build, ripgrep checks (with grep -r fallback), CSV
report at test-output/static-report.csv per AC-7 columns.
Smoke tests cover AC-3, AC-4 (fast, 5 tests, PASS) and AC-1, AC-2,
AC-5, AC-8 (e2e, gated by Risk 4 docker availability). Static profile
(13 checks) PASS — STC-SEC1 (no literal OWM key) lifted from
QUARANTINE per AZ-447 with a narrowed pattern.
Files:
+24 tests/**, +10 e2e/**, +vitest.config.ts, +tsconfig.test.json
~package.json (test scripts + devDeps for vitest, @testing-library/*,
msw, @playwright/test, jsdom, @types/node, @vitest/coverage-v8)
~scripts/run-tests.sh, scripts/run-performance-tests.sh — switched
RESULTS_DIR to test-output/, compose path to project-local
~.gitignore — added /test-output/
Verification:
bun run test:fast → 11 / 11 PASS
./scripts/run-tests.sh → static 13/13 + fast 11/11 PASS, exit 0
Tracker: AZ-456 → In Testing.
Co-authored-by: Cursor <cursoragent@cursor.com>
80 lines
3.3 KiB
TypeScript
80 lines
3.3 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
// Smoke tests for AZ-456 e2e infrastructure (AC-1, AC-2, AC-5, AC-8).
|
|
// Every other test file under e2e/tests/ is owned by AZ-457..AZ-482; those
|
|
// tasks add the production-shaped assertions per group. This file MUST stay
|
|
// minimal so any flake here is unambiguously an infrastructure regression.
|
|
|
|
const EXTERNAL_HOSTS = [
|
|
/api\.openweathermap\.org/,
|
|
/unpkg\.com/,
|
|
/\.tile\.openstreetmap\.org$/,
|
|
/^tile\.openstreetmap\.org$/,
|
|
]
|
|
|
|
test.describe('AZ-456 e2e infrastructure', () => {
|
|
test.beforeEach(async ({ context }, testInfo) => {
|
|
// AC-8: external-host firewall. The compose network is already isolated
|
|
// from the internet; this route guard catches code paths that try to
|
|
// reach an external host directly (and lets resilience tests flip it).
|
|
const externalHits: string[] = []
|
|
await context.route(/.*/, async (route) => {
|
|
const url = route.request().url()
|
|
if (EXTERNAL_HOSTS.some((re) => re.test(new URL(url).hostname))) {
|
|
externalHits.push(url)
|
|
await route.abort()
|
|
return
|
|
}
|
|
await route.continue()
|
|
})
|
|
testInfo.attachments.push({ name: 'externalHits', contentType: 'application/json', body: Buffer.from('[]') })
|
|
;(testInfo as unknown as { __externalHits: string[] }).__externalHits = externalHits
|
|
})
|
|
|
|
test.afterEach(async ({}, testInfo) => {
|
|
const externalHits = (testInfo as unknown as { __externalHits?: string[] }).__externalHits ?? []
|
|
expect(externalHits, 'leaked external requests detected').toEqual([])
|
|
})
|
|
|
|
test('AC-1: SPA HTML is served from azaion-ui', async ({ page }) => {
|
|
const response = await page.goto('/')
|
|
expect(response?.ok()).toBeTruthy()
|
|
const html = await page.content()
|
|
expect(html).toMatch(/<html[\s>]/i)
|
|
})
|
|
|
|
test('AC-2: owm-stub returns the canned wind shape', async ({ request }) => {
|
|
const res = await request.get('http://owm-stub:8081/data/2.5/weather?lat=0&lon=0&appid=test')
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
expect(body.wind).toEqual({ speed: 5.0, deg: 270 })
|
|
})
|
|
|
|
test('AC-2: tile-stub returns a 256x256 PNG', async ({ request }) => {
|
|
const res = await request.get('http://tile-stub:8082/1/0/0.png')
|
|
expect(res.status()).toBe(200)
|
|
expect(res.headers()['content-type']).toBe('image/png')
|
|
const body = await res.body()
|
|
expect(body.subarray(0, 8)).toEqual(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))
|
|
})
|
|
|
|
test('AC-5: Playwright runs under the configured browser project', async ({ browserName }) => {
|
|
expect(['chromium', 'firefox']).toContain(browserName)
|
|
})
|
|
|
|
test('AC-8: external host is blocked by the route guard', async ({ page }) => {
|
|
const externalHits = (test.info() as unknown as { __externalHits?: string[] }).__externalHits ?? []
|
|
const beforeCount = externalHits.length
|
|
await page.goto('/')
|
|
await page.evaluate(() =>
|
|
fetch('https://api.openweathermap.org/data/2.5/weather?appid=leak').catch(() => null),
|
|
)
|
|
// The guard converts the request into an abort, so the leak is recorded
|
|
// but no real request escapes. The afterEach assertion will fire next.
|
|
expect(externalHits.length).toBeGreaterThan(beforeCount)
|
|
// Reset so afterEach doesn't fail this specific test (the guard already
|
|
// proved the assertion).
|
|
externalHits.length = 0
|
|
})
|
|
})
|