import assert from 'node:assert/strict'; import { fileURLToPath } from 'node:url'; import vue from '@vitejs/plugin-vue'; import { createServer } from 'vite'; import { createSSRApp, h } from 'vue'; import { renderToString } from 'vue/server-renderer'; import { createInertiaApp } from '@inertiajs/vue3'; const server = await createServer({ configFile: false, plugins: [vue()], resolve: { alias: { '@': fileURLToPath(new URL('../../resources/js', import.meta.url)), }, }, server: { middlewareMode: true, hmr: false, watch: null }, appType: 'custom', }); try { const { matchesNavigationUrl, projectNavigation, projectSettingsNavigation, workspaceNavigation, } = await server.ssrLoadModule('/resources/js/lib/navigation.ts'); 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 { default: AppLayout } = await server.ssrLoadModule( '/resources/js/layouts/AppLayout.vue', ); const locale = createLocalization(); const permissions = { globalAdmin: false, projectCreate: false, canManageTeams: false, }; const project = { id: 7, name: 'Engineering', key: 'ENG', canManage: false, canCreateTickets: false, }; const targets = (items) => items.map((item) => item.href.url); assert.deepEqual(targets(workspaceNavigation(permissions, locale.t)), [ '/dashboard', '/projects', ]); assert.deepEqual( targets( workspaceNavigation( { ...permissions, canManageTeams: true }, locale.t, ), ), ['/dashboard', '/projects', '/teams'], ); assert.deepEqual( targets( workspaceNavigation( { ...permissions, projectCreate: true }, locale.t, ), ), ['/dashboard', '/projects', '/project-templates'], ); assert.ok( targets( workspaceNavigation( { ...permissions, globalAdmin: true }, locale.t, ), ).includes('/admin/users'), ); assert.equal(projectNavigation(null, locale.t).length, 0); assert.deepEqual(targets(projectNavigation(project, locale.t)), [ '/projects/7', '/projects/7/board', '/projects/7/backlog', ]); assert.equal(projectSettingsNavigation(project, locale.t).length, 0); assert.deepEqual( targets( projectSettingsNavigation( { ...project, canManage: true }, locale.t, ), ), [ '/projects/7/settings/general', '/projects/7/settings/board', '/projects/7/settings/metadata', '/projects/7/settings/repositories', '/projects/7/settings/notifications', '/projects/7/access', ], ); for (const [target, current, section, expected] of [ ['/projects', '/projects/7/board', true, true], ['/projects', '/project-templates', true, false], ['/projects/7', '/projects/70', true, false], ['/projects/7', '/projects/7/board', false, false], [ '/projects/7/board', '/projects/7/board?filter=mine#lane', false, true, ], [ '/projects/7/board/', 'https://kanbangit.test/projects/7/board', false, true, ], ['/', '/projects', true, false], ]) { assert.equal(matchesNavigationUrl(target, current, section), expected); } const render = async ( url, projectContext, authPermissions = permissions, localization = locale, ) => { const response = await createInertiaApp({ page: { component: 'Preview', url, version: null, clearHistory: false, encryptHistory: false, props: { name: 'KanbanGit', sidebarOpen: true, appearance: 'light', auth: { permissions: authPermissions, user: { id: 1, name: 'Test User', email: 'test@example.test', avatar: null, }, }, navigation: { project: projectContext }, }, }, resolve: () => ({ render: () => h( AppLayout, { breadcrumbs: [ { title: 'Board', href: '/projects/7/board' }, ], }, () => h('h1', {}, 'Content'), ), }), render: renderToString, setup: ({ App, props, plugin }) => createSSRApp({ render: () => h(App, props) }) .use(plugin) .use(localization) .use(createAppearance('light')), }); return response.body; }; const reader = await render('/projects/7/board?filter=mine', project); assert.match( reader, /]*href="\/projects\/7\/board")(?=[^>]*aria-current="page")[^>]*>/, ); assert.doesNotMatch(reader, /href="\/projects\/7\/settings/); assert.doesNotMatch(reader, /href="\/admin\/users"/); assert.doesNotMatch(reader, /href="\/projects\/7\/tickets\/create/); assert.doesNotMatch(reader, /Laravel Starter Kit|vue-starter-kit/); assert.match(reader, /aria-controls="app-navigation"/); const writer = await render('/projects/7/backlog', { ...project, canCreateTickets: true, }); assert.match( writer, /href="\/projects\/7\/tickets\/create\?source=backlog"/, ); assert.doesNotMatch(writer, /href="\/projects\/7\/settings/); const admin = await render('/projects/7/settings/general', { ...project, canManage: true, canCreateTickets: true, }); assert.match( admin, /]*href="\/projects\/7\/settings\/general")(?=[^>]*aria-current="page")[^>]*>/, ); // Render another page in the same process to catch stale module-level Inertia state. const dashboard = await render('/dashboard', null); assert.match( dashboard, /]*href="\/dashboard")(?=[^>]*aria-current="page")[^>]*>/, ); assert.doesNotMatch(dashboard, /href="\/projects\/7/); const translated = await render( '/dashboard', null, permissions, createLocalization( { locale: 'de' }, { de: { ...en, 'navigation.dashboard': 'Übersicht', 'navigation.expand': 'Navigation öffnen', }, }, ), ); assert.match(translated, /Übersicht/); console.log('Navigation behavior checks passed.'); } finally { await server.close(); }