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 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 03:48:43 +02:00
parent ade64b5fb1
commit 31e79f79c3
2 changed files with 113 additions and 3 deletions

View File

@@ -41,8 +41,9 @@ vi.mock('@vueuse/core', () => ({
// ---------------------------------------------------------------------------
const globalStubs = {
// Icon renders nothing; we just need the collapse button to be present
Icon: { template: '<span class="icon-stub" />' },
// Icon stub exposes `name` via data-icon so tests can assert which glyph
// renders (e.g. the mobile close X vs the desktop collapse chevron).
Icon: { props: ['name', 'size'], template: '<span class="icon-stub" :data-icon="name" />' },
}
function mountHeader() {
@@ -194,6 +195,51 @@ describe('SidebarHeader', () => {
expect(btn.attributes('aria-label')).toBe('Collapse sidebar')
})
// -------------------------------------------------------------------------
// MOBILE-SHELL-PARITY — mobile close control (defect 2) + always-expanded
// header (defect 1)
// -------------------------------------------------------------------------
it('mobile: brand-row control is an explicit close (aria-label "Sluit menu", X icon)', () => {
mockIsMobileRef.value = true
const wrapper = mountHeader()
const btn = wrapper.find('button[aria-label]')
expect(btn.attributes('aria-label')).toBe('Sluit menu')
expect(btn.find('.icon-stub').attributes('data-icon')).toBe('tabler-x')
})
it('desktop: brand-row control stays the collapse chevron (aria-label "Collapse sidebar", chevron icon)', () => {
mockIsMobileRef.value = false
const wrapper = mountHeader()
const btn = wrapper.find('button[aria-label]')
expect(btn.attributes('aria-label')).toBe('Collapse sidebar')
expect(btn.find('.icon-stub').attributes('data-icon')).toBe('tabler-chevron-left')
})
it('mobile: header renders EXPANDED even when sidebarCollapsed is true (logo parity)', async () => {
mockIsMobileRef.value = true
const wrapper = mountHeader()
const shell = useShellUiStore()
shell.sidebarCollapsed = true
await wrapper.vm.$nextTick()
// Expanded brand row: wordmark present, exactly one control (the close X),
// and NO collapsed-state expand-chevron row.
expect(wrapper.find('.brand-name').exists()).toBe(true)
expect(wrapper.text()).toContain('Crewli')
const buttons = wrapper.findAll('button[aria-label]')
expect(buttons).toHaveLength(1)
expect(buttons[0].attributes('aria-label')).toBe('Sluit menu')
})
// P6-styling-fix — the brand row is ALWAYS left-aligned with constant
// px-4 padding (no justify-content switch on collapse). The logo's
// horizontal position is therefore identical in expanded vs collapsed