test(app): smoke tests for three layout skeletons

WS-3 session 1a Task 3.

Vitest covers each layout with: (1) it mounts without throwing,
(2) it renders the expected DOM structure (top-bar/main/footer for
PortalLayout, none for PublicLayout, slot-passthrough for
OrganizerLayout), (3) it places <RouterView /> in the right region.

Vuetify components (VApp/VAppBar/VMain/VFooter) are stubbed to their
semantic HTML equivalents so the structural assertions still hold
without pulling vuetify/components into the trimmed-down vitest
config (which lacks the CSS plugin needed to transform Vuetify's
.css side effects). OrganizerLayout uses vi.mock to short-circuit
the DefaultLayoutWithVerticalNav import for the same reason.

Vitest count: 41 -> 49 in apps/app.
This commit is contained in:
2026-04-29 08:46:36 +02:00
parent 99c5695db9
commit 39c1332a00
3 changed files with 130 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
import { describe, expect, it, vi } from 'vitest'
import { mount } from '@vue/test-utils'
// Mock the import path so OrganizerLayout's `import DefaultLayoutWithVerticalNav`
// statement doesn't pull in the real component (which transitively imports
// Vuetify .css files that the trimmed-down vitest config can't transform).
vi.mock('@/layouts/components/DefaultLayoutWithVerticalNav.vue', () => ({
default: { template: '<div><slot /></div>' },
}))
const OrganizerLayout = (await import('../OrganizerLayout.vue')).default
describe('OrganizerLayout', () => {
it('mounts without throwing', () => {
const wrapper = mount(OrganizerLayout, {
global: {
stubs: { RouterView: true },
},
})
expect(wrapper.exists()).toBe(true)
})
it('renders a RouterView in its slot', () => {
const wrapper = mount(OrganizerLayout, {
global: {
stubs: { RouterView: { template: '<div data-test="router-view" />' } },
},
})
expect(wrapper.find('[data-test="router-view"]').exists()).toBe(true)
})
})

View File

@@ -0,0 +1,50 @@
import { describe, expect, it } from 'vitest'
import { mount } from '@vue/test-utils'
import PortalLayout from '../PortalLayout.vue'
// Stub Vuetify components as their semantic HTML equivalents so we can
// assert structure without pulling vuetify/components (which loads .css
// files that the trimmed-down vitest config can't transform).
const vuetifyStubs = {
VApp: { template: '<div><slot /></div>' },
VAppBar: { template: '<header><slot /></header>' },
VMain: { template: '<main><slot /></main>' },
VFooter: { template: '<footer><slot /></footer>' },
}
describe('PortalLayout', () => {
it('mounts without throwing', () => {
const wrapper = mount(PortalLayout, {
global: {
stubs: { ...vuetifyStubs, RouterView: true },
},
})
expect(wrapper.exists()).toBe(true)
})
it('renders a top app bar, main region, and footer', () => {
const wrapper = mount(PortalLayout, {
global: {
stubs: { ...vuetifyStubs, RouterView: true },
},
})
expect(wrapper.find('header').exists()).toBe(true)
expect(wrapper.find('main').exists()).toBe(true)
expect(wrapper.find('footer').exists()).toBe(true)
})
it('renders a RouterView inside the main region', () => {
const wrapper = mount(PortalLayout, {
global: {
stubs: {
...vuetifyStubs,
RouterView: { template: '<div data-test="router-view" />' },
},
},
})
expect(wrapper.find('main [data-test="router-view"]').exists()).toBe(true)
})
})

View File

@@ -0,0 +1,47 @@
import { describe, expect, it } from 'vitest'
import { mount } from '@vue/test-utils'
import PublicLayout from '../PublicLayout.vue'
// Stub Vuetify components as their semantic HTML equivalents so we can
// assert structure without pulling vuetify/components (which loads .css
// files that the trimmed-down vitest config can't transform).
const vuetifyStubs = {
VApp: { template: '<div><slot /></div>' },
VMain: { template: '<main><slot /></main>' },
}
describe('PublicLayout', () => {
it('mounts without throwing', () => {
const wrapper = mount(PublicLayout, {
global: {
stubs: { ...vuetifyStubs, RouterView: true },
},
})
expect(wrapper.exists()).toBe(true)
})
it('does NOT render a header or footer (intentionally minimal)', () => {
const wrapper = mount(PublicLayout, {
global: {
stubs: { ...vuetifyStubs, RouterView: true },
},
})
expect(wrapper.find('header').exists()).toBe(false)
expect(wrapper.find('footer').exists()).toBe(false)
})
it('renders a RouterView inside main', () => {
const wrapper = mount(PublicLayout, {
global: {
stubs: {
...vuetifyStubs,
RouterView: { template: '<div data-test="router-view" />' },
},
},
})
expect(wrapper.find('main [data-test="router-view"]').exists()).toBe(true)
})
})