@bongos/core 1.19.659 → 1.19.661
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 +19 -19
- package/docs/copy-inventory.md +55 -54
- package/docs/copy-registry.json +81 -72
- package/docs/module-api-changelog.md +4 -0
- package/modules/government/catalog.js +42 -5
- package/modules/hall-ui/public/work.css +6 -0
- package/modules/hall-ui/public/work.html +18 -6
- package/modules/hall-ui/public/work.js +161 -66
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/goal_sort_staleness.mjs +12 -1
- package/tests/government_catalog.mjs +13 -0
- package/tests/government_parity.mjs +32 -2
- package/tests/hall_board_world.mjs +103 -0
|
@@ -471,3 +471,106 @@ test('the tab strip is left alone — it scrolls in its own row and never moved
|
|
|
471
471
|
assert.ok(strip, 'the tab strip rule is present');
|
|
472
472
|
assert.match(strip[1], /overflow-x:\s*auto/, 'it scrolls itself, which is why it is not the overflow cause');
|
|
473
473
|
});
|
|
474
|
+
|
|
475
|
+
// ---- the board's filters drive BOTH queue tabs (task 1003828) ---------------
|
|
476
|
+
//
|
|
477
|
+
// The owner reported an auto-filed artist review as "going into uncategorized
|
|
478
|
+
// BONGOS-V2 backlog tasks" and asked for tasks to be "filterable by role type".
|
|
479
|
+
// The review was never uncategorised — cascade.js inherits its parent's goal —
|
|
480
|
+
// but the Backlog tab it lands on could not say so and could not be narrowed:
|
|
481
|
+
//
|
|
482
|
+
// * renderBacklog() applied the SEARCH BOX ONLY. Version, Goal, Kind,
|
|
483
|
+
// Discipline, Newcomer and Run mode were read inside renderClaimable and
|
|
484
|
+
// nowhere else, so six controls were silently inert over the tab holding the
|
|
485
|
+
// most rows. Nothing errored; the list simply did not change.
|
|
486
|
+
// * The filter bar was INSIDE #claim-scroll, which makeTabs hides. So on the
|
|
487
|
+
// Backlog tab those controls were not merely inert — they were off-screen.
|
|
488
|
+
// * A backlog row rendered id + title + version + rank + kind. No goal, no
|
|
489
|
+
// discipline. A correctly-goaled task genuinely read as uncategorised.
|
|
490
|
+
//
|
|
491
|
+
// Each is invisible in a browser the way this file's other entries are: the page
|
|
492
|
+
// renders, it just renders a control that does nothing.
|
|
493
|
+
|
|
494
|
+
const workJs = read('work.js');
|
|
495
|
+
|
|
496
|
+
test('the filter bar is above the panels, not inside the Ready one that gets hidden', () => {
|
|
497
|
+
const filters = workHtml.indexOf('id="work-filters"');
|
|
498
|
+
const tabs = workHtml.indexOf('id="work-tabs"');
|
|
499
|
+
const claim = workHtml.indexOf('id="claim-scroll"');
|
|
500
|
+
assert.ok(filters > 0 && tabs > 0 && claim > 0, 'all three mounts present');
|
|
501
|
+
assert.ok(filters > tabs, 'the bar follows the tab strip it belongs to');
|
|
502
|
+
assert.ok(filters < claim,
|
|
503
|
+
'and precedes the Ready panel — inside it, makeTabs hides the filters with the tab');
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
test('the bar shows on the two QUEUE tabs and hides on the two that it cannot narrow', () => {
|
|
507
|
+
assert.match(workJs, /const FILTERED_TABS = new Set\(\['ready', 'backlog'\]\)/,
|
|
508
|
+
'Ready and Backlog are task feeds; In Progress and Completed render other objects');
|
|
509
|
+
assert.match(workJs, /function syncFilterBarVisibility/);
|
|
510
|
+
// Wired BOTH ways: on a tab change, and once at boot — makeTabs restores a
|
|
511
|
+
// deep-linked tab (/work#completed) before the bar is built, and fires
|
|
512
|
+
// onChange only on a CHANGE, so opening state has to be set explicitly.
|
|
513
|
+
assert.match(workJs, /onChange: \(id\) => \{[\s\S]{0,160}syncFilterBarVisibility\(id\)/,
|
|
514
|
+
'a tab change re-syncs the bar');
|
|
515
|
+
assert.match(workJs, /syncFilterBarVisibility\(workTabs && workTabs\.active \? workTabs\.active\(\) : 'ready'\)/,
|
|
516
|
+
'and the opening tab decides the opening state');
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
test('both queue tabs narrow through ONE predicate, so a filter cannot reach only one', () => {
|
|
520
|
+
assert.match(workJs, /function matchesBoardFilters\(t, f\)/,
|
|
521
|
+
'the predicate is shared, not copied per tab');
|
|
522
|
+
// Both renderers must consult it. This is the whole defect: before, only the
|
|
523
|
+
// claimable one did.
|
|
524
|
+
const claimable = workJs.slice(workJs.indexOf('function renderClaimable'), workJs.indexOf('async function populateCreatorFilter'));
|
|
525
|
+
const backlog = workJs.slice(workJs.indexOf('function renderBacklog'), workJs.indexOf('async function refreshLive'));
|
|
526
|
+
for (const [name, body] of [['renderClaimable', claimable], ['renderBacklog', backlog]]) {
|
|
527
|
+
assert.ok(body.length, `${name} body located`);
|
|
528
|
+
assert.match(body, /matchesBoardFilters\(t, filters\)/, `${name} narrows through the shared predicate`);
|
|
529
|
+
assert.match(body, /applyRunMode\(list, \$\('#f-fit'\)/, `${name} applies the set-relative run-mode lens too`);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
test('a backlog row names its craft and its goal', () => {
|
|
534
|
+
const backlog = workJs.slice(workJs.indexOf('function renderBacklog'));
|
|
535
|
+
// The same vocabulary the Ready card has carried since 1755 — a second spelling
|
|
536
|
+
// would let the two tabs describe one task differently.
|
|
537
|
+
assert.match(backlog, /const discipline = t\.discipline \|\| 'unclassified'/);
|
|
538
|
+
assert.match(backlog, /disciplineTag/, 'the row renders the discipline tag');
|
|
539
|
+
assert.match(backlog, /taskState\.goalTitles \|\| \{\}/, 'the goal is named by TITLE, not by bare id');
|
|
540
|
+
assert.match(backlog, /'No goal'/, 'and a goal-less row says so rather than rendering a gap');
|
|
541
|
+
assert.ok(rule(workCss, '.history__goal'), '.history__goal has a rule — an unstyled sub-line inherits the title size');
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
test('the backlog asks the SERVER the narrow question, because its read is capped', () => {
|
|
545
|
+
// A client-side-only narrow of a capped read LIES: filtering the 500 fetched
|
|
546
|
+
// rows to the artist ones hides the artist tasks that sat at row 501.
|
|
547
|
+
assert.match(workJs, /function backlogFilterQuery/);
|
|
548
|
+
const q = workJs.slice(workJs.indexOf('function backlogFilterQuery'), workJs.indexOf('BACKLOG_LIMIT ='));
|
|
549
|
+
for (const param of ['version=', 'kind=', 'discipline=', 'goal_id=']) {
|
|
550
|
+
assert.ok(q.includes(param), `the backlog read sends ${param} — GET /tasks answers it`);
|
|
551
|
+
}
|
|
552
|
+
// '__none__' (ungoaled) has no server predicate, so it must not be sent.
|
|
553
|
+
assert.match(q, /f\.goal && f\.goal !== '__none__'/,
|
|
554
|
+
"the client-only 'No goal' lens is never sent to the route");
|
|
555
|
+
// It reads through boardFilterValues rather than re-querying the DOM, so the
|
|
556
|
+
// query and the client predicate can never disagree about a control's value.
|
|
557
|
+
assert.match(q, /const f = boardFilterValues\(\);/);
|
|
558
|
+
// And it stays SEPARATE from the claimable query: GET /tasks/claimable answers
|
|
559
|
+
// only version + discipline, so kind/goal_id there would be a silent no-op.
|
|
560
|
+
const claimQ = workJs.slice(workJs.indexOf('function serverFilterQuery'), workJs.indexOf('task 1003828 — the BACKLOG'));
|
|
561
|
+
assert.ok(!claimQ.includes('goal_id='), 'the claimable feed is not sent goal_id');
|
|
562
|
+
assert.ok(!claimQ.includes('kind='), 'nor kind');
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test('each feed decides its own re-fetch, so a server change cannot strand the other tab', () => {
|
|
566
|
+
// The old shape compared ONE query and returned early, which left the other
|
|
567
|
+
// tab's client-side lenses unapplied until the next poll happened to repaint.
|
|
568
|
+
assert.match(workJs, /if \(q !== lastServerFilterQuery\) \{ lastServerFilterQuery = q; refreshLive\(\); \} else \{ renderClaimable\(\); \}/);
|
|
569
|
+
assert.match(workJs, /if \(bq !== lastBacklogQuery\) \{ lastBacklogQuery = bq; loadBacklog\(\); \} else \{ renderBacklog\(\); \}/);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
test('the Goal filter offers goals that only have BACKLOG work', () => {
|
|
573
|
+
const populate = workJs.slice(workJs.indexOf('function populateGoalFilter'), workJs.indexOf('async function copyPromptForTask'));
|
|
574
|
+
assert.match(populate, /\[\.\.\.taskState\.all, \.\.\.\(taskState\.backlog \|\| \[\]\)\]/,
|
|
575
|
+
'built from the claimable feed alone, a backlog-only goal was unofferable');
|
|
576
|
+
});
|