Files
sm-seo-plugin/tests/elements/transfer.test.ts
Jakub Zych dcfbdc05ed test: element backstops
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.
2026-10-27 13:26:59 +01:00

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()
})
})