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