| 1 | // fixtures/sizes.test.mjs — the font-size vocabulary, under plain Node. |
| 2 | // |
| 3 | // fonts.test.mjs's counterpart. Importing this module is also what makes |
| 4 | // sizes.mjs's own module-load guard run in the gate — the same |
| 5 | // arrangement align.test.mjs and fonts.test.mjs have, and for the reason |
| 6 | // their comments give at length: a guard that only executes when a |
| 7 | // browser mounts the editor is dead code in CI, so a drifted vocabulary |
| 8 | // would pass the whole gate and then throw for every user. |
| 9 | |
| 10 | import { test } from 'node:test' |
| 11 | import assert from 'node:assert/strict' |
| 12 | |
| 13 | import { |
| 14 | SIZES, |
| 15 | DEFAULT_SIZE, |
| 16 | SIZE_MARKS, |
| 17 | LEGACY_SIZES, |
| 18 | sizeMark, |
| 19 | sizeForMarks, |
| 20 | } from './sizes.mjs' |
| 21 | |
| 22 | test('the default is offered, is first, and carries no mark', () => { |
| 23 | assert.ok(SIZES.includes(DEFAULT_SIZE)) |
| 24 | assert.equal(SIZES[0], DEFAULT_SIZE) |
| 25 | // The literal null is the assertion, not a derivation of sizeMark's |
| 26 | // own definition. fonts.mjs's guard has a long comment about an |
| 27 | // assertion that compared a value to itself and could not fail for |
| 28 | // any input; this is where that expectation belongs — in a test, |
| 29 | // against a literal. |
| 30 | assert.equal(sizeMark(DEFAULT_SIZE), null) |
| 31 | assert.equal(sizeMark(12), null) |
| 32 | }) |
| 33 | |
| 34 | test('every other size maps to its own mark', () => { |
| 35 | assert.equal(sizeMark(8), 'size_8') |
| 36 | assert.equal(sizeMark(14), 'size_14') |
| 37 | assert.equal(sizeMark(72), 'size_72') |
| 38 | }) |
| 39 | |
| 40 | test('a size string works, because that is what a <select> gives you', () => { |
| 41 | assert.equal(sizeMark('14'), 'size_14') |
| 42 | assert.equal(sizeMark('12'), null) |
| 43 | }) |
| 44 | |
| 45 | test('a size this build does not offer is undefined, never null', () => { |
| 46 | // undefined and null are different answers: null clears the group, |
| 47 | // which is a real edit. A stale page sending 13 must not silently |
| 48 | // reset a person's text to 12. |
| 49 | assert.equal(sizeMark(13), undefined) |
| 50 | assert.equal(sizeMark(0), undefined) |
| 51 | assert.equal(sizeMark('enormous'), undefined) |
| 52 | assert.equal(sizeMark(''), undefined) |
| 53 | }) |
| 54 | |
| 55 | test('no marks means the default size', () => { |
| 56 | assert.equal(sizeForMarks([]), DEFAULT_SIZE) |
| 57 | assert.equal(sizeForMarks(['strong', 'em']), DEFAULT_SIZE) |
| 58 | }) |
| 59 | |
| 60 | test('a mark reports its own size', () => { |
| 61 | assert.equal(sizeForMarks(['size_18']), 18) |
| 62 | assert.equal(sizeForMarks(['strong', 'size_9', 'em']), 9) |
| 63 | }) |
| 64 | |
| 65 | // The legacy marks are the reason this module has a mapping at all, and |
| 66 | // the reason the control could not simply slice the prefix off a mark |
| 67 | // name the way it used to. |
| 68 | test('the four-step scale still reports a size on the ladder', () => { |
| 69 | assert.equal(sizeForMarks(['size_sm']), 10) |
| 70 | assert.equal(sizeForMarks(['size_lg']), 14) |
| 71 | assert.equal(sizeForMarks(['size_xl']), 18) |
| 72 | for (const pt of Object.values(LEGACY_SIZES)) { |
| 73 | assert.ok(SIZES.includes(pt), `${pt}pt is not on the ladder, so the control could not show it`) |
| 74 | } |
| 75 | }) |
| 76 | |
| 77 | test('the legacy marks are in the group the toolbar clears', () => { |
| 78 | // If they were not, choosing a size on a run carrying an old mark |
| 79 | // would leave it in place and the run would be at two sizes at once — |
| 80 | // which the schema forbids and POST /d/{sqid}/updates does not. |
| 81 | for (const mark of Object.keys(LEGACY_SIZES)) { |
| 82 | assert.ok(SIZE_MARKS.includes(mark), `${mark} is not cleared when a size is chosen`) |
| 83 | } |
| 84 | }) |
| 85 | |
| 86 | test('the ladder wins over a legacy mark on the same run', () => { |
| 87 | // Impossible through the editor (the schema's excludes), reachable |
| 88 | // through POST /d/{sqid}/updates. The answer has to be stable rather |
| 89 | // than whichever was found first, and internal/export/pdf.go's |
| 90 | // sizeOrder makes the same choice so the screen and the PDF agree. |
| 91 | assert.equal(sizeForMarks(['size_lg', 'size_24']), 24) |
| 92 | assert.equal(sizeForMarks(['size_24', 'size_lg']), 24) |
| 93 | }) |
| 94 | |