import assert from 'node:assert/strict'; import process from 'node:process'; import { fileURLToPath } from 'node:url'; import vue from '@vitejs/plugin-vue'; import tailwind from '@tailwindcss/vite'; import { createServer } from 'vite'; import { createSSRApp, h } from 'vue'; import { renderToString } from 'vue/server-renderer'; import { createInertiaApp } from '@inertiajs/vue3'; const serve = process.argv.includes('--serve'); const server = await createServer({ configFile: false, cacheDir: 'node_modules/.vite/security-fixture', plugins: [vue(), ...(serve ? [tailwind()] : [])], resolve: { alias: { '@': fileURLToPath(new URL('../../resources/js', import.meta.url)), }, }, server: serve ? { host: '127.0.0.1', port: 4185, strictPort: true, hmr: false } : { middlewareMode: true, hmr: false, watch: null }, appType: 'custom', }); const application = { id: 1, name: 'Desktop tool', created_at: '2026-09-14T00:00:00Z', last_used_at: null, }; const key = 'kg_app_one_time_test_key'; function makePage(url, extra = {}) { return { component: url.startsWith('/settings/security') ? 'Security' : 'Applications', url, version: null, clearHistory: false, encryptHistory: false, props: { name: 'KanbanGit', errors: {}, sidebarOpen: true, appearance: 'system', canManageTwoFactor: true, requiresConfirmation: true, twoFactorEnabled: false, applications: [application], applicationKey: null, auth: { user: { id: 1, name: 'Alex Morgan', email: 'alex@example.test', avatar: null, avatar_color: '#10B981', avatar_is_generated: true, }, permissions: { globalAdmin: false, projectCreate: false, canManageTeams: false, }, }, navigation: { project: null }, ...extra, }, }; } if (serve) { server.middlewares.use(async (request, response, next) => { const path = new URL(request.url, 'http://localhost').pathname; if (!path.startsWith('/settings') && !path.startsWith('/user/')) { return next(); } const result = request.headers.cookie?.match(/security_test_result=(\w+)/)?.[1] ?? 'success'; const mutation = request.method !== 'GET'; if (mutation && ['server', 'expired'].includes(result)) { response.statusCode = result === 'expired' ? 419 : 503; response.end('Simulated failure'); return; } let body = ''; if (mutation) { for await (const chunk of request) { body += chunk; } } const submitted = body ? JSON.parse(body) : {}; const cookies = request.headers.cookie ?? ''; let enabled = cookies.includes('security_enabled=true'); let extra = { twoFactorEnabled: enabled }; let url = path; if (path.startsWith('/user/two-factor-') && !mutation) { response.setHeader('Content-Type', 'application/json'); response.end( JSON.stringify( path.endsWith('qr-code') ? { svg: '', } : path.endsWith('secret-key') ? { secretKey: 'MANUALSETUPTESTKEY' } : ['old-code-0001', 'old-code-0002'], ), ); return; } if (mutation) { url = path.startsWith('/settings/applications') ? '/settings/applications' : '/settings/security'; if (result === 'validation') { extra.errors = path.includes('confirmed-two-factor') ? { confirmTwoFactorAuthentication: { code: 'The provided two factor authentication code was invalid.', }, } : path.includes('applications') ? { name: 'The name field is required.' } : { current_password: 'The password is incorrect.' }; } else if (path.includes('confirmed-two-factor')) { enabled = true; response.setHeader( 'Set-Cookie', 'security_enabled=true; Path=/', ); } else if ( path === '/user/two-factor-authentication' && request.method === 'DELETE' ) { enabled = false; response.setHeader( 'Set-Cookie', 'security_enabled=false; Path=/', ); } else if ( path === '/settings/applications' && request.method === 'POST' ) { extra.applicationKey = key; extra.applications = [{ ...application, name: submitted.name }]; } else if ( path.startsWith('/settings/applications/') && request.method === 'DELETE' ) { extra.applications = []; } extra.twoFactorEnabled = enabled; } const page = makePage(url, extra); response.setHeader('Vary', 'X-Inertia'); if (request.headers['x-inertia']) { response.setHeader('X-Inertia', 'true'); response.setHeader('Content-Type', 'application/json'); response.end(JSON.stringify(page)); return; } const escaped = JSON.stringify(page) .replaceAll('&', '&') .replaceAll('"', '"') .replaceAll('<', '<'); response.setHeader('Content-Type', 'text/html'); response.end( await server.transformIndexHtml( request.url, '
', ), ); }); await server.listen(); console.log('Security fixture ready.'); } else { try { const { createLocalization } = await server.ssrLoadModule( '/resources/js/lib/localization.ts', ); const { createAppearance } = await server.ssrLoadModule( '/resources/js/composables/useAppearance.ts', ); const Security = ( await server.ssrLoadModule( '/resources/js/pages/settings/Security.vue', ) ).default; const Applications = ( await server.ssrLoadModule( '/resources/js/pages/settings/Applications.vue', ) ).default; const render = async (url, component, extra = {}) => ( await createInertiaApp({ page: makePage(url, extra), resolve: () => component, render: renderToString, setup: ({ App, props, plugin }) => createSSRApp({ render: () => h(App, props) }) .use(plugin) .use(createLocalization()) .use(createAppearance()), }) ).body; const security = await render('/settings/security', Security); assert.match(security, /autocomplete="current-password"/); assert.match(security, /Enable 2FA/); assert.doesNotMatch( await render('/settings/security', Security, { canManageTwoFactor: false, }), /Enable 2FA/, ); const enabled = await render('/settings/security', Security, { twoFactorEnabled: true, }); assert.match(enabled, /View recovery codes/); assert.doesNotMatch(enabled, /old-code/); const hidden = await render('/settings/applications', Applications); assert.doesNotMatch(hidden, /id="application-key"/); assert.doesNotMatch(hidden, /Copy key/); const shown = await render('/settings/applications', Applications, { applicationKey: key, }); assert.match(shown, /id="application-key"/); assert.match(shown, /Copy key/); assert.match(shown, /kg_app_one_time_test_key/); console.log('Security UI rendering checks passed.'); } finally { await server.close(); } }