import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { flushPromises } from '@vue/test-utils' import { setBundle } from '../../src/app/i18n' import { clearUser } from '../../src/state/useAuth' import { setNavigation } from '../../src/state/useNavigation' import { loadSettings, setSettings } from '../../src/state/useSettings' import { clearToasts } from '../../src/state/useToasts' import lang from '../fixtures/lang.json' import listSchema from '../fixtures/widgets.list-schema.json' import listRows from '../fixtures/widgets.list.json' import settings from '../fixtures/settings.json' import { API, mockApi, mountApp, requestsTo, type Route } from '../helpers' const SETTING = `${API}/settings/search` function routes(overrides: Record = {}): Record { return { [`GET ${API}/settings`]: { body: settings.list }, [`GET ${SETTING}/schema`]: { body: settings.schema }, [`GET ${SETTING}`]: { body: settings.values }, [`GET ${API}/acme/demo/widgets/schema/list`]: { body: listSchema }, [`GET ${API}/acme/demo/widgets`]: { body: listRows }, [`PUT ${SETTING}`]: async (request) => ({ body: { data: { data: { ...settings.values.data.data, ...((await request.json()) as object) }, exists: true }, meta: {} }, }), ...overrides, } } beforeEach(() => { clearUser() setNavigation([]) setSettings([]) clearToasts() setBundle(lang.data, 'pl') }) afterEach(() => { document.body.innerHTML = '' }) describe('settings', () => { it('shows the Ustawienia rail item only when settings pages are permitted', async () => { const { wrapper } = await mountApp('/acme/demo/widgets', routes()) expect(wrapper.find('[data-rail-settings]').exists()).toBe(false) mockApi(routes()) await loadSettings() await flushPromises() const item = wrapper.find('[data-rail-settings]') expect(item.text()).toBe('Ustawienia') expect(item.attributes('href')).toBe('/settings') expect(item.attributes('aria-current')).toBeUndefined() expect(item.find('svg').exists()).toBe(true) }) it('lists every permitted settings page grouped by category', async () => { mockApi(routes()) await loadSettings() const { wrapper } = await mountApp('/settings', routes()) expect(wrapper.find('h1').text()).toBe('Ustawienia') expect(wrapper.find('[data-rail-settings]').attributes('aria-current')).toBe('page') const groups = wrapper.findAll('[data-settings-group]') expect(groups.map((group) => group.find('h2').text())).toEqual(['Appearance', 'System']) expect(groups[1]!.findAll('[data-settings]').map((link) => link.attributes('data-settings'))).toEqual([ 'mail', 'search', ]) const search = wrapper.find('[data-settings="search"]') expect(search.attributes('href')).toBe('/settings/search') expect(search.text()).toContain('Search') expect(search.text()).toContain('Search engine options') expect(search.find('svg').exists()).toBe(true) }) it('renders a settings page through the form registry and PUTs its values', async () => { mockApi(routes()) await loadSettings() const { wrapper, calls } = await mountApp('/settings/search', routes()) expect(wrapper.find('h1').text()).toBe('Search') const toggle = wrapper.find('#setting-use_engine') expect(toggle.attributes('role')).toBe('switch') expect(toggle.attributes('aria-checked')).toBe('false') expect((wrapper.find('#setting-engine_host').element as HTMLInputElement).value).toBe('search.local') await toggle.trigger('click') await wrapper.find('#setting-engine_host').setValue('engine.local') await wrapper.find('[data-action="save"]').trigger('click') await flushPromises() const [put] = requestsTo(calls, 'PUT', SETTING) expect(put!.headers.get('X-Requested-With')).toBe('XMLHttpRequest') // A numeric toggle keeps its type: 0 becomes 1, not true. expect(await put!.json()).toEqual({ use_engine: 1, engine_host: 'engine.local' }) expect(wrapper.find('[role="status"]').text()).toContain('Zapisano ustawienia') expect(wrapper.find('#setting-use_engine').attributes('aria-checked')).toBe('true') }) it('maps a settings 422 onto its fields', async () => { const { wrapper } = await mountApp( '/settings/search', routes({ [`PUT ${SETTING}`]: { status: 422, body: { error: { code: 'validation_failed', message: 'The given data was invalid.', details: { engine_host: ['The engine host format is invalid.'] }, }, }, }, }), { attach: true }, ) await wrapper.find('[data-action="save"]').trigger('click') await flushPromises() expect(wrapper.find('[data-error-banner]').text()).toContain('Popraw 1 pole oznaczone poniżej.') expect(wrapper.find('#setting-engine_host').attributes('aria-invalid')).toBe('true') expect(wrapper.find('#setting-engine_host-error').text()).toBe('The engine host format is invalid.') expect(document.activeElement?.id).toBe('setting-engine_host') }) })