/** * mizan E2E Integration Tests * * Real Chromium → Real React app (generated hooks) → Real Django backend * * Every test uses the generated mizan API, not raw call() or fetch(). */ import { test, expect } from '@playwright/test' const BASE = process.env.HARNESS_URL || 'http://localhost:5174' async function fixture(page: any, name: string) { await page.goto(`${BASE}#${name}`) await page.waitForSelector('[data-testid="result"], [data-testid="error-type"]', { timeout: 10000 }) } async function getResult(page: any): Promise { const el = page.locator('[data-testid="result"]') if (await el.count() > 0) return JSON.parse(await el.textContent()) return null } async function getError(page: any) { const typeEl = page.locator('[data-testid="error-type"]') if (await typeEl.count() === 0) return null return { type: await typeEl.textContent(), code: await page.locator('[data-testid="error-code"]').textContent(), message: await page.locator('[data-testid="error-message"]').textContent(), } } // ─── useEcho, useAdd, useMultiply ─────────────────────────────────────────── test.describe('generated function hooks', () => { test('useEcho returns echoed text', async ({ page }) => { await fixture(page, 'echo') const result = await getResult(page) expect(result.message).toContain('e2e-test') }) test('useAdd returns correct sum', async ({ page }) => { await fixture(page, 'add') const result = await getResult(page) expect(result.result).toBe(42) }) test('useMultiply (class-based ServerFunction) returns product', async ({ page }) => { await fixture(page, 'multiply') const result = await getResult(page) expect(result.product).toBe(42) }) test('usePermissionCheckFn succeeds with correct secret', async ({ page }) => { await fixture(page, 'permission-success') const result = await getResult(page) expect(result.message).toBe('access granted') }) }) // ─── Error handling ───────────────────────────────────────────────────────── test.describe('error codes from generated hooks', () => { test('non-existent function → MizanError NOT_FOUND', async ({ page }) => { await fixture(page, 'not-found') const error = await getError(page) expect(error!.type).toBe('MizanError') expect(error!.code).toBe('NOT_FOUND') }) test('wrong input types → MizanError VALIDATION_ERROR', async ({ page }) => { await fixture(page, 'validation-error') const error = await getError(page) expect(error!.type).toBe('MizanError') expect(error!.code).toBe('VALIDATION_ERROR') }) test('useWhoami anonymous → auth error', async ({ page }) => { await fixture(page, 'auth-required') const error = await getError(page) expect(error!.type).toBe('MizanError') expect(['UNAUTHORIZED', 'FORBIDDEN']).toContain(error!.code) }) test('useStaffOnly anonymous → UNAUTHORIZED', async ({ page }) => { await fixture(page, 'staff-only') const error = await getError(page) expect(error!.type).toBe('MizanError') expect(['UNAUTHORIZED', 'FORBIDDEN']).toContain(error!.code) }) test('useSuperuserOnly anonymous → UNAUTHORIZED', async ({ page }) => { await fixture(page, 'superuser-only') const error = await getError(page) expect(error!.type).toBe('MizanError') expect(['UNAUTHORIZED', 'FORBIDDEN']).toContain(error!.code) }) test('useVerifiedOnly anonymous → FORBIDDEN', async ({ page }) => { await fixture(page, 'verified-only') const error = await getError(page) expect(error!.type).toBe('MizanError') expect(['UNAUTHORIZED', 'FORBIDDEN']).toContain(error!.code) }) test('useNotImplementedFn → NOT_IMPLEMENTED', async ({ page }) => { await fixture(page, 'not-implemented') const error = await getError(page) expect(error!.type).toBe('MizanError') expect(error!.code).toBe('NOT_IMPLEMENTED') }) test('useBuggyFn → INTERNAL_ERROR', async ({ page }) => { await fixture(page, 'internal-error') const error = await getError(page) expect(error!.type).toBe('MizanError') expect(error!.code).toBe('INTERNAL_ERROR') }) test('usePermissionCheckFn wrong secret → FORBIDDEN', async ({ page }) => { await fixture(page, 'permission-error') const error = await getError(page) expect(error!.type).toBe('MizanError') expect(error!.code).toBe('FORBIDDEN') }) }) // ─── Context hooks ────────────────────────────────────────────────────────── test.describe('generated context hooks', () => { test('useCurrentUser returns anonymous data', async ({ page }) => { await page.goto(`${BASE}#context-current-user`) // Context loads async, wait for result await page.waitForSelector('[data-testid="result"]', { timeout: 10000 }) const result = await getResult(page) expect(result.authenticated).toBe(false) expect(result.email).toBe('') }) }) // ─── Form hooks ─── (removed; forms codegen deferred per Blazr scope) ────── // ─── Channel hooks ────────────────────────────────────────────────────────── test.describe('generated channel hooks', () => { test.skip('useChatChannel receives echoed message', async ({ page }) => { // channels deferred per Blazr scope await page.goto(`${BASE}#channel-chat`) await page.waitForFunction( () => { const el = document.querySelector('[data-testid="channel-message-count"]') return el && parseInt(el.textContent || '0') > 0 }, { timeout: 15000 } ) const msg = JSON.parse(await page.locator('[data-testid="channel-last-message"]').textContent()) expect(msg.text).toBe('hello from e2e') }) })