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/teams-fixture', plugins: [vue(), ...(serve ? [tailwind()] : [])], resolve: { alias: { '@': fileURLToPath(new URL('../../resources/js', import.meta.url)), }, }, server: serve ? { host: '127.0.0.1', port: 4197, strictPort: true, hmr: false } : { middlewareMode: true, hmr: false, watch: null }, appType: 'custom', }); const users = [ { id: 1, name: 'Alex Morgan', email: 'alex@example.test', avatar: null }, { id: 2, name: 'Sam Taylor', email: 'sam@example.test', avatar: null }, { id: 3, name: 'Jordan Longname With Several Names', email: 'jordan.long.email.address@example.test', avatar: null, }, ]; function initial() { return { canCreateTeams: true, users, status: null, teams: [ { id: 1, name: 'Platform team', user_ids: [1], users: [users[0]] }, { id: 2, name: 'Delivery team', user_ids: [], users: [] }, ], teamPermissions: { 1: { 2: ['team:invite'] } }, permissionOptions: ['invite', 'manage', 'admin'].map((name) => ({ value: 'team:' + name, label: 'Team ' + name, })), }; } const makePage = (extra = {}) => ({ component: 'Teams', url: '/teams', version: null, clearHistory: false, encryptHistory: false, props: { name: 'KanbanGit', errors: {}, sidebarOpen: true, appearance: 'system', auth: { user: users[0], permissions: { globalAdmin: false, projectCreate: false, canManageTeams: true, }, }, navigation: { project: null }, ...initial(), ...extra, }, }); if (serve) { let data = initial(); server.middlewares.use(async (request, response, next) => { if (!request.url.startsWith('/teams')) { return next(); } const url = new URL(request.url, 'http://localhost'); if (url.searchParams.has('reset')) { data = initial(); } if (url.searchParams.has('manager')) { data.canCreateTeams = false; } if (url.searchParams.has('empty')) { data.teams = []; } let errors = {}; if (request.method !== 'GET') { const result = request.headers.cookie?.match(/teams_result=(\w+)/)?.[1] ?? 'success'; if (['server', 'forbidden'].includes(result)) { response.statusCode = result === 'forbidden' ? 403 : 503; response.end('Simulated failure'); return; } let body = ''; for await (const chunk of request) { body += chunk; } const submitted = JSON.parse(body || '{}'); if (result === 'validation') { errors = url.pathname.endsWith('/members') ? { 'user_ids.0': 'This user is unavailable.' } : url.pathname.includes('/permissions/') ? { 'permissions.0': 'This permission is unavailable.' } : { name: 'This team name is already taken.' }; } else if (request.method === 'POST') { data.teams.push({ id: 3, name: submitted.name, user_ids: [], users: [], }); } else if (url.pathname.endsWith('/members')) { const team = data.teams.find( (team) => team.id === Number(url.pathname.split('/')[2]), ); team.user_ids = submitted.user_ids; team.users = users.filter((user) => team.user_ids.includes(user.id), ); } else { const [, , teamId, , , userId] = url.pathname.split('/'); data.teamPermissions[teamId] ??= {}; data.teamPermissions[teamId][userId] = submitted.permissions; } if (Object.keys(errors).length === 0) { data.status = 'Saved'; } } const page = makePage({ ...data, 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('Teams 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 { en } = await server.ssrLoadModule( '/resources/js/locales/en.ts', ); const component = ( await server.ssrLoadModule('/resources/js/pages/teams/Index.vue') ).default; const render = async (extra = {}, locale = createLocalization()) => ( await createInertiaApp({ page: makePage(extra), resolve: () => component, render: renderToString, setup: ({ App, props, plugin }) => createSSRApp({ render: () => h(App, props) }) .use(plugin) .use(locale) .use(createAppearance()), }) ).body; const html = await render(); assert.match(html, /Platform team/); assert.match(html, /1 member/); assert.match(html, /0 members/); assert.match(html, /Manage members of Platform team/); assert.match(html, /Manage permissions for Platform team/); assert.match(html, /No members yet/); assert.match(html, /Create team/); assert.doesNotMatch(html, /Global admin|Hold Cmd|