import { beforeEach, describe, expect, it } from 'vitest' import { hasSettings, loadSettings, setSettings, settings, settingsEntry, settingsGroups, settingsLinkFor, settingsPath, settingsSectionOf, useSettings, } from '../../src/state/useSettings' import { settingsFixture } from '../fixtures/typed' import { API, mockApi, resetState } from '../helpers' beforeEach(() => { resetState() }) describe('useSettings (D-21)', () => { it('is hidden until at least one settings page is permitted', async () => { expect(hasSettings.value).toBe(false) mockApi({ [`GET ${API}/settings`]: { body: settingsFixture.list } }) expect(await loadSettings()).toHaveLength(3) expect(hasSettings.value).toBe(true) expect(useSettings().settings).toBe(settings) }) it('hides settings again when the request fails', async () => { setSettings(settingsFixture.list.data) mockApi({ [`GET ${API}/settings`]: { status: 403, body: { error: { code: 'forbidden', message: 'x', details: {} } } } }) expect(await loadSettings()).toEqual([]) expect(hasSettings.value).toBe(false) }) it('finds an entry by code', () => { setSettings(settingsFixture.list.data) expect(settingsEntry('mail')?.label).toBe('Mail') expect(settingsEntry('nope')).toBeNull() }) it('groups by category in first-seen order after sorting by order', () => { setSettings(settingsFixture.list.data) expect(settingsGroups.value.map((group) => [group.category, group.entries.map((entry) => entry.code)])).toEqual([ ['Appearance', ['branding']], ['System', ['mail', 'search']], ]) }) it('links a singleton to its form and a controller link to the controller route', () => { const singleton = settingsFixture.list.data[0]! expect(settingsPath({ ...singleton, code: 'a b' })).toBe('/settings/a%20b') expect(settingsPath({ ...singleton, code: 'locales', model: '', controller: 'acme.demo.locales' })).toBe('/acme/demo/locales') expect(settingsPath({ ...singleton, code: 'broken', model: '', controller: 'acme.demo' })).toBe('/settings') expect(useSettings().settingsPath).toBe(settingsPath) }) it('finds the link entry that opens a controller, never a singleton', () => { const link = { ...settingsFixture.list.data[0]!, code: 'locales', model: '', controller: 'acme.demo.locales' } setSettings([...settingsFixture.list.data, link]) expect(settingsLinkFor('acme.demo.locales')?.code).toBe('locales') expect(settingsLinkFor('acme.demo.widgets')).toBeNull() expect(settingsLinkFor('')).toBeNull() expect(settingsLinkFor(null)).toBeNull() expect(useSettings().settingsLinkFor).toBe(settingsLinkFor) }) }) describe('settingsSectionOf (quick-261022-imo)', () => { const link = (code: string, order: number, controller: string) => ({ ...settingsFixture.list.data[0]!, code, label: code, model: '', controller, order, }) it('is null without a current entry or for a one-entry category', () => { setSettings(settingsFixture.list.data) expect(settingsSectionOf(null)).toBeNull() expect(settingsSectionOf(settingsEntry('branding'))).toBeNull() }) it('returns the current category in settingsGroups order', () => { setSettings(settingsFixture.list.data) const section = settingsSectionOf(settingsEntry('mail')) expect(section?.category).toBe('System') expect(section?.current.code).toBe('mail') expect(section?.entries.map((entry) => entry.code)).toEqual(['mail', 'search']) }) it('places a link entry by its order and never lists another category', () => { setSettings([...settingsFixture.list.data, link('locales', 15, 'acme.lang.locales')]) const section = settingsSectionOf(settingsLinkFor('acme.lang.locales')) expect(section?.current.code).toBe('locales') expect(section?.entries.map((entry) => entry.code)).toEqual(['mail', 'locales', 'search']) expect(section?.entries.some((entry) => entry.category === 'Appearance')).toBe(false) expect(settingsSectionOf(settingsEntry('search'))?.entries.map((entry) => entry.code)).not.toContain('branding') }) it('is exposed through useSettings', () => { expect(useSettings().settingsSectionOf).toBe(settingsSectionOf) }) })