diff --git a/apps/app/src/layouts/__tests__/OrganizerLayout.spec.ts b/apps/app/src/layouts/__tests__/OrganizerLayout.spec.ts
new file mode 100644
index 00000000..fcf2f112
--- /dev/null
+++ b/apps/app/src/layouts/__tests__/OrganizerLayout.spec.ts
@@ -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: '
' },
+}))
+
+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: '' } },
+ },
+ })
+
+ expect(wrapper.find('[data-test="router-view"]').exists()).toBe(true)
+ })
+})
diff --git a/apps/app/src/layouts/__tests__/PortalLayout.spec.ts b/apps/app/src/layouts/__tests__/PortalLayout.spec.ts
new file mode 100644
index 00000000..b818a535
--- /dev/null
+++ b/apps/app/src/layouts/__tests__/PortalLayout.spec.ts
@@ -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: '
' },
+ VAppBar: { template: '' },
+ VMain: { template: '' },
+ VFooter: { template: '' },
+}
+
+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: '' },
+ },
+ },
+ })
+
+ expect(wrapper.find('main [data-test="router-view"]').exists()).toBe(true)
+ })
+})
diff --git a/apps/app/src/layouts/__tests__/PublicLayout.spec.ts b/apps/app/src/layouts/__tests__/PublicLayout.spec.ts
new file mode 100644
index 00000000..297151f7
--- /dev/null
+++ b/apps/app/src/layouts/__tests__/PublicLayout.spec.ts
@@ -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: '
' },
+ VMain: { template: '' },
+}
+
+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: '' },
+ },
+ },
+ })
+
+ expect(wrapper.find('main [data-test="router-view"]').exists()).toBe(true)
+ })
+})