/** * Side card settings section render tests: the section is DECLARATIVE — * every small card (icon, title, type id, extensions, on/off state) derives * from the sidebar service's tab/viewer registries instead of hardcoded * copy. The toggles are CARDS in a responsive grid: the card's main area is * the switch, the visual state IS the state (highlighted = enabled), * announced via `aria-pressed`, and the check badge sits at the far right. * The general rows follow the DSH settings-row recipe with custom SWITCHES * (real checkboxes driving a styled track). Features that declare related * settings carry a gear corner button whose popup rows (switch controls) * are tested through the extracted FeatureSettingsRows component (the Modal * portal renders only while open). * * Rendered with renderToString (mount effects — the settings RPC sync — do * not run in SSR; the initial store prefs are the render input). */ import { describe, expect, it } from 'vitest' import { renderToString } from 'react-dom/server' import { createElement } from 'react' import { createSidebarStore, type SidebarStore } from '../src/client/state.ts' import { createBetterSidebarService, type BetterSidebarService } from '../src/client/service.ts' import { SIDEBAR_PREFS_DEFAULTS } from '../src/prefs-shared.ts' import { FeatureSettingsRows, SideCardSection, type SideCardSectionProps } from '../src/client/SideCardSection.tsx' /** One tab + one viewer + the subagent-style nested toggle under a tab. */ function mount(): { store: SidebarStore; service: BetterSidebarService } { const store = createSidebarStore() const service = createBetterSidebarService(store) service.registerTab({ id: 'explorer', title: () => 'Explorer', icon: () => createElement('svg', { 'data-icon': 'explorer' }), order: 10, component: () => null, }) service.registerTab({ id: 'subagent', title: () => 'Subagents', icon: () => createElement('svg', { 'data-icon': 'subagent' }), order: 30, settings: { toggles: [{ key: 'autoOpenSubagent', title: () => 'Auto-open Subagents', desc: () => 'Expand on new subagent', }], }, component: () => null, }) service.registerFileViewer({ id: 'image', title: () => 'Image', icon: () => createElement('svg', { 'data-icon': 'image' }), exts: ['png', 'jpg'], fetchStrategy: 'mediaUrl', component: () => null, }) return { store, service } } function renderSection(store: SidebarStore, service: BetterSidebarService): string { return renderToString(createElement( SideCardSection, { store, service } as unknown as SideCardSectionProps, )) } /** Count `aria-pressed` occurrences of one value in the rendered HTML. */ function pressedCount(html: string, value: string): number { return html.match(new RegExp(`aria-pressed="${value}"`, 'g'))?.length ?? 0 } describe('SideCardSection declarative inventory', () => { it('renders one small card per registered tab: icon + title + type id + pressed state', () => { const { store, service } = mount() const html = renderSection(store, service) expect(html).toContain('data-icon="explorer"') expect(html).toContain('>Explorer<') // The type id is the card's desc (the declarative "type" surface). expect(html).toContain('>explorer<') expect(html).toContain('data-icon="subagent"') expect(html).toContain('>Subagents<') // Default prefs: openByDefault + interceptOpenPath switches checked, and // both tabs + the image viewer cards pressed (3 aria-pressed cards). // The nested auto-open toggle is NOT an inline card (it lives in the popup). expect(pressedCount(html, 'true')).toBe(3) expect(pressedCount(html, 'false')).toBe(0) // The general toggles are custom switches (real checkboxes, checked). expect(html.match(/checked=""/g)?.length).toBe(2) expect(html).not.toContain('Auto-open Subagents') }) it('renders the section intro and group headings with inventory counts', () => { const { store, service } = mount() const html = renderSection(store, service) expect(html).toContain('Manage what the side card shows and how it behaves') // Group headings carry the inventory count badge (2 tabs, 1 viewer). expect(html).toContain('>Sidebar contentFile viewers2') expect(html).toContain('>1') }) it('renders one small card per registered viewer: icon + title + exts', () => { const { store, service } = mount() const html = renderSection(store, service) expect(html).toContain('data-icon="image"') expect(html).toContain('>Image<') // The covered extensions are the card's desc. expect(html).toContain('png · jpg') }) it('renders the gear corner button on features that declare related settings', () => { const { store, service } = mount() const html = renderSection(store, service) // Subagents declares a toggle → its card carries the settings gear // (aria-label = "