@bongos/core 1.19.339 → 1.19.340
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.
- package/.bongos-core.json +14 -14
- package/docs/copy-inventory.md +153 -151
- package/docs/copy-registry.json +212 -194
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/cosmos.css +18 -3
- package/public-landing/projects.html +191 -51
- package/src/module-api.js +3 -1
- package/tests/projects_hub.mjs +1 -1
- package/tests/projects_hub_pre_uat.mjs +85 -4
package/tests/projects_hub.mjs
CHANGED
|
@@ -293,7 +293,7 @@ test('the new view balances its own tree — the map-only walkers never covered
|
|
|
293
293
|
// lives there too; a lopsided div in the wizard block would be caught by
|
|
294
294
|
// nothing else while silently reparenting the footer.
|
|
295
295
|
const start = HUB.indexOf('<section id="view-new"');
|
|
296
|
-
const end = HUB.indexOf('<
|
|
296
|
+
const end = HUB.indexOf('<footer class="foot"');
|
|
297
297
|
assert.ok(start !== -1 && end > start, 'need the view-new span');
|
|
298
298
|
const wiz = HUB.slice(start, end);
|
|
299
299
|
assert.equal((wiz.match(/<div\b/g) || []).length, (wiz.match(/<\/div>/g) || []).length,
|
|
@@ -69,14 +69,14 @@ test('a queued standup lights "Request filed", not "Standing it up" — on the c
|
|
|
69
69
|
const strip = fn('stripFor');
|
|
70
70
|
assert.match(strip, /if \(s === 'requested'\) \{[\s\S]*?stripMarkup\(revive \? 'revive' : 'standup', 0, 0,/, 'card: requested → stage 0 current');
|
|
71
71
|
assert.match(strip, /if \(s === 'provisioning'\) \{[\s\S]*?stripMarkup\(revive \? 'revive' : 'standup', 1, 1,/, 'card: provisioning → stage 1 current');
|
|
72
|
-
assert.match(SCRIPT, /if \(s === 'requested'\) \{\n\s*
|
|
72
|
+
assert.match(SCRIPT, /if \(s === 'requested'\) \{\n\s*setProgress\(progressMarkup\(0, 0,/, 'done panel: requested → stage 0 current');
|
|
73
73
|
assert.doesNotMatch(SCRIPT, /Waiting for a runner/, '"runner" is operator vocabulary');
|
|
74
74
|
assert.doesNotMatch(SCRIPT, /a runner picks it up/, '"runner" is operator vocabulary (retry message)');
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
/* ── the done panel never lies at the end of its watch, and never opens a door early ── */
|
|
78
78
|
test('the 33-minute hand-off keeps a snag honest and keeps its flow', () => {
|
|
79
|
-
assert.match(SCRIPT, /if \(ticks > 400\) \{[\s\S]*?if \(live\.instStatus !== 'error'\) \{[\s\S]*?Still working[\s\S]*?watchFlow\)
|
|
79
|
+
assert.match(SCRIPT, /if \(ticks > 400\) \{[\s\S]*?if \(live\.instStatus !== 'error'\) \{[\s\S]*?Still working[\s\S]*?watchFlow\)/, 'no "still building" over an error; the strip keeps its vocabulary');
|
|
80
80
|
assert.doesNotMatch(SCRIPT, /Still building — this tab will stop watching now/);
|
|
81
81
|
assert.match(SCRIPT, /progressMarkup\(1, 1, m\.slice\(5\), 'isErr', watchFlow\)/, 'the session-expiry message keeps its flow too');
|
|
82
82
|
});
|
|
@@ -190,7 +190,7 @@ test('a live private-repo project is not told it is broken', () => {
|
|
|
190
190
|
test('Back moves between views — a deliberate view change is a history entry', () => {
|
|
191
191
|
assert.match(SCRIPT, /history\.pushState\(null, '', '\?view=' \+ v \+ idPart\)/);
|
|
192
192
|
assert.match(SCRIPT, /history\.replaceState\(null, '', '\?view=' \+ v \+ idPart\)/, 'boot + background settles still rewrite in place');
|
|
193
|
-
assert.match(SCRIPT, /window\.addEventListener\('popstate', function \(\) \{ setView\(viewFromUrl\(\)\); \}\)/);
|
|
193
|
+
assert.match(SCRIPT, /window\.addEventListener\('popstate', function \(\) \{ closeCard\(\); setView\(viewFromUrl\(\)\); \}\)/);
|
|
194
194
|
assert.match(SCRIPT, /else if \(v === 'manage' && a\.dataset\.nav === 'mine'\) a\.setAttribute\('aria-current', 'page'\)/, 'manage keeps the Your-projects pill lit');
|
|
195
195
|
});
|
|
196
196
|
|
|
@@ -300,7 +300,7 @@ test('decision 9 — only mouse-driven focus on a wizard heading goes ringless',
|
|
|
300
300
|
|
|
301
301
|
test('decision 10 — both footers carry the open-source (AGPL-3.0) link, in link ink', () => {
|
|
302
302
|
const AGPL = /<a href="https:\/\/www\.gnu\.org\/licenses\/agpl-3\.0\.html" target="_blank" rel="noopener">Open source \(AGPL-3\.0\)<\/a>\./;
|
|
303
|
-
const hubFoot = (HUB.match(/<
|
|
303
|
+
const hubFoot = (HUB.match(/<footer class="foot">[\s\S]*?<\/footer>/) || [''])[0];
|
|
304
304
|
const indexFoot = (INDEX.match(/<div class="foot">[\s\S]*?<\/div>/) || [''])[0];
|
|
305
305
|
assert.match(hubFoot, AGPL, 'projects.html');
|
|
306
306
|
assert.match(indexFoot, AGPL, 'index.html');
|
|
@@ -362,3 +362,84 @@ test('card action results announce, nothing under the pointer is deleted, and fo
|
|
|
362
362
|
assert.match(fn('focusKeyOf'), /data-teardown-yes'\) \|\| el\.hasAttribute\('data-teardown-no'\)\) return '\[data-teardown\]'/, 'Yes / Keep land back on the door that opened them');
|
|
363
363
|
assert.match(fn('focusCardControl'), /li\.querySelector\('\.actions a, \.actions button'\) \|\|\n\s*li\.querySelector\('details\.attachFold summary'\)/, 'nearest control as the fallback');
|
|
364
364
|
});
|
|
365
|
+
|
|
366
|
+
/* ── accessibility follow-ups (task 1003246) ── */
|
|
367
|
+
test('the done panel’s progress region is stable, change-only, and says each stage’s state', () => {
|
|
368
|
+
assert.match(HUB, /<div id="liveProgress" role="status" aria-live="polite"><\/div>/, 'the live region is the static wrapper');
|
|
369
|
+
assert.doesNotMatch(fn('progressMarkup'), /aria-live/, 'the rebuilt strip carries no live-region attributes');
|
|
370
|
+
assert.match(fn('progressMarkup'), /\(c === 'now' \? ' aria-current="step"' : ''\)/, 'the current stage is marked');
|
|
371
|
+
assert.match(fn('progressMarkup'), /<span class="vh">, ' \+ word \+ '<\/span>/, 'each stage says its state to a screen reader');
|
|
372
|
+
assert.match(fn('setProgress'), /if \(!el \|\| html === lastProgHtml\) return;/, 'an identical tick performs no DOM write');
|
|
373
|
+
assert.doesNotMatch(SCRIPT, /\$\('liveProgress'\)\.innerHTML =/, 'every write goes through setProgress');
|
|
374
|
+
assert.doesNotMatch(fn('renderProgress'), /el\.innerHTML =/, 'renderProgress included');
|
|
375
|
+
assert.match(SCRIPT, /lastStepsHtml = '';\n\s*lastProgHtml = '';/, 'starting another project resets both guards');
|
|
376
|
+
assert.match(COSMOS, /\.vh\{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset\(50%\);white-space:nowrap;\}/, 'the visually-hidden utility is in cosmos');
|
|
377
|
+
assert.match(fn('stripMarkup'), /role="status" aria-live="polite"/, 'the mine-card strip is left alone this pass');
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
test('the map table sorts from the keyboard and an owned row’s name is a real link', () => {
|
|
381
|
+
assert.match(HUB, /<th class="colName" data-sort="name" aria-sort="ascending"><button type="button" class="thSort">Project<\/button><\/th>/);
|
|
382
|
+
assert.match(HUB, /<th class="right" data-sort="builders"><button type="button" class="thSort">Builders<\/button><\/th>/);
|
|
383
|
+
assert.match(SCRIPT, /var btn = th\.querySelector\('button'\);\n\s*if \(!btn\) return;\n\s*btn\.addEventListener\('click'/, 'the listener sits on the button, not the th');
|
|
384
|
+
assert.doesNotMatch(SCRIPT, /th\.style\.cursor/, 'no JS-set cursor on the th');
|
|
385
|
+
assert.match(COSMOS, /\.tbl th button\{all:unset;cursor:pointer;\}/);
|
|
386
|
+
assert.match(COSMOS, /\.tbl th button:focus-visible\{outline:2px solid var\(--accent\);outline-offset:2px;\}/, 'keyboard focus on the sort button is visible');
|
|
387
|
+
const generated = (SCRIPT.match(/return '<tr data-generated([\s\S]*?)'<\/tr>';/) || [, ''])[1];
|
|
388
|
+
assert.match(generated, /<a class="ptitle" data-manage="' \+ esc\(String\(p\._inst\.id\)\) \+ '" href="\?view=manage&id=' \+ esc\(String\(p\._inst\.id\)\) \+ '">/, 'an owned row’s title is a manage anchor');
|
|
389
|
+
const rows = SCRIPT.slice(SCRIPT.indexOf("$('mapRows').addEventListener('click'"));
|
|
390
|
+
const branch = rows.indexOf("closest('a[data-manage]')");
|
|
391
|
+
const bail = rows.indexOf("if (ev.target.closest && ev.target.closest('a')) return;");
|
|
392
|
+
assert.ok(branch > 0 && bail > branch, 'the manage branch runs before the anchor bail-out');
|
|
393
|
+
assert.match(HUB, /Sort by Project or Builders\./, 'the hint names the two sortable columns');
|
|
394
|
+
assert.match(COSMOS, /\.tbl \.ptitle\{[^}]*text-decoration:none;\}/, 'the link wears the title’s ink, not the UA underline');
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test('the hub has one main landmark, a heading for the map, a footer, and a skip link', () => {
|
|
398
|
+
const main = HUB.indexOf('<main id="hubMain">');
|
|
399
|
+
const map = HUB.indexOf('<section id="view-map">');
|
|
400
|
+
const close = HUB.indexOf('\n</main>');
|
|
401
|
+
const foot = HUB.indexOf('<footer class="foot">');
|
|
402
|
+
assert.ok(main > 0 && main < map && close > HUB.indexOf('<section id="view-new"') && foot > close, 'the four views sit inside #hubMain, the footer after it');
|
|
403
|
+
assert.equal((HUB.match(/<main\b/g) || []).length, 1, 'exactly one main');
|
|
404
|
+
assert.match(HUB, /<section id="view-map">\n\s*<h1 class="vh">The project map<\/h1>/, 'the map view has a heading');
|
|
405
|
+
assert.match(HUB, /<a class="skip vh" href="#hubMain">Skip to content<\/a>\n<header class="shell">/, 'the skip link is the first thing before the shell');
|
|
406
|
+
assert.match(HUB, /\.skip:focus\{position:fixed;top:8px;left:8px;/, 'it becomes a real pill on focus');
|
|
407
|
+
assert.doesNotMatch(HUB, /<main class="card">/, 'the old inner main is a div');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test('completed rail steps are a way back, the repo picker is honest ARIA, the card inerts the page', () => {
|
|
411
|
+
assert.match(fn('paintRail'), /if \(step < 6 && n < cur\) \{\n\s*li\.setAttribute\('role', 'button'\);\n\s*li\.setAttribute\('tabindex', '0'\);\n\s*li\.setAttribute\('data-goto', String\(n\)\);/, 'only answered steps become buttons, and none once the project is filed');
|
|
412
|
+
assert.match(SCRIPT, /railEl\.addEventListener\('click'/);
|
|
413
|
+
assert.match(SCRIPT, /railEl\.addEventListener\('keydown'/);
|
|
414
|
+
assert.match(SCRIPT, /show\(Number\(li\.getAttribute\('data-goto'\)\), true\)/, 'a deliberate step: focus moves');
|
|
415
|
+
assert.match(COSMOS, /\.steps li\[role=button\]\{cursor:pointer;\}/);
|
|
416
|
+
assert.match(COSMOS, /\.steps li\[role=button\]:focus-visible\{outline:2px solid var\(--accent\);/);
|
|
417
|
+
assert.doesNotMatch(HUB, /role="listbox"/);
|
|
418
|
+
assert.doesNotMatch(SCRIPT, /role="option"/);
|
|
419
|
+
assert.match(HUB, /<div id="ghList" class="ghlist" role="group" aria-label="Your repositories"><\/div>/, 'the label survives the listbox role');
|
|
420
|
+
assert.match(SCRIPT, /class="ghrow" data-full="' \+ esc\(r\.full_name\) \+ '" aria-current="' \+ \(r\.full_name === sel\) \+ '"/, 'the chosen row is aria-current');
|
|
421
|
+
assert.match(HUB, /\.ghrow\[aria-current=true\]\{/, 'and painted through it');
|
|
422
|
+
assert.doesNotMatch(HUB, /aria-selected/, 'aria-selected went with the option role');
|
|
423
|
+
assert.match(SCRIPT, /if \(ev\.key !== 'Escape' \|\| \$\('ghList'\)\.hidden\) return;\n\s*ev\.stopPropagation\(\);\n\s*\$\('ghSearch'\)\.focus\(\);\n\s*closeRepoList\(\);/, 'Escape closes the list after handing focus back');
|
|
424
|
+
assert.match(SCRIPT, /if \(ev\.relatedTarget && !\$\('ghAdopt'\)\.contains\(ev\.relatedTarget\)\) closeRepoList\(\);/, 'focus leaving the picker closes it; a null relatedTarget is left alone');
|
|
425
|
+
assert.match(fn('openCard'), /\$\('cardScrim'\)\.hidden = false;\n\s*pageInert\(true\);\n\s*\$\('cardClose'\)\.focus\(\);/);
|
|
426
|
+
assert.match(fn('closeCard'), /pageInert\(false\);[^\n]*\n\s*if \(cardReturn && cardReturn\.focus\) cardReturn\.focus\(\);/, 'inert is lifted before focus is handed back');
|
|
427
|
+
assert.match(fn('pageInert'), /'a\.skip, header\.shell, footer\.foot, #view-mine, #view-new, #view-manage, #view-map > :not\(#cardScrim\)'/, 'the map is inerted a child at a time — the card is its child; the skip link sits outside the shell and goes too');
|
|
428
|
+
assert.match(SCRIPT, /'popstate', function \(\) \{ closeCard\(\); setView\(viewFromUrl\(\)\); \}/, 'Back closes the card, so the page is never left inert');
|
|
429
|
+
assert.equal((HUB.match(/aria-controls="projectsList"/g) || []).length, 5, 'the five state filters name the list they filter');
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
test('control nits: orb cursor, caret under reduced motion, mode label, home label, held reduce-motion query, target sizes', () => {
|
|
433
|
+
assert.doesNotMatch(COSMOS, /\.orb\{flex:0 0 54px;[^}]*cursor:pointer/, 'the base orb is not a pointer');
|
|
434
|
+
assert.match(COSMOS, /\na\.orb\{cursor:pointer;\}/, 'only the linked orb is');
|
|
435
|
+
assert.doesNotMatch(COSMOS, /\.orb\.ghost\{[^}]*cursor:default/);
|
|
436
|
+
assert.match(HUB, /@media \(prefers-reduced-motion:reduce\)\{\.shell \.caret\{display:none;\}\}/, 'the caret hides instead of freezing solid');
|
|
437
|
+
assert.match(fn('paintModeToggle'), /el\.setAttribute\('aria-label', dark \? 'Switch to light mode' : 'Switch to dark mode'\);/);
|
|
438
|
+
assert.doesNotMatch(fn('paintModeToggle'), /aria-pressed/);
|
|
439
|
+
assert.match(SCRIPT, /\$\('markLeft'\)\.parentNode\.setAttribute\('aria-label', productName \+ ' — home'\);\n\s*if \(REDUCED \|\|/, 'the home link is labelled before the typing loop’s early return');
|
|
440
|
+
assert.match(SCRIPT, /var REDUCED_MQ = window\.matchMedia \? window\.matchMedia\('\(prefers-reduced-motion: reduce\)'\) : null;/);
|
|
441
|
+
assert.match(SCRIPT, /if \(REDUCED\) stopSky\(\); else startSky\(\);/, 'a mid-session flip stops or restarts the sky');
|
|
442
|
+
assert.match(SCRIPT, /REDUCED_MQ\.addEventListener\('change', onReducedChange\)/);
|
|
443
|
+
assert.match(HUB, /\.again\{[^}]*padding:6px 2px;min-height:24px;/);
|
|
444
|
+
assert.match(HUB, /\nsummary\{min-height:24px;padding:3px 0;\}/, 'every fold is a 24px target; the disclosure marker stays');
|
|
445
|
+
});
|