- 41 unit and component suites under admin/tests/{app,state,shell,list,form,relation,views,ui}
covering states and a11y roles; every src module is imported by a test
- typed fixture helper assigns each JSON fixture to its generated OpenAPI type
- fix: iconFor ignores inherited object members such as "constructor"
- fix: field controls import ./control instead of the registry (import cycle
left a renderer unregistered depending on module load order)
- fix: dropdown shows the placeholder for an unknown stored value next to an emptyOption
- fix: list announces a failed schema load even when the rows arrive after it
- tailwind no longer scans admin/tests; boardwalk/dist rebuilt
75 lines
2.7 KiB
TypeScript
75 lines
2.7 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { listQueryKey, parseListQuery, toListQuery } from '../../src/app/listQuery'
|
|
|
|
describe('parseListQuery', () => {
|
|
it('reads search, sort, dir, page, per_page and filter[<name>]', () => {
|
|
expect(
|
|
parseListQuery({
|
|
search: ' blue ',
|
|
sort: 'name',
|
|
dir: 'desc',
|
|
page: '3',
|
|
per_page: '50',
|
|
'filter[active]': 'true',
|
|
'filter[created]': '2026-01-01..2026-02-01',
|
|
}),
|
|
).toEqual({
|
|
search: 'blue',
|
|
sort: 'name',
|
|
dir: 'desc',
|
|
page: 3,
|
|
per_page: 50,
|
|
filter: { active: 'true', created: '2026-01-01..2026-02-01' },
|
|
})
|
|
})
|
|
|
|
it.each(['0', '-1', '1.5', 'abc', '', '01', ' 2'])('drops the invalid page and per_page %j', (value) => {
|
|
const out = parseListQuery({ page: value, per_page: value })
|
|
expect(out.page).toBeUndefined()
|
|
expect(out.per_page).toBeUndefined()
|
|
})
|
|
|
|
it('defaults the direction to asc with a sort and ignores a direction without one', () => {
|
|
expect(parseListQuery({ sort: 'name', dir: 'sideways' })).toEqual({ sort: 'name', dir: 'asc' })
|
|
expect(parseListQuery({ dir: 'desc' })).toEqual({})
|
|
expect(parseListQuery({ sort: ' ' })).toEqual({})
|
|
})
|
|
|
|
it('takes the last value of a repeated key and drops empty filters and non-filter keys', () => {
|
|
expect(parseListQuery({ search: ['a', 'b'], 'filter[x]': '', 'filter[]': 'y', filters: 'z', other: '1' })).toEqual({
|
|
search: 'b',
|
|
})
|
|
expect(parseListQuery({ page: ['2', null] })).toEqual({})
|
|
})
|
|
})
|
|
|
|
describe('toListQuery', () => {
|
|
it('writes only meaningful values and omits page 1', () => {
|
|
expect(toListQuery({})).toEqual({})
|
|
expect(toListQuery({ page: 1, search: '' })).toEqual({})
|
|
expect(toListQuery({ page: 2, per_page: 10 })).toEqual({ page: '2', per_page: '10' })
|
|
expect(toListQuery({ sort: 'code' })).toEqual({ sort: 'code', dir: 'asc' })
|
|
expect(toListQuery({ filter: { b: '2', a: '1', c: '' } })).toEqual({ 'filter[a]': '1', 'filter[b]': '2' })
|
|
})
|
|
|
|
it('round-trips through parseListQuery', () => {
|
|
const input = {
|
|
search: 'blue',
|
|
sort: 'name',
|
|
dir: 'desc' as const,
|
|
page: 4,
|
|
per_page: 25,
|
|
filter: { active: 'false', maker: '2' },
|
|
}
|
|
const raw = toListQuery(input)
|
|
const back = parseListQuery(raw as Record<string, string>)
|
|
expect(back).toEqual(input)
|
|
})
|
|
|
|
it('keys equal queries alike regardless of filter order', () => {
|
|
expect(listQueryKey({ filter: { a: '1', b: '2' } })).toBe(listQueryKey({ filter: { b: '2', a: '1' } }))
|
|
expect(listQueryKey({ page: 1 })).toBe(listQueryKey({}))
|
|
expect(listQueryKey({ page: 2 })).not.toBe(listQueryKey({}))
|
|
})
|
|
})
|