@bongos/core 1.19.446 → 1.19.448

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,153 @@
1
+ // tests/hall_board_world.mjs — the Work Board + Home in the chrome world
2
+ // (task 1003304, goal 1000081).
3
+ //
4
+ // tests/hall_tokens.mjs pins the TIER these two pages spend; this pins how they
5
+ // spend it. Everything here is a fact that is silent in a browser: the page
6
+ // still renders, it just renders wrong, or it renders right on one machine and
7
+ // fails a floor on the reviewer's. Each assertion is something the re-skin
8
+ // measured and would otherwise have to re-measure by hand every time.
9
+ //
10
+ // What it locks:
11
+ // * THE CARD-IN-CARD. In v3 a claimable task IS a --bg-card panel, so the
12
+ // section around the list has to be .scroll--bare. Put the .scroll back and
13
+ // the board silently returns to a stack of boxes inside a box, which is
14
+ // exactly the treatment the owner's pick removed — and nothing errors.
15
+ // * THE 24px FLOOR ON BOTH AXES. The two checkboxes were 15px controls; they
16
+ // are now 24px hit areas with a 14px drawn box, and the prose links buy
17
+ // their width with padding and give it back with a negative margin. A
18
+ // later "tidy" that drops the ::before box or the negative margin puts the
19
+ // floor back under, and only the kit render would catch it.
20
+ // * WHITE ON THE SALMON. DESIGN.md bans it outright (it fails AA); the claim
21
+ // button carried a literal `#fff` until this task. It is the single most
22
+ // likely thing to be pasted back from another codebase.
23
+ // * THE COMPAT ALIASES. work.css's whole removal list. The aliases still
24
+ // EXIST for the pages nobody has re-skinned, so spending one here is legal
25
+ // CSS that quietly opts this sheet back out of the world.
26
+ // * THE ONE h1. work.html had none — the hall predates the floor — so the
27
+ // page failed one-header-main-h1 on all 36 width x mode x state renders.
28
+ //
29
+ // Reproduce the renders these came from:
30
+ // node modules/ui-design/kit/render.js --states modules/hall-ui/public/work.states.json
31
+ // node modules/ui-design/kit/render.js --states modules/hall-ui/public/index.states.json
32
+
33
+ import { test } from 'node:test';
34
+ import assert from 'node:assert/strict';
35
+ import fs from 'node:fs';
36
+ import path from 'node:path';
37
+ import { fileURLToPath } from 'node:url';
38
+
39
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
40
+ const HALL = path.join(ROOT, 'modules', 'hall-ui', 'public');
41
+ const read = (f) => fs.readFileSync(path.join(HALL, f), 'utf8');
42
+
43
+ const styleCss = read('style.css');
44
+ const workCss = read('work.css');
45
+ const workHtml = read('work.html');
46
+
47
+ // The body of the first rule matching `selector {`, comments stripped. Naive on
48
+ // purpose: these are flat declaration blocks, not nested at-rules. Takes a PLAIN
49
+ // selector and escapes it here — pass a pre-escaped one and it is escaped twice,
50
+ // which matches a literal backslash and silently finds nothing.
51
+ const rule = (css, selector) => {
52
+ const esc = selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
53
+ const re = new RegExp(`(^|[},])\\s*${esc}\\s*\\{([^}]*)\\}`, 'm');
54
+ const m = css.replace(/\/\*[\s\S]*?\*\//g, '').match(re);
55
+ return m ? m[2] : null;
56
+ };
57
+
58
+ // ---- 1. the card is the panel, so the section must not be one ---------------
59
+
60
+ test('the Ready queue is BARE — a task card inside a .scroll is a card in a card', () => {
61
+ const m = workHtml.match(/<section[^>]*id="claim-scroll"[^>]*>/);
62
+ assert.ok(m, 'work.html still has the #claim-scroll section');
63
+ assert.match(m[0], /class="[^"]*\bscroll--bare\b/,
64
+ 'v3 puts claimable tasks on the ground as --bg-card cards; a .scroll around them paints a card inside a card');
65
+ });
66
+
67
+ test('.task IS the panel — --bg-card at the full radius, with no drawn edge at rest', () => {
68
+ const t = rule(styleCss, '.task');
69
+ assert.ok(t, '.task has a rule');
70
+ assert.match(t, /background:\s*var\(--bg-card\)/, 'the card is the panel tone');
71
+ assert.match(t, /border-radius:\s*var\(--radius\)/, 'the full radius, not --radius-sm');
72
+ assert.match(t, /border:\s*var\(--line\)\s+solid\s+transparent/,
73
+ 'the edge is transparent at rest and reserved so hover does not reflow the row');
74
+ });
75
+
76
+ // ---- 2. the 24px floor, on BOTH axes ---------------------------------------
77
+
78
+ // The floor is `width < 24 || height < 24`, and the width half is the one that
79
+ // gets missed: "Git" came back 17x24 after a height-only fix.
80
+ for (const sel of ['.task__select', '.wip__select']) {
81
+ test(`${sel} is a 24px target with a 14px drawn box`, () => {
82
+ const css = sel.includes('wip') ? workCss : styleCss;
83
+ const box = rule(css, sel);
84
+ assert.ok(box, `${sel} has a rule`);
85
+ assert.match(box, /width:\s*24px/, 'the HIT AREA is 24px — this is what the floor measures');
86
+ assert.match(box, /appearance:\s*none/, 'the native box has to go, or sizing it up sizes the glyph up too');
87
+ const before = rule(css, `${sel}::before`);
88
+ assert.ok(before, `${sel}::before draws the box the viewer actually sees`);
89
+ assert.match(before, /width:\s*14px/, 'the drawn box stays 14px inside the 24px target');
90
+ });
91
+ }
92
+
93
+ test('a prose link buys BOTH axes — padding for width, negative margin to give it back', () => {
94
+ const body = styleCss.replace(/\/\*[\s\S]*?\*\//g, '')
95
+ .match(/\.scroll__lede a,[\s\S]*?\{([^}]*)\}/);
96
+ assert.ok(body, 'the shared prose-link floor rule is still keyed on .scroll__lede a');
97
+ assert.match(body[1], /min-width:\s*24px/, 'width is half the floor — "Git" is 17px wide and fails without it');
98
+ assert.match(body[1], /min-height:\s*24px/, 'height is the other half');
99
+ assert.match(body[1], /margin:\s*0\s+-4px/,
100
+ 'the negative margin hands the padding back to the line, so the sentence does not re-space');
101
+ });
102
+
103
+ // ---- 3. the world's own bans ------------------------------------------------
104
+
105
+ test('nothing on the board paints text white on the accent', () => {
106
+ const claim = rule(workCss, '.task__claim');
107
+ assert.ok(claim, '.task__claim has a rule');
108
+ assert.match(claim, /background:\s*var\(--accent\)/, 'the claim button is the page\'s one accent fill');
109
+ assert.match(claim, /color:\s*var\(--accent-on\)/,
110
+ '--accent-on is the dark ink for the salmon; white on it fails AA and DESIGN.md bans it');
111
+ // Match white as a VALUE, not anywhere in the block: `white-space: nowrap` is
112
+ // a property name and read as a colour by a bare /\bwhite\b/.
113
+ assert.doesNotMatch(claim, /:\s*(#fff\b|#ffffff\b|white\b)/i,
114
+ 'the literal this task removed must not come back');
115
+ });
116
+
117
+ test('work.css spends no compat alias — that was the sheet\'s removal list', () => {
118
+ const aliases = ['--surface', '--surface-2', '--border', '--border-strong', '--accent-weak', '--accent-text'];
119
+ const bare = workCss.replace(/\/\*[\s\S]*?\*\//g, '');
120
+ const spent = aliases.filter((a) => new RegExp(`var\\(\\s*${a}\\s*[,)]`).test(bare));
121
+ assert.deepEqual(spent, [],
122
+ 'the aliases still exist for un-reskinned pages, so spending one here is legal CSS that silently opts this sheet out of the world');
123
+ });
124
+
125
+ test('the bulk toolbar carries no rgba literal and is opaque over the cards', () => {
126
+ const bar = rule(workCss, '.bulk-toolbar');
127
+ assert.ok(bar, '.bulk-toolbar has a rule');
128
+ assert.doesNotMatch(bar, /rgba?\(/i,
129
+ 'the three rgba(255,255,255,…) literals went with the inverted --ink surface that needed them');
130
+ // The bar sits over CONTENT, not the page ground, and --glass in light is a 5%
131
+ // tint with the blur at 0 — bare glass let a task title read straight through.
132
+ assert.match(bar, /background:\s*linear-gradient\(var\(--glass\), var\(--glass\)\), var\(--bg\)/,
133
+ 'the tint needs the page ground painted under it, or the bar is transparent in light');
134
+ assert.match(bar, /z-index:\s*var\(--z-/, 'the z ladder, not a hand-picked number');
135
+ });
136
+
137
+ // ---- 4. the landmark floor --------------------------------------------------
138
+
139
+ test('work.html has exactly one h1', () => {
140
+ // Strip HTML comments first: the comment ABOVE the heading names <h1>, and
141
+ // counting it made this read 2 when the page has one.
142
+ const h1s = workHtml.replace(/<!--[\s\S]*?-->/g, '').match(/<h1\b/g) || [];
143
+ assert.equal(h1s.length, 1,
144
+ 'the page had none until task 1003304 and failed one-header-main-h1 on every render');
145
+ });
146
+
147
+ test('both re-skinned pages have a states file, so the floors are reproducible', () => {
148
+ for (const f of ['work.states.json', 'index.states.json']) {
149
+ const json = JSON.parse(read(f));
150
+ assert.equal(json.surface, 'hall-ui', `${f} names its surface`);
151
+ assert.ok(Object.keys(json.states).length >= 4, `${f} renders more than the default state`);
152
+ }
153
+ });