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/settings-fixture', plugins: [vue(), ...(serve ? [tailwind()] : [])], resolve: { alias: { '@': fileURLToPath(new URL('../../resources/js', import.meta.url)), }, }, server: serve ? { host: '127.0.0.1', port: 4184, strictPort: true, hmr: false } : { middlewareMode: true, hmr: false, watch: null }, appType: 'custom', }); function makeUser(generated = false) { return { id: 1, name: 'Alex Morgan', email: 'alex@example.test', avatar: generated ? null : '/avatar.png', avatar_color: '#10B981', avatar_is_generated: generated, email_verified_at: null, must_change_password_on_next_login: false, created_at: '2026-09-14T00:00:00Z', updated_at: '2026-09-14T00:00:00Z', }; } function makePage(url, errors = {}, generated = false) { return { component: url.startsWith('/settings/appearance') ? 'Appearance' : 'Profile', url, version: null, clearHistory: false, encryptHistory: false, props: { name: 'KanbanGit', errors, sidebarOpen: true, mustVerifyEmail: true, status: null, appearance: 'system', auth: { user: makeUser(generated), permissions: { globalAdmin: false, projectCreate: false, canManageTeams: false, }, }, navigation: { project: null }, }, }; } if (serve) { server.middlewares.use(async (request, response, next) => { if (!request.url.startsWith('/settings')) { return next(); } let url = request.url; let errors = {}; let generated = false; if (request.method !== 'GET') { const result = request.headers.cookie?.match( /settings_test_result=(\w+)/, )?.[1] ?? 'success'; if (result === 'server') { response.statusCode = 503; response.end('Simulated server failure'); return; } if (result === 'validation') { errors = { avatar: 'The avatar must be a file of type: jpg, jpeg, png, webp.', }; } generated = request.url.startsWith('/settings/profile/avatar'); url = '/settings/profile'; } const page = makePage(url, errors, generated); 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( 'Settings fixture ready at http://127.0.0.1:4184/settings/profile', ); } else { try { const { createLocalization } = await server.ssrLoadModule( '/resources/js/lib/localization.ts', ); const { createAppearance } = await server.ssrLoadModule( '/resources/js/composables/useAppearance.ts', ); const { en } = await server.ssrLoadModule( '/resources/js/locales/en.ts', ); const Profile = ( await server.ssrLoadModule( '/resources/js/pages/settings/Profile.vue', ) ).default; const Appearance = ( await server.ssrLoadModule( '/resources/js/pages/settings/Appearance.vue', ) ).default; const render = async ( url, component, locale = createLocalization(), ) => { const response = await createInertiaApp({ page: makePage(url), resolve: () => component, render: renderToString, setup: ({ App, props, plugin }) => createSSRApp({ render: () => h(App, props) }) .use(plugin) .use(locale) .use(createAppearance('system')), }); return response.body; }; const profile = await render('/settings/profile', Profile); assert.match(profile, /Account settings/); assert.match(profile, /Profile picture/); assert.match(profile, /Choose picture/); assert.match(profile, /accept="image\/png,image\/jpeg,image\/webp"/); assert.match( profile, /On mobile, you can select from your photo library or camera/, ); assert.match(profile, /aria-current="page"[^>]*>[\s\S]*Profile/); assert.match(profile, /Delete account/); const appearance = await render('/settings/appearance', Appearance); assert.equal((appearance.match(/name="appearance"/g) ?? []).length, 3); assert.match(appearance, /value="light"/); assert.match(appearance, /value="dark"/); assert.match(appearance, /value="system"[^>]*checked/); assert.match(appearance, /Follow the light or dark preference/); assert.match(appearance, /aria-current="page"[^>]*>[\s\S]*Appearance/); const localized = await render( '/settings/profile', Profile, createLocalization( { locale: 'de' }, { de: { ...en, 'settings.eyebrow': 'Kontoeinstellungen', 'profile.title': 'Profil', 'profile.choosePicture': 'Bild auswählen', }, }, ), ); assert.match(localized, /Kontoeinstellungen/); assert.match(localized, /Profil/); assert.match(localized, /Bild auswählen/); console.log('Settings rendering and localization checks passed.'); } finally { await server.close(); } }