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/project-creation-fixture', plugins: [vue(), ...(serve ? [tailwind()] : [])], resolve: { alias: { '@': fileURLToPath(new URL('../../resources/js', import.meta.url)), }, }, server: serve ? { host: '127.0.0.1', port: 4179, strictPort: true, hmr: false } : { middlewareMode: true, hmr: false, watch: null }, appType: 'custom', }); const areas = [ 'project_settings', 'board_columns', 'ticket_statuses', 'status_transitions', 'ticket_types', 'ticket_priorities', 'ticket_fields', 'ticket_link_types', 'repository_connections', 'notification_actions', ]; function makePage(url, errors = {}) { const query = new URL(url, 'http://localhost').searchParams; return { component: url.startsWith('/projects/create') ? 'Create' : 'Destination', url, version: null, clearHistory: false, encryptHistory: false, props: { name: 'KanbanGit', errors, sidebarOpen: true, auth: { user: { id: 1, name: 'Alex Morgan', email: 'alex@example.test', avatar: null, }, permissions: { globalAdmin: false, projectCreate: true, canManageTeams: false, }, }, navigation: { project: null }, copyAreaOptions: areas.map((value) => ({ value, label: value, description: 'Server description', })), sourceProjects: query.has('empty') ? [] : [ { id: 7, key: 'ENG', name: 'Engineering ยท Platform and developer experience', }, { id: 8, key: 'OPS', name: 'Operations' }, ], sourceTemplates: query.has('empty') || query.has('no-templates') ? [] : [ { id: 3, name: 'Team baseline', slug: 'team-baseline', copy_areas: ['board_columns', 'ticket_types'], }, { id: 4, name: 'Minimal setup', slug: 'minimal', copy_areas: [], }, ], selectedSourceTemplateId: query.has('template') ? Number(query.get('template')) : null, }, }; } if (serve) { // Real Vue/Inertia form with isolated transport; PHPUnit covers persisted projects. server.middlewares.use(async (request, response, next) => { if (!request.url.startsWith('/projects')) { return next(); } let url = request.url; let errors = {}; if (request.method === 'POST') { const result = request.headers.cookie?.match( /creation_test_result=(\w+)/, )?.[1] ?? 'success'; let body = ''; for await (const chunk of request) { body += chunk; } const data = JSON.parse(body); if (result === 'server') { response.statusCode = 503; response.end('Simulated server failure'); return; } url = result === 'validation' ? '/projects/create' : '/projects/99'; if (result === 'validation') { errors = { key: 'The key has already been taken.', ...(data.creation_mode === 'duplicate' ? { 'copy_areas.0': 'Select a valid configuration area.', } : {}), }; } } const page = makePage(url, errors); 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( 'Project creation fixture ready at http://127.0.0.1:4179/projects/create', ); } 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 Create = ( await server.ssrLoadModule( '/resources/js/pages/projects/Create.vue', ) ).default; const render = async ( url, errors = {}, locale = createLocalization(), ) => { const response = await createInertiaApp({ page: makePage(url, errors), resolve: () => Create, render: renderToString, setup: ({ App, props, plugin }) => createSSRApp({ render: () => h(App, props) }) .use(plugin) .use(locale) .use(createAppearance('light')), }); return response.body.split('