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-access-fixture', plugins: [vue(), ...(serve ? [tailwind()] : [])], resolve: { alias: { '@': fileURLToPath(new URL('../../resources/js', import.meta.url)), }, }, server: serve ? { host: '127.0.0.1', port: 4198, 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 { project: { id: 1, name: 'Core platform', key: 'CORE' }, users, status: null, globalAdminUserIds: [1], directPermissions: { 2: ['project:read'] }, teamPermissions: { 1: ['project:write'] }, effectivePermissions: { 1: ['project:read', 'project:write', 'project:admin'], 2: ['project:read', 'project:write'], 3: [], }, teams: [ { id: 1, name: 'Platform team', user_ids: [2], users: [users[1]] }, { id: 2, name: 'Delivery team', user_ids: [], users: [] }, ], permissionOptions: ['read', 'write', 'admin'].map((name) => ({ value: 'project:' + name, label: name, })), }; } const makePage = (extra = {}) => ({ component: 'Access', url: '/projects/1/access', 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('/projects/1/')) { return next(); } const url = new URL(request.url, 'http://localhost'); if (url.searchParams.has('reset')) { data = initial(); } if (url.searchParams.has('noUsers')) { data.users = []; } if (url.searchParams.has('empty')) { data.teams = []; } let errors = {}; if (request.method !== 'GET') { const result = request.headers.cookie?.match(/access_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 = { 'permissions.0': 'This permission is unavailable.' }; } else { const parts = url.pathname.split('/'); const kind = parts[4]; const id = parts[5]; data[ kind === 'users' ? 'directPermissions' : 'teamPermissions' ][id] = submitted.permissions; for (const user of users) { const grants = [ ...(data.directPermissions[user.id] ?? []), ...data.teams .filter((team) => team.user_ids.includes(user.id)) .flatMap( (team) => data.teamPermissions[team.id] ?? [], ), ]; data.effectivePermissions[user.id] = data.globalAdminUserIds.includes(user.id) || grants.includes('project:admin') ? ['project:read', 'project:write', 'project:admin'] : grants.includes('project:write') ? ['project:read', 'project:write'] : grants.includes('project:read') ? ['project:read'] : []; } } 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('Project access 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/projects/Access.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, /People and effective access/); assert.match(html, /Global administrator ยท all project permissions/); assert.match(html, /Via Platform team:/); assert.match(html, /No project access/); assert.match(html, /Edit access for Sam Taylor/); assert.match(html, /No project permissions assigned/); assert.doesNotMatch(html, /