Files
summercms/admin/tests/list/RowStateBadges.test.ts
Jakub Zych 84efdc09e0 test(12.1-05): unit tests for the Phase 12.1 SPA components
- BulkActionsMenu, RowStateBadges, RecordActions, PreviewField, FormErrorBanner, PasswordField and PermissionEditorField, each mounted on its own
- backstop: a row state outside the fixed set renders no badge and no class
- backstop: the permission editor's emission rules per mode, the locked row and codes outside the options
2026-10-05 15:29:44 +02:00

69 lines
3.3 KiB
TypeScript

// Row state badges (UI-SPEC S4, D-12). Fixtures are neutral; no application
// names appear in framework tests.
import { describe, expect, it } from 'vitest'
import { mount } from '@vue/test-utils'
import RowStateBadges from '../../src/components/list/RowStateBadges.vue'
const labels = { deleted: 'Deleted', negative: 'Blocked', disabled: 'Not active' }
const badges = (states: readonly string[], use: Record<string, string> = labels) =>
mount(RowStateBadges, { props: { states, labels: use } })
describe('row state badges (UI-SPEC S4, D-12)', () => {
it.each([
['deleted', 'Deleted', ['border', 'border-border-strong', 'text-muted']],
['negative', 'Blocked', ['bg-danger-soft', 'text-danger']],
['disabled', 'Not active', ['bg-subtle', 'text-muted']],
])('renders %s as a text badge with its own classes', (state, text, classes) => {
const wrapper = badges([state])
const all = wrapper.findAll('[data-row-state]')
expect(all).toHaveLength(1)
expect(all[0]!.attributes('data-row-state')).toBe(state)
expect(all[0]!.text()).toBe(text)
expect(all[0]!.classes()).toEqual(expect.arrayContaining([...classes, 'rounded-pill', 'whitespace-nowrap', 'shrink-0']))
// The state is said in text: colour alone would fail WCAG 1.4.1.
expect(all[0]!.classes()).not.toContain('sr-only')
expect(all[0]!.classes()).not.toContain('line-through')
})
it('renders combined states in the fixed order deleted, negative, disabled', () => {
for (const input of [
['disabled', 'negative', 'deleted'],
['negative', 'deleted', 'disabled'],
['deleted', 'negative', 'disabled'],
]) {
const wrapper = badges(input)
expect(wrapper.findAll('[data-row-state]').map((badge) => badge.attributes('data-row-state'))).toEqual(['deleted', 'negative', 'disabled'])
}
expect(badges(['disabled', 'deleted']).findAll('[data-row-state]').map((badge) => badge.text())).toEqual(['Deleted', 'Not active'])
})
it('renders a repeated state once', () => {
const wrapper = badges(['negative', 'negative', 'negative'])
expect(wrapper.findAll('[data-row-state]')).toHaveLength(1)
})
it('renders nothing at all without states', () => {
const wrapper = badges([])
expect(wrapper.find('span').exists()).toBe(false)
expect(wrapper.text()).toBe('')
})
it('falls back to the state name when the schema sends no label for it', () => {
expect(badges(['negative'], {}).find('[data-row-state]').text()).toBe('negative')
})
it('backstop: a row state outside the fixed set renders no badge and no class', () => {
const hostile = ['starred', 'bg-danger-soft', 'deleted line-through', '<b>x</b>', 'DELETED', '__proto__', 'constructor', '']
const wrapper = badges(hostile, { ...labels, starred: 'Starred', constructor: 'Constructor' })
expect(wrapper.find('[data-row-state]').exists()).toBe(false)
expect(wrapper.find('span').exists()).toBe(false)
expect(wrapper.html()).not.toMatch(/starred|bg-danger-soft|line-through|Starred|Constructor/)
// Next to known states the unknown ones change nothing.
const mixed = badges(['starred', 'negative', '<b>x</b>'])
expect(mixed.findAll('[data-row-state]').map((badge) => [badge.attributes('data-row-state'), badge.text()])).toEqual([['negative', 'Blocked']])
expect(mixed.html()).not.toContain('starred')
expect(mixed.find('b').exists()).toBe(false)
})
})