vitest + happy-dom tests of the UI-SPEC backstops: E1 status thresholds (29/30 characters, 580/581 px; 119/120 and 920/921 for descriptions) with a mocked measureText and the counter's wrap contract; the E2 grammar over every directive combination, the emitted values and the stacking contract with unclipped en and pl labels; the E3 effective title, word-boundary truncation and keyphrase stem matching; each E4 lint rule and a clean file; E6 apply gating. happy-dom has no layout engine, so the narrow-width rows check the stylesheet's layout contract.
85 lines
3.7 KiB
TypeScript
85 lines
3.7 KiB
TypeScript
import { afterEach, beforeAll, describe, expect, it } from 'vitest'
|
|
import { applyState, ROW_CAP, shownRows, type PreviewData, type PreviewRow } from '../../assets/src/elements/transfer'
|
|
import { labelsFor } from '../../assets/src/elements/labels'
|
|
|
|
// E6 golem15-seo-transfer (UI-SPEC C13): Apply is enabled only after a
|
|
// preview with no refused row and at least one change.
|
|
|
|
beforeAll(async () => {
|
|
await import('../../assets/src/elements/index')
|
|
})
|
|
|
|
afterEach(() => {
|
|
document.body.replaceChildren()
|
|
})
|
|
|
|
function row(line: number, change: PreviewRow['change']): PreviewRow {
|
|
return { line, change, address: '/a' + line, field: 'target', before: '', after: '/b', note: change === 'refused' ? 'Loop' : '' }
|
|
}
|
|
|
|
function previewData(rows: PreviewRow[]): PreviewData {
|
|
const summary = { new: 0, changed: 0, unchanged: 0, refused: 0, warning: 0 }
|
|
for (const r of rows) {
|
|
summary[r.change]++
|
|
}
|
|
return { op: 'preview', token: 'tok-1', dataset: 'redirects', summary, rows, total: rows.length, count: summary.new + summary.changed }
|
|
}
|
|
|
|
/** A transfer element answered with a preview; returns it and its actions. */
|
|
function screen(data: PreviewData): { node: HTMLElement; actions: unknown[] } {
|
|
const node = document.createElement('golem15-seo-transfer')
|
|
node.setAttribute('ui-locale', 'en')
|
|
const actions: unknown[] = []
|
|
node.addEventListener('summer-action', (ev) => actions.push((ev as CustomEvent).detail.payload))
|
|
document.body.append(node)
|
|
node.dispatchEvent(new CustomEvent('summer-result', { detail: { data } }))
|
|
return { node, actions }
|
|
}
|
|
|
|
function applyButton(node: HTMLElement): HTMLButtonElement {
|
|
const buttons = node.querySelectorAll<HTMLButtonElement>('.seo-transfer__actions button')
|
|
return buttons[buttons.length - 1]
|
|
}
|
|
|
|
describe('E6 apply gating', () => {
|
|
it('one refused row disables Apply and shows refusedBlocks', () => {
|
|
const { node } = screen(previewData([row(2, 'new'), row(3, 'changed'), row(4, 'refused')]))
|
|
expect(applyButton(node).disabled).toBe(true)
|
|
expect(node.textContent).toContain(labelsFor('en').refusedBlocks)
|
|
const first = node.querySelector('tbody tr')
|
|
expect(first?.className).toBe('seo-transfer__tr--refused')
|
|
})
|
|
|
|
it('zero changes disables Apply and shows the nothing note, not refusedBlocks', () => {
|
|
const { node } = screen(previewData([row(2, 'unchanged')]))
|
|
expect(applyButton(node).disabled).toBe(true)
|
|
expect(node.textContent).toContain(labelsFor('en').nothing)
|
|
expect(node.textContent).not.toContain(labelsFor('en').refusedBlocks)
|
|
})
|
|
|
|
it('a clean preview enables Apply, which sends the preview token', () => {
|
|
const { node, actions } = screen(previewData([row(2, 'new'), row(3, 'unchanged')]))
|
|
const apply = applyButton(node)
|
|
expect(apply.disabled).toBe(false)
|
|
expect(node.textContent).not.toContain(labelsFor('en').refusedBlocks)
|
|
apply.click()
|
|
expect(actions).toContainEqual({ op: 'apply', token: 'tok-1' })
|
|
})
|
|
|
|
it('applyState and shownRows', () => {
|
|
expect(applyState(previewData([row(1, 'refused'), row(2, 'new')]))).toBe('refused')
|
|
expect(applyState(previewData([row(1, 'unchanged')]))).toBe('nothing')
|
|
expect(applyState(previewData([row(1, 'changed')]))).toBe('ready')
|
|
const many = [...Array.from({ length: ROW_CAP + 5 }, (_, i) => row(i + 2, 'new')), row(999, 'refused')]
|
|
const shown = shownRows(many)
|
|
expect(shown.length).toBe(ROW_CAP)
|
|
expect(shown[0]).toEqual(row(999, 'refused'))
|
|
})
|
|
|
|
it('a refused preview answer shows its message and no Apply', () => {
|
|
const { node } = screen({ op: 'preview', error: 'stale', message: 'The data changed since the preview.' } as unknown as PreviewData)
|
|
expect(node.textContent).toContain('The data changed since the preview.')
|
|
expect(node.querySelector('.seo-transfer__actions')).toBeNull()
|
|
})
|
|
})
|