From 31e79f79c34517993068684c0b86d52e551b966e Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Wed, 3 Jun 2026 03:48:43 +0200 Subject: [PATCH] test: mobile drawer parity contract (close control, expanded header, height) MOBILE-SHELL-PARITY. Adds 7 Vitest assertions for the three defects: - AppSidebar: header pt is '!hidden' (default close-X suppressed), content pt is a full-height flex column (flex-col/h-full/min-h-0), showCloseIcon is not forced false, and WorkspaceSwitcher renders inside the drawer. - SidebarHeader: the Icon stub now exposes data-icon; mobile brand-row control is an explicit close (aria-label 'Sluit menu', tabler-x), desktop stays the collapse chevron, and the header renders expanded on mobile even when sidebarCollapsed is true (logo parity). Co-Authored-By: Claude Opus 4.8 --- .../layout/__tests__/AppSidebar.spec.ts | 66 ++++++++++++++++++- .../layout/__tests__/SidebarHeader.spec.ts | 50 +++++++++++++- 2 files changed, 113 insertions(+), 3 deletions(-) diff --git a/apps/app/src/components-v2/layout/__tests__/AppSidebar.spec.ts b/apps/app/src/components-v2/layout/__tests__/AppSidebar.spec.ts index 399c68c8..d23ebcda 100644 --- a/apps/app/src/components-v2/layout/__tests__/AppSidebar.spec.ts +++ b/apps/app/src/components-v2/layout/__tests__/AppSidebar.spec.ts @@ -56,7 +56,7 @@ vi.mock('@vueuse/core', () => ({ */ const DrawerStub = { name: 'Drawer', - props: ['visible', 'position', 'pt'], + props: ['visible', 'position', 'pt', 'showCloseIcon'], emits: ['update:visible'], template: '
', } @@ -138,6 +138,70 @@ describe('AppSidebar', () => { expect(wrapper.find('.drawer-stub').exists()).toBe(true) }) + // ------------------------------------------------------------------------- + // MOBILE-SHELL-PARITY — drawer chrome pt contract + // ------------------------------------------------------------------------- + + it('mobile: drawer force-hides the PrimeVue default header (!hidden) so the default close-X cannot overlap', async () => { + mockIsMobileRef.value = true + + const wrapper = mountSidebar() + + await wrapper.vm.$nextTick() + + const pt = wrapper.findComponent(DrawerStub).props('pt') as { + header: { class: string } + content: { class: string } + } + + // defect 2: PrimeVue's default header (which holds the default close-X) + // is suppressed with the important variant so base styles cannot win. + expect(pt.header.class).toContain('!hidden') + }) + + it('mobile: drawer content is a full-height flex column (WorkspaceSwitcher bottom-anchor)', async () => { + mockIsMobileRef.value = true + + const wrapper = mountSidebar() + + await wrapper.vm.$nextTick() + + const pt = wrapper.findComponent(DrawerStub).props('pt') as { + content: { class: string } + } + + // defect 3: full-height flex column so SidebarNav claims the slack and + // WorkspaceSwitcher (flex-shrink-0) anchors at the bottom. + expect(pt.content.class).toContain('flex-col') + expect(pt.content.class).toContain('h-full') + expect(pt.content.class).toContain('min-h-0') + }) + + it('mobile: drawer does NOT suppress the close affordance via showCloseIcon=false', async () => { + mockIsMobileRef.value = true + + const wrapper = mountSidebar() + + await wrapper.vm.$nextTick() + + // The mobile close control lives in SidebarHeader's brand row; the Drawer + // is left at its default showCloseIcon (never forced to false). + expect(wrapper.findComponent(DrawerStub).props('showCloseIcon')).not.toBe(false) + }) + + it('mobile: WorkspaceSwitcher renders inside the drawer', async () => { + mockIsMobileRef.value = true + + const wrapper = mountSidebar() + + await wrapper.vm.$nextTick() + + const drawer = wrapper.find('.drawer-stub') + + expect(drawer.exists()).toBe(true) + expect(drawer.find('.workspace-switcher-stub').exists()).toBe(true) + }) + // ------------------------------------------------------------------------- // Desktop