@bongos/core 1.24.42 → 1.24.44
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 +108 -103
- package/docs/copy-inventory.md +115 -165
- package/docs/copy-registry.json +230 -781
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/onboarding/primer.md +1 -1
- package/docs/page-inventory.json +10 -38
- package/docs/page-readings.json +528 -627
- package/modules/hall-ui/public/ai-studio.html +4 -4
- package/modules/hall-ui/public/atlas.html +4 -4
- package/modules/hall-ui/public/blockers.html +4 -4
- package/modules/hall-ui/public/board-room.html +4 -4
- package/modules/hall-ui/public/builders.js +22 -505
- package/modules/hall-ui/public/city.html +4 -4
- package/modules/hall-ui/public/collab.html +4 -4
- package/modules/hall-ui/public/commands.html +4 -4
- package/modules/hall-ui/public/copy-desk.html +4 -4
- package/modules/hall-ui/public/craft-mode.js +2 -2
- package/modules/hall-ui/public/deploy.html +4 -4
- package/modules/hall-ui/public/dom-utils.js +2 -1
- package/modules/hall-ui/public/goal-map.html +4 -4
- package/modules/hall-ui/public/goals.html +16 -4
- package/modules/hall-ui/public/government.html +4 -4
- package/modules/hall-ui/public/hall-render.js +23 -524
- package/modules/hall-ui/public/hall-widgets.js +11 -109
- package/modules/hall-ui/public/idea.html +4 -4
- package/modules/hall-ui/public/ideas.html +4 -4
- package/modules/hall-ui/public/index.html +6 -19
- package/modules/hall-ui/public/index.states.json +0 -33
- package/modules/hall-ui/public/market-watch-item.html +4 -4
- package/modules/hall-ui/public/market-watch-rules.html +4 -4
- package/modules/hall-ui/public/market-watch.html +4 -4
- package/modules/hall-ui/public/modules.html +4 -4
- package/modules/hall-ui/public/money.html +4 -4
- package/modules/hall-ui/public/my-agents.html +4 -4
- package/modules/hall-ui/public/palette.js +7 -6
- package/modules/hall-ui/public/primer.html +4 -4
- package/modules/hall-ui/public/profile-own.js +639 -0
- package/modules/hall-ui/public/profile.css +2 -1
- package/modules/hall-ui/public/profile.html +48 -5
- package/modules/hall-ui/public/profile.js +27 -2
- package/modules/hall-ui/public/profile.states.json +18 -0
- package/modules/hall-ui/public/project-settings.html +4 -4
- package/modules/hall-ui/public/roadmap.css +7 -138
- package/modules/hall-ui/public/roadmap.js +35 -293
- package/modules/hall-ui/public/roster.html +4 -4
- package/modules/hall-ui/public/settings.html +4 -4
- package/modules/hall-ui/public/shell-trail.js +2 -1
- package/modules/hall-ui/public/shell.js +31 -28
- package/modules/hall-ui/public/street.html +4 -4
- package/modules/hall-ui/public/studio.html +4 -4
- package/modules/hall-ui/public/task.html +4 -4
- package/modules/hall-ui/public/thinking.html +4 -4
- package/modules/hall-ui/public/tweak-editor.html +4 -4
- package/modules/hall-ui/public/watch.html +4 -4
- package/modules/hall-ui/public/work.html +5 -5
- package/modules/onboarding/founding-mode.js +2 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/bongos/gen-hall-sitemap.js +1 -0
- package/src/bongos/serve-internal.js +6 -6
- package/src/module-api.js +1 -1
- package/tests/craft_mode_nav.mjs +2 -1
- package/tests/curve_controls_delegation.mjs +9 -8
- package/tests/curve_kind_overlay.mjs +5 -5
- package/tests/founding_mode.mjs +5 -3
- package/tests/hall_acclaim_vote_control.mjs +5 -3
- package/tests/hall_audit.mjs +10 -6
- package/tests/hall_board_world.mjs +40 -26
- package/tests/hall_builders_page_pass.mjs +14 -5
- package/tests/hall_goals_band.mjs +88 -0
- package/tests/hall_home_ships_bounded.mjs +10 -21
- package/tests/hall_kit_charts.mjs +7 -5
- package/tests/hall_loading.mjs +2 -1
- package/tests/hall_nav.mjs +41 -39
- package/tests/hall_numbers_page.mjs +100 -0
- package/tests/hall_page_gate_map.mjs +5 -1
- package/tests/hall_palette.mjs +19 -24
- package/tests/hall_profile_world.mjs +7 -2
- package/tests/hall_reading_room.mjs +3 -2
- package/tests/hall_reading_world.mjs +17 -18
- package/tests/hall_sitemap.mjs +2 -1
- package/tests/helpers.mjs +8 -2
- package/tests/leaderboard_roles.mjs +1 -1
- package/tests/module_contributions.mjs +6 -4
- package/tests/nav_permission_atoms.mjs +4 -2
- package/tests/page_tweak_artist.mjs +2 -1
- package/tests/rank_tier_single_source.mjs +3 -0
- package/tests/task_visuals.mjs +3 -1
- package/modules/hall-ui/public/roadmap.html +0 -58
- package/modules/hall-ui/public/roadmap.states.json +0 -31
|
@@ -138,10 +138,10 @@
|
|
|
138
138
|
</div>
|
|
139
139
|
</div>
|
|
140
140
|
|
|
141
|
-
<script src="/builders/dom-utils.js?v=2026-
|
|
142
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
143
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
144
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
141
|
+
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
142
|
+
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
143
|
+
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
144
|
+
<script src="/builders/shell.js?v=2026-11-05-goals"></script>
|
|
145
145
|
<script src="/builders/palette.js" defer></script>
|
|
146
146
|
|
|
147
147
|
<script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
|
|
@@ -142,10 +142,10 @@
|
|
|
142
142
|
|
|
143
143
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
144
144
|
|
|
145
|
-
<script src="/builders/dom-utils.js?v=2026-
|
|
146
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
147
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
148
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
145
|
+
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
146
|
+
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
147
|
+
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
148
|
+
<script src="/builders/shell.js?v=2026-11-05-goals"></script>
|
|
149
149
|
<script src="/builders/palette.js" defer></script>
|
|
150
150
|
<script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
|
|
151
151
|
<script src="/builders/tweak-editor-lib.js?v=2026-09-29-tw27"></script>
|
|
@@ -268,10 +268,10 @@
|
|
|
268
268
|
|
|
269
269
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
270
270
|
chrome exists before the page script runs. -->
|
|
271
|
-
<script src="/builders/dom-utils.js?v=2026-
|
|
272
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
273
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
274
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
271
|
+
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
272
|
+
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
273
|
+
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
274
|
+
<script src="/builders/shell.js?v=2026-11-05-goals"></script>
|
|
275
275
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
276
276
|
<script src="/builders/palette.js" defer></script>
|
|
277
277
|
<!-- shared component kit (window.OTBKit) — tabs, ledger, pager (task 2166). -->
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
<!-- IN PROGRESS — the live in-flight list (active claims, all builders). -->
|
|
114
114
|
<section class="scroll" id="wip-scroll" aria-labelledby="wip-h" hidden>
|
|
115
115
|
<h2 id="wip-h" class="scroll__h">In Progress<span class="count-pill" id="wip-count" hidden></span></h2>
|
|
116
|
-
<p class="scroll__lede">What is being built right now. See <a href="/
|
|
116
|
+
<p class="scroll__lede">What is being built right now. See <a href="/goals">Goals</a> for the version's progress and its goals.</p>
|
|
117
117
|
|
|
118
118
|
<table class="ledger" id="wip-table">
|
|
119
119
|
<thead><tr><th></th><th>#</th><th>Task</th><th>Builder</th><th>For</th><th></th></tr></thead>
|
|
@@ -171,10 +171,10 @@
|
|
|
171
171
|
|
|
172
172
|
<!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
|
|
173
173
|
time so the chrome exists before the deferred page scripts execute. -->
|
|
174
|
-
<script src="/builders/dom-utils.js?v=2026-
|
|
175
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
176
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
177
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
174
|
+
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
175
|
+
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
176
|
+
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
177
|
+
<script src="/builders/shell.js?v=2026-11-05-goals"></script>
|
|
178
178
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
179
179
|
<script src="/builders/palette.js" defer></script>
|
|
180
180
|
<!-- task 1004727: the ONE karma vote control every task/idea list renders -->
|
|
@@ -34,7 +34,8 @@ const STAGES = Object.freeze(['decide', 'scope', 'strategize', 'charter', 'legal
|
|
|
34
34
|
// 'community' wakes only when the planet is ALIVE, which is when founding mode switches
|
|
35
35
|
// off entirely, so during founding it is always asleep.
|
|
36
36
|
const WAKES = Object.freeze({
|
|
37
|
-
|
|
37
|
+
// task 1005051: Roadmap folded into Goals, so scope wakes Goals and its map.
|
|
38
|
+
scope: Object.freeze(['goals', 'goal-map']),
|
|
38
39
|
strategize: Object.freeze(['work']),
|
|
39
40
|
charter: Object.freeze(['board-room']),
|
|
40
41
|
// task 1005046: legalize woke Economy and legislation woke Ranks. Both pages
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.44",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.44",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.44",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -11215,5 +11215,17 @@
|
|
|
11215
11215
|
"id": "1005076",
|
|
11216
11216
|
"text": "New builders now get one clear setup screen they finish once, and everyone already set up no longer sees a setup banner across the top of Home."
|
|
11217
11217
|
}
|
|
11218
|
+
],
|
|
11219
|
+
"1.24.43": [
|
|
11220
|
+
{
|
|
11221
|
+
"id": "1005050",
|
|
11222
|
+
"text": "Your numbers are on one page now: your Profile. The separate Accomplishments page is gone from the sidebar. The parts only you should see — your rank and how to reach the next one, how your work was reviewed, your karma histor"
|
|
11223
|
+
}
|
|
11224
|
+
],
|
|
11225
|
+
"1.24.44": [
|
|
11226
|
+
{
|
|
11227
|
+
"id": "1005051",
|
|
11228
|
+
"text": "Goals now opens with the version's progress and the timeline of every version, so the separate Roadmap page is gone from the sidebar. Old Roadmap links open Goals. The Tasks board's 'see the version' line points at Goals too."
|
|
11229
|
+
}
|
|
11218
11230
|
]
|
|
11219
11231
|
}
|
|
@@ -64,6 +64,7 @@ const ROUTES = {
|
|
|
64
64
|
'/diagrams': { to: '/primer', why: 'now a Primer chapter, redirects; its images still serve (task 1005046)' },
|
|
65
65
|
'/sessions': { to: '/watch', why: 'now a Watch tab, redirects (task 1005049)' },
|
|
66
66
|
'/gate': { to: '/watch', why: 'Autobongos, now a Watch tab, redirects (task 1005049)' },
|
|
67
|
+
'/roadmap': { to: '/goals', why: 'now the top of Goals, redirects (task 1005051)' },
|
|
67
68
|
};
|
|
68
69
|
// Targets that leave the hall on purpose: sign-in, the hub's /account (platform-server.js;
|
|
69
70
|
// settings.js points the link at the hub), the API, module assets, and any file with an extension.
|
|
@@ -1039,10 +1039,10 @@ function mountInternalSurfaces(app) {
|
|
|
1039
1039
|
return res.redirect(302, `${isBuildersHost(req) ? '' : '/builders'}/market-watch${qs}#settings`);
|
|
1040
1040
|
});
|
|
1041
1041
|
|
|
1042
|
-
// Pages folded into
|
|
1043
|
-
//
|
|
1044
|
-
const RETIRED_TO_SECTION = Object.freeze({ fleet: 'money#hosting', sessions: 'watch#sessions', gate: 'watch#autobongos' });
|
|
1045
|
-
const RETIRED_SECTION_RE = /^\/builders\/(fleet|sessions|gate)(?:\.html)?\/?$/;
|
|
1042
|
+
// Pages folded into another page; 302 and no gate, as above. Task 1005047: Fleet costs is
|
|
1043
|
+
// /money#hosting; 1005049: Sessions and Autobongos are Watch tabs; 1005051: Roadmap is Goals'.
|
|
1044
|
+
const RETIRED_TO_SECTION = Object.freeze({ fleet: 'money#hosting', sessions: 'watch#sessions', gate: 'watch#autobongos', roadmap: 'goals' });
|
|
1045
|
+
const RETIRED_SECTION_RE = /^\/builders\/(fleet|sessions|gate|roadmap)(?:\.html)?\/?$/;
|
|
1046
1046
|
app.use((req, res, next) => {
|
|
1047
1047
|
if (isModuleSurfaceHost(req)) return next();
|
|
1048
1048
|
if (req.method !== 'GET' && req.method !== 'HEAD') return next();
|
|
@@ -1050,7 +1050,7 @@ function mountInternalSurfaces(app) {
|
|
|
1050
1050
|
if (!m) return next();
|
|
1051
1051
|
const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
|
|
1052
1052
|
const [page, section] = RETIRED_TO_SECTION[m[1]].split('#');
|
|
1053
|
-
return res.redirect(302, `${isBuildersHost(req) ? '' : '/builders'}/${page}${qs}
|
|
1053
|
+
return res.redirect(302, `${isBuildersHost(req) ? '' : '/builders'}/${page}${qs}${section ? `#${section}` : ''}`);
|
|
1054
1054
|
});
|
|
1055
1055
|
|
|
1056
1056
|
// task 1005046 (owner decision 2026-11-03, idea 1001311): ONE reading room. Ranks,
|
|
@@ -1348,7 +1348,7 @@ function mountInternalSurfaces(app) {
|
|
|
1348
1348
|
// whole gate; the governing reads it adds are gated by their own routes.
|
|
1349
1349
|
// task 1004921 `ai-studio` (the viewer's own routes).
|
|
1350
1350
|
const SIGNEDIN_BUILDERS_PAGE_RE =
|
|
1351
|
-
/^\/builders\/(?:work|settings|atlas|modules|goals|goal-map|
|
|
1351
|
+
/^\/builders\/(?:work|settings|atlas|modules|goals|goal-map|copy-desk|studio|ai-studio|tweak-editor|ideas|thinking|collab|task|idea|primer(?:\.md)?|my-agents|commands)(?:\.html)?\/?$/;
|
|
1352
1352
|
// The diagram image assets under /builders/diagrams/ ride the same tier as the
|
|
1353
1353
|
// page that embeds them — the Primer, since task 1005046 retired the Diagrams
|
|
1354
1354
|
// page (its URL now 302s to the Primer's first diagram, above).
|
package/src/module-api.js
CHANGED
|
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
|
|
|
81
81
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
82
82
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
83
83
|
|
|
84
|
-
const CORE_VERSION = '1.24.
|
|
84
|
+
const CORE_VERSION = '1.24.44'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
85
85
|
|
|
86
86
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
87
87
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
package/tests/craft_mode_nav.mjs
CHANGED
|
@@ -146,7 +146,8 @@ test('THE ALWAYS-PRESENT LIST: one shared list, Builders first, then Collab and
|
|
|
146
146
|
|
|
147
147
|
test('every tab a mode names is a real NAV item, and every NAV item lives in some mode', () => {
|
|
148
148
|
const { craft } = boot();
|
|
149
|
-
|
|
149
|
+
// 24 since task 1005051 folded the Roadmap into Goals (a sanity floor, not a count).
|
|
150
|
+
assert.ok(NAV_IDS.length >= 24, `read ${NAV_IDS.length} NAV ids from shell.js`);
|
|
150
151
|
const named = new Set(CRAFTS.flatMap((m) => craft.tabsFor(m)));
|
|
151
152
|
for (const id of named) assert.ok(NAV_IDS.includes(id), `${id} is a NAV id`);
|
|
152
153
|
for (const id of NAV_IDS) assert.ok(named.has(id), `${id} is reachable from some mode's nav`);
|
|
@@ -23,18 +23,19 @@ import { fileURLToPath } from 'node:url';
|
|
|
23
23
|
|
|
24
24
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
25
25
|
const PUB = path.join(ROOT, 'modules/hall-ui/public');
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// task 1005050: the Progress section moved with Accomplishments to the Profile
|
|
27
|
+
// page's "Just you" screen — its markup is profile.html's, its wiring profile-own.js's.
|
|
28
|
+
const pageSrc = fs.readFileSync(path.join(PUB, 'profile.html'), 'utf8');
|
|
29
|
+
const buildersSrc = fs.readFileSync(path.join(PUB, 'profile-own.js'), 'utf8');
|
|
28
30
|
|
|
29
31
|
const SECTION_ID = 'analytics-scroll';
|
|
30
32
|
|
|
31
33
|
// The Progress widget's markup: the concatenated string literals of its w() call.
|
|
32
34
|
function progressMarkup() {
|
|
33
|
-
const start =
|
|
34
|
-
assert.ok(start >= 0, `
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
return [...call.matchAll(/'((?:[^'\\]|\\.)*)'/g)].map((m) => m[1]).slice(2).join('');
|
|
35
|
+
const start = pageSrc.indexOf(`id="${SECTION_ID}"`);
|
|
36
|
+
assert.ok(start >= 0, `profile.html no longer carries '${SECTION_ID}'`);
|
|
37
|
+
const open = pageSrc.lastIndexOf('<section', start);
|
|
38
|
+
return pageSrc.slice(open, pageSrc.indexOf('</section>', start) + '</section>'.length);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
// True if the element with id=`outer` encloses the element with id=`inner`.
|
|
@@ -57,7 +58,7 @@ function encloses(html, outer, inner) {
|
|
|
57
58
|
// The selector each curve-control listener is attached to in builders.js.
|
|
58
59
|
function delegationTarget(marker) {
|
|
59
60
|
const at = buildersSrc.indexOf(marker);
|
|
60
|
-
assert.ok(at >= 0, `
|
|
61
|
+
assert.ok(at >= 0, `profile-own.js no longer handles ${marker}`);
|
|
61
62
|
const before = buildersSrc.slice(0, at);
|
|
62
63
|
const all = [...before.matchAll(/\$\('#([\w-]+)'\)\?\.addEventListener\('(click|change)'/g)];
|
|
63
64
|
assert.ok(all.length, `no delegated listener found before ${marker}`);
|
|
@@ -254,7 +254,7 @@ test('cost is WITHHELD under a kind filter, not faked', () => {
|
|
|
254
254
|
});
|
|
255
255
|
|
|
256
256
|
test('the client hides the cost chart and stat rather than printing $0.00', () => {
|
|
257
|
-
const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/
|
|
257
|
+
const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
|
|
258
258
|
assert.match(render, /costUnavailable = Array\.isArray\(data\.unavailable\) && data\.unavailable\.includes\('cost_trend'\)/);
|
|
259
259
|
assert.match(render, /costUnavailable\s*\?\s*statCard\('Cost \/ work', '—'/,
|
|
260
260
|
'a withheld cost must render as —, never as a price');
|
|
@@ -264,7 +264,7 @@ test('the controls are written BEFORE every early return', () => {
|
|
|
264
264
|
// A kind with nothing shipped in the window takes the totalShipped === 0
|
|
265
265
|
// branch, which blanks the body. If the filter were drawn after that, the
|
|
266
266
|
// reader would be stranded at "no data" with no control to undo it.
|
|
267
|
-
const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/
|
|
267
|
+
const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
|
|
268
268
|
const fn = render.slice(render.indexOf('function renderCurveBody()'));
|
|
269
269
|
const controls = fn.indexOf('kindFilterHtml()');
|
|
270
270
|
const firstReturn = fn.indexOf('if (!data) {');
|
|
@@ -275,8 +275,8 @@ test('the controls are written BEFORE every early return', () => {
|
|
|
275
275
|
});
|
|
276
276
|
|
|
277
277
|
test('the scope toggle is no longer rank-gated', () => {
|
|
278
|
-
const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/
|
|
279
|
-
const builders = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/
|
|
278
|
+
const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
|
|
279
|
+
const builders = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
|
|
280
280
|
assert.ok(!/analyticsState\.isArchon\s*&&/.test(render),
|
|
281
281
|
'the scope toggle is still gated on rank');
|
|
282
282
|
// The field is REMOVED, not merely unread — a write-only state flag is how a
|
|
@@ -288,7 +288,7 @@ test('the scope toggle is no longer rank-gated', () => {
|
|
|
288
288
|
});
|
|
289
289
|
|
|
290
290
|
test('a kind change re-fetches BOTH series, and the newest answer wins', () => {
|
|
291
|
-
const builders = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/
|
|
291
|
+
const builders = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
|
|
292
292
|
const fn = builders.slice(builders.indexOf('async function reloadCurveForKind'));
|
|
293
293
|
const body = fn.slice(0, fn.indexOf('\n }') + 4);
|
|
294
294
|
assert.match(body, /analytics\/builder\/\$\{id\}\?days=90\$\{q\}/);
|
package/tests/founding_mode.mjs
CHANGED
|
@@ -43,14 +43,16 @@ test('active with no stage yet is the decide pre-stage, and every room sleeps',
|
|
|
43
43
|
assert.equal(f.stage, 'decide');
|
|
44
44
|
assert.equal(f.stage_number, 0);
|
|
45
45
|
assert.equal(f.stage_count, 5);
|
|
46
|
-
|
|
46
|
+
// task 1005051: Roadmap folded into Goals; scope wakes Goals and its map.
|
|
47
|
+
for (const id of ['goals', 'goal-map', 'work', 'board-room', 'collab', 'roster']) {
|
|
47
48
|
assert.ok(id in f.asleep, `${id} sleeps while deciding`);
|
|
48
49
|
}
|
|
49
50
|
});
|
|
50
51
|
|
|
51
52
|
test('a room wakes once the stage that wakes it is behind', () => {
|
|
52
53
|
const f = fm.shapeFoundingMode({ active: 'true', stage: 'charter' });
|
|
53
|
-
for (const id of ['
|
|
54
|
+
for (const id of ['goals', 'goal-map', 'work']) assert.ok(!(id in f.asleep), `${id} woke`);
|
|
55
|
+
assert.ok(!('roadmap' in f.asleep), 'the folded Roadmap is no room to wake');
|
|
54
56
|
assert.equal(f.asleep['board-room'], 'charter');
|
|
55
57
|
// task 1005046: Economy and Ranks were the rooms legalize and legislation woke;
|
|
56
58
|
// they are Primer chapters now, and the Primer never sleeps.
|
|
@@ -121,7 +123,7 @@ class El {
|
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
function makeHall() {
|
|
124
|
-
const navIds = ['home', '
|
|
126
|
+
const navIds = ['home', 'goals', 'goal-map', 'work', 'board-room', 'collab', 'roster', 'profile'];
|
|
125
127
|
const nav = navIds.map((id) => { const a = new El('a'); a.id = 'nav-' + id; a.className = 'nav-item'; return a; });
|
|
126
128
|
const appBody = new El('div');
|
|
127
129
|
const topbar = new El('header'); topbar.id = 'app-topbar';
|
|
@@ -19,7 +19,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
19
19
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
20
20
|
const HALL = path.join(ROOT, 'modules', 'hall-ui', 'public');
|
|
21
21
|
const read = (f) => fs.readFileSync(path.join(HALL, f), 'utf8').replace(/\r\n/g, '\n');
|
|
22
|
-
|
|
22
|
+
// task 1005050: the peer-recognition feed moved with Accomplishments to the
|
|
23
|
+
// Profile page's "Just you" screen (profile-own.js; its markup is profile.html's).
|
|
24
|
+
const builders = read('profile-own.js');
|
|
23
25
|
|
|
24
26
|
// A small DOM: enough for the control's cast() and event dispatch.
|
|
25
27
|
function el(attrs = {}) {
|
|
@@ -71,7 +73,7 @@ function bootControl(fetchImpl) {
|
|
|
71
73
|
// The feed's real handler, sliced from builders.js and run over a held list.
|
|
72
74
|
function feedHandler(tasks) {
|
|
73
75
|
const start = builders.indexOf('function onAcclaimVote(');
|
|
74
|
-
assert.ok(start > 0, '
|
|
76
|
+
assert.ok(start > 0, 'profile-own.js defines onAcclaimVote');
|
|
75
77
|
const end = builders.indexOf('\n }\n', start) + 4;
|
|
76
78
|
const ctx = { acclaimState: { tasks } };
|
|
77
79
|
vm.createContext(ctx);
|
|
@@ -109,5 +111,5 @@ test('a vote through the real control lands in the held, paged rows', async () =
|
|
|
109
111
|
test('the old vote markup, handler and CSS are gone', () => {
|
|
110
112
|
assert.doesNotMatch(builders, /acclaim__btn|acclaim__vote|data-vote-task|onAcclaimClick|data-acclaim-net/);
|
|
111
113
|
assert.doesNotMatch(read('style.css'), /\.acclaim__(btn|vote|readonly|net)/);
|
|
112
|
-
assert.match(read('
|
|
114
|
+
assert.match(read('profile.html'), /<th>Work<\/th><th>By<\/th><th>Votes<\/th><\/tr>/);
|
|
113
115
|
});
|
package/tests/hall_audit.mjs
CHANGED
|
@@ -320,16 +320,19 @@ test('the three bar copies are gone, and their class families with them', () =>
|
|
|
320
320
|
for (const dead of ['curve__kind-', 'curve__chart', 'watch-trend', 'pmix']) {
|
|
321
321
|
assert.ok(!css.includes(dead), `${dead} is the kit's .kit-bars/.kit-chart now`);
|
|
322
322
|
}
|
|
323
|
-
|
|
323
|
+
// task 1005050: the Curve moved with Accomplishments to profile-own.js.
|
|
324
|
+
assert.match(read('profile-own.js'), /OTBKit\.barSeriesHtml\(/, 'the Curve\'s kinds draw through the kit');
|
|
324
325
|
assert.match(read('watch.js'), /OTBKit\.barSeriesHtml\(/, 'Watch\'s trend draws through the kit');
|
|
325
326
|
assert.match(read('profile-charts.js'), /OTBKit\.barSeriesHtml\(/, 'the craft mix draws through the kit');
|
|
326
327
|
assert.match(read('profile-charts.js'), /OTBKit\.calendarGridHtml\(/, 'the contribution calendar draws through the kit (task 1003572)');
|
|
327
|
-
assert.match(read('
|
|
328
|
+
assert.match(read('profile-own.js'), /OTBKit\.sparklineHtml\(/, 'and the Curve\'s line does too');
|
|
328
329
|
});
|
|
329
330
|
|
|
330
331
|
test('the peer-recognition feed is paged through OTBKit, not by hand', () => {
|
|
331
|
-
|
|
332
|
-
|
|
332
|
+
// task 1005050: the feed moved with Accomplishments to the profile's "Just you"
|
|
333
|
+
// screen; profile-own.js stamps the kit's bar and pages it.
|
|
334
|
+
const js = read('profile-own.js');
|
|
335
|
+
assert.match(js, /pagerBarHtml\('acclaim'/, 'the acclaim card stamps the kit pager bar');
|
|
333
336
|
assert.match(js, /makePager\('acclaim'/, 'acclaim has a kit pager');
|
|
334
337
|
assert.match(js, /paginate\(acclaimState\.tasks, acclaimPager\)/, 'rows are sliced by the kit');
|
|
335
338
|
assert.match(js, /wirePager\(acclaimPager/, 'prev/next/per-page are wired');
|
|
@@ -486,8 +489,9 @@ test('the colour-literal guard CATCHES a reintroduction, and tolerates a task re
|
|
|
486
489
|
'a token is exactly what this rule wants');
|
|
487
490
|
});
|
|
488
491
|
|
|
489
|
-
test("
|
|
490
|
-
|
|
492
|
+
test("the Reviews grade column renders its three verdicts through the vocabulary", () => {
|
|
493
|
+
// task 1005050: Accomplishments' Reviews table is the profile's "Just you" screen's.
|
|
494
|
+
const js = read('profile-own.js');
|
|
491
495
|
const start = js.indexOf('function renderRecentGrades');
|
|
492
496
|
assert.ok(start > 0, 'renderRecentGrades present');
|
|
493
497
|
const body = js.slice(start, js.indexOf('\n }', start));
|
|
@@ -153,11 +153,14 @@ test('work.html has exactly one h1', () => {
|
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
test('both re-skinned pages have a states file, so the floors are reproducible', () => {
|
|
156
|
-
|
|
156
|
+
// task 1005050: Home's two Accomplishments states left with that view (it
|
|
157
|
+
// folded into the Profile page, whose states file measures the "Just you" screen).
|
|
158
|
+
for (const [f, min] of [['work.states.json', 4], ['index.states.json', 2]]) {
|
|
157
159
|
const json = JSON.parse(read(f));
|
|
158
160
|
assert.equal(json.surface, 'hall-ui', `${f} names its surface`);
|
|
159
|
-
assert.ok(Object.keys(json.states).length >=
|
|
161
|
+
assert.ok(Object.keys(json.states).length >= min, `${f} renders more than the default state`);
|
|
160
162
|
}
|
|
163
|
+
assert.ok(JSON.parse(read('profile.states.json')).states.yours, 'the "Just you" screen is measured');
|
|
161
164
|
});
|
|
162
165
|
|
|
163
166
|
// ---- 7. the way INTO a record is the title ----------------------------------
|
|
@@ -397,46 +400,57 @@ test('RUN: the Standing card carries the three facts, the place as "2nd of 4", a
|
|
|
397
400
|
assert.match(withPrefs.el('home-standing-body').innerHTML, /<dt>Leaderboard place<\/dt><dd>—<\/dd>/, 'no roll yet: a dash, not a wrong place');
|
|
398
401
|
});
|
|
399
402
|
|
|
400
|
-
// task 1005046: "Where you stand" left the retired Ranks page for Accomplishments
|
|
401
|
-
//
|
|
402
|
-
// the viewer's rank, the rank
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
};
|
|
403
|
+
// task 1005046: "Where you stand" left the retired Ranks page for Accomplishments,
|
|
404
|
+
// and task 1005050 moved it on, with Accomplishments, to your own profile's
|
|
405
|
+
// "Just you" screen (profile-own.js). The card names the viewer's rank, the rank
|
|
406
|
+
// above it read off the ladder, and how to reach it.
|
|
407
|
+
function bootOwnRank(builder) {
|
|
408
|
+
const nodes = {};
|
|
409
|
+
const el = (id) => (nodes[id] = nodes[id] || { id, hidden: true, innerHTML: '', textContent: '', style: {}, addEventListener() {}, querySelector: () => null });
|
|
410
|
+
const document = { getElementById: el, querySelector: (sel) => el(String(sel).replace(/^#/, '')), querySelectorAll: () => [] };
|
|
411
|
+
const window = {
|
|
412
|
+
OTB: { escapeHtml: (x) => String(x ?? '').replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])), $: document.querySelector, fmtNum: String, fmtDate: String,
|
|
413
|
+
rankLabel: (r) => (r ? r.charAt(0).toUpperCase() + r.slice(1) : '') },
|
|
414
|
+
OTBKit: { makePager: () => ({ page: 1 }), paginate: (x) => x, wirePager() {}, pagerBarHtml: () => '' },
|
|
415
|
+
};
|
|
416
|
+
// A quiet console: the feeds this stub refuses log their own failure.
|
|
417
|
+
const sandbox = { window, document, console: { log() {}, warn() {}, error() {} }, setTimeout, Promise };
|
|
418
|
+
vm.createContext(sandbox);
|
|
419
|
+
vm.runInContext(read('profile-own.js'), sandbox, { filename: 'profile-own.js' });
|
|
420
|
+
const api = { request: () => Promise.resolve({ status: 404, ok: false, data: null }) };
|
|
421
|
+
window.OTBProfileOwn.render({ api, API: '/api/bongos', me: { builder } });
|
|
422
|
+
return { el };
|
|
423
|
+
}
|
|
407
424
|
|
|
408
425
|
test('RUN: Where you stand names the viewer\'s rank, the one above it, and how to reach it', () => {
|
|
409
|
-
const {
|
|
410
|
-
api.renderProfile({ ...ME.builder, rank: 'thetes' });
|
|
426
|
+
const { el } = bootOwnRank({ ...ME.builder, rank: 'thetes' });
|
|
411
427
|
const html = el('rank-standing-body').innerHTML;
|
|
428
|
+
assert.equal(el('rank-scroll').hidden, false, 'the card shows on your own profile');
|
|
412
429
|
assert.match(html, /<div class="ranks-standing__badge"><span class="rank-badge rank-badge--thetes"/);
|
|
413
430
|
assert.match(html, /<p class="ranks-standing__title">The proven builder<\/p>/);
|
|
414
|
-
assert.match(html, /The rank above yours is <span class="rank-badge rank-badge--metic"/, 'the next rung is read off the
|
|
431
|
+
assert.match(html, /The rank above yours is <span class="rank-badge rank-badge--metic"/, 'the next rung is read off the ladder');
|
|
415
432
|
assert.match(html, /Promotion to <strong>Metic<\/strong>/, 'and the climb is the one from THIS rank');
|
|
416
433
|
assert.match(html, /<a href="\/primer#the-rank-ladder">/, 'the full ladder is one chapter of the Primer');
|
|
417
434
|
assert.doesNotMatch(html, /Loading…/, 'the skeleton is replaced');
|
|
418
435
|
});
|
|
419
436
|
|
|
420
437
|
test('RUN: Where you stand says so at the top, and coerces a stray rank to the entry rung', () => {
|
|
421
|
-
const
|
|
422
|
-
top.api.renderProfile({ ...ME.builder, rank: 'archon' });
|
|
423
|
-
const atTop = top.el('rank-standing-body').innerHTML;
|
|
438
|
+
const atTop = bootOwnRank({ ...ME.builder, rank: 'archon' }).el('rank-standing-body').innerHTML;
|
|
424
439
|
assert.equal((atTop.match(/You hold the highest rank/g) || []).length, 1, 'said once, not twice');
|
|
425
440
|
assert.doesNotMatch(atTop, /ranks-standing__next/, 'no next rung to name');
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
const html = stray.el('rank-standing-body').innerHTML;
|
|
429
|
-
assert.match(html, /<p class="ranks-standing__title">The newcomer<\/p>/, 'a reserved rank reads as xenos, as the Standing band does');
|
|
441
|
+
const html = bootOwnRank({ ...ME.builder, rank: 'strategos' }).el('rank-standing-body').innerHTML;
|
|
442
|
+
assert.match(html, /<p class="ranks-standing__title">The newcomer<\/p>/, 'a reserved rank reads as xenos');
|
|
430
443
|
assert.match(html, /The rank above yours is <span class="rank-badge rank-badge--thetes"/);
|
|
431
444
|
});
|
|
432
445
|
|
|
433
|
-
test('the Where you stand card is
|
|
434
|
-
const
|
|
435
|
-
const at =
|
|
436
|
-
assert.ok(at > -1, '
|
|
437
|
-
const
|
|
438
|
-
assert.match(
|
|
439
|
-
assert.match(
|
|
446
|
+
test('the Where you stand card is on the profile\'s "Just you" screen, hidden until drawn, with a Loading… line', () => {
|
|
447
|
+
const html = read('profile.html');
|
|
448
|
+
const at = html.indexOf('id="rank-scroll"');
|
|
449
|
+
assert.ok(at > -1, 'profile.html carries the card');
|
|
450
|
+
const sec = html.slice(html.lastIndexOf('<section', at), html.indexOf('</section>', at));
|
|
451
|
+
assert.match(sec, /data-group="yours" hidden/);
|
|
452
|
+
assert.match(sec, /<div id="rank-standing-body"><div class="hall-loading" role="status">Loading…<\/div><\/div>/);
|
|
453
|
+
assert.ok(!/w\('rank-scroll'/.test(read('hall-widgets.js')), 'Home no longer registers it');
|
|
440
454
|
});
|
|
441
455
|
|
|
442
456
|
test('RUN: the three notices are inbox lines with a kind, a line and — where one exists — a door', () => {
|
|
@@ -229,10 +229,19 @@ test('B: the id wears the same mono/faint voice as the login', () => {
|
|
|
229
229
|
// PART C — grade and cost on the peer-acclaim feed (task 1002835)
|
|
230
230
|
// =========================================================================
|
|
231
231
|
|
|
232
|
-
// acclaimEvidenceHtml
|
|
233
|
-
// (
|
|
234
|
-
//
|
|
235
|
-
const acclaimEvidence = () =>
|
|
232
|
+
// acclaimEvidenceHtml moved with the peer-recognition feed to profile-own.js
|
|
233
|
+
// (task 1005050: Accomplishments folded into the Profile page). It is private to
|
|
234
|
+
// that file's closure, so the shipped function's own source is run here.
|
|
235
|
+
const acclaimEvidence = () => {
|
|
236
|
+
const src = read('profile-own.js');
|
|
237
|
+
const start = src.indexOf(' function acclaimEvidenceHtml(t) {');
|
|
238
|
+
assert.ok(start > 0, 'profile-own.js defines acclaimEvidenceHtml');
|
|
239
|
+
const end = src.indexOf('\n }\n', start) + 4;
|
|
240
|
+
const ctx = {};
|
|
241
|
+
vm.createContext(ctx);
|
|
242
|
+
vm.runInContext(`${src.slice(start, end)}\nthis.fn = acclaimEvidenceHtml;`, ctx);
|
|
243
|
+
return ctx.fn;
|
|
244
|
+
};
|
|
236
245
|
test('C: a graded, costed ship carries both numbers', () => {
|
|
237
246
|
const html = acclaimEvidence()({ grade_score: '8.5', grade_passed: true, cost_usd: '12.3456' });
|
|
238
247
|
assert.match(html, /ledger__verdict--ok">8\.5 \/ 10</, 'a passing grade reads as passing');
|
|
@@ -264,7 +273,7 @@ test('C: absence renders as absence, never as zero', () => {
|
|
|
264
273
|
});
|
|
265
274
|
|
|
266
275
|
test('C: the evidence line hangs under the work, in the feed row', () => {
|
|
267
|
-
const src = read('
|
|
276
|
+
const src = read('profile-own.js');
|
|
268
277
|
assert.match(
|
|
269
278
|
src,
|
|
270
279
|
/<td>\$\{title\}\$\{acclaimEvidenceHtml\(t\)\}<\/td>/,
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// tests/hall_goals_band.mjs — Roadmap folds into Goals (task 1005051, owner
|
|
2
|
+
// decision 2026-11-03, idea 1001315 item 4).
|
|
3
|
+
//
|
|
4
|
+
// Since one version is built at a time (task 1004821) the Roadmap page mostly
|
|
5
|
+
// repeated Goals. Goals now opens with the version's progress and the timeline:
|
|
6
|
+
//
|
|
7
|
+
// * THE BAND. goals.html carries #rm-root at the top of its list view, with
|
|
8
|
+
// roadmap.css, roadmap-timeline.js (still the drawing) and roadmap.js.
|
|
9
|
+
// * OLD LINKS LAND ON GOALS. /roadmap 302s to /goals (serve-internal.js
|
|
10
|
+
// RETIRED_TO_SECTION), query kept; the real middleware runs here.
|
|
11
|
+
// * NOTHING POINTS AT IT. No nav item, no Governing/Building tab, no founding
|
|
12
|
+
// room, and the Tasks board's "see the version" line names Goals.
|
|
13
|
+
//
|
|
14
|
+
// The band's own rendering (the timeline glue, a missing module, an undated
|
|
15
|
+
// version) is run in tests/hall_reading_world.mjs bootRoadmap.
|
|
16
|
+
//
|
|
17
|
+
// Run: node tests/hall_goals_band.mjs
|
|
18
|
+
|
|
19
|
+
import { test } from 'node:test';
|
|
20
|
+
import assert from 'node:assert/strict';
|
|
21
|
+
import fs from 'node:fs';
|
|
22
|
+
import http from 'node:http';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import { createRequire } from 'node:module';
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
26
|
+
|
|
27
|
+
const require = createRequire(import.meta.url);
|
|
28
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
29
|
+
const HALL = path.join(ROOT, 'modules', 'hall-ui', 'public');
|
|
30
|
+
const read = (f) => fs.readFileSync(path.join(HALL, f), 'utf8');
|
|
31
|
+
|
|
32
|
+
async function withHall(fn) {
|
|
33
|
+
const express = require('express');
|
|
34
|
+
const { mountInternalSurfaces } = require('../src/bongos/serve-internal.js');
|
|
35
|
+
const app = express();
|
|
36
|
+
mountInternalSurfaces(app);
|
|
37
|
+
const server = app.listen(0, '127.0.0.1');
|
|
38
|
+
await new Promise((r) => server.once('listening', r));
|
|
39
|
+
const { port } = server.address();
|
|
40
|
+
const get = (p, host) => new Promise((resolve, reject) => {
|
|
41
|
+
http.get({ host: '127.0.0.1', port, path: p, headers: { host } }, (res) => {
|
|
42
|
+
res.resume();
|
|
43
|
+
res.on('end', () => resolve({ status: res.statusCode, location: res.headers.location || '' }));
|
|
44
|
+
}).on('error', reject);
|
|
45
|
+
});
|
|
46
|
+
try { await fn(get); } finally { server.close(); }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test('RUN: /roadmap 302s to /goals, in every spelling, with its query', async () => {
|
|
50
|
+
await withHall(async (get) => {
|
|
51
|
+
for (const form of ['/roadmap', '/roadmap/', '/roadmap.html']) {
|
|
52
|
+
const r = await get(form, 'builders.127.0.0.1');
|
|
53
|
+
assert.equal(r.status, 302, `${form} is a temporary redirect`);
|
|
54
|
+
assert.equal(r.location, '/goals', `${form} lands on Goals, with no stray fragment`);
|
|
55
|
+
}
|
|
56
|
+
assert.equal((await get('/roadmap?from=mail', 'builders.127.0.0.1')).location, '/goals?from=mail');
|
|
57
|
+
// the map it joined still carries its sections
|
|
58
|
+
assert.equal((await get('/sessions', 'builders.127.0.0.1')).location, '/watch#sessions');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('Goals opens with the band, and loads what draws it', () => {
|
|
63
|
+
const html = read('goals.html');
|
|
64
|
+
const list = html.slice(html.indexOf('<section id="goals-list-view"'));
|
|
65
|
+
assert.ok(list.indexOf('id="rm-root"') > 0 && list.indexOf('id="rm-root"') < list.indexOf('class="scroll goals-panel"'),
|
|
66
|
+
'the band sits above the goals list');
|
|
67
|
+
assert.match(list, /<div id="rm-root"><div class="hall-loading" role="status">Loading the version…<\/div><\/div>/, 'with a Loading… line');
|
|
68
|
+
assert.match(html, /href="\/builders\/roadmap\.css"/);
|
|
69
|
+
const i = (s) => html.indexOf(s);
|
|
70
|
+
assert.ok(i('/builders/hall-kit.js') < i('/builders/roadmap-timeline.js'), 'the kit before the timeline');
|
|
71
|
+
assert.ok(i('/builders/roadmap-timeline.js') < i('/builders/roadmap.js'), 'the timeline before the band');
|
|
72
|
+
assert.ok(!fs.existsSync(path.join(HALL, 'roadmap.html')), 'the page itself is gone');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('the band draws the version and the timeline, and no goal summary of its own', () => {
|
|
76
|
+
const js = read('roadmap.js');
|
|
77
|
+
assert.match(js, /function versionCard\(v\)/);
|
|
78
|
+
assert.match(js, /html \+= timelineHtml\(all, Date\.now\(\)\)/);
|
|
79
|
+
assert.ok(!/\/goals\?version=|goalSummary|goalsBlock/.test(js), 'Goals already reads the goals; the band does not read them again');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('nothing points at the Roadmap any more', () => {
|
|
83
|
+
assert.ok(!/href: '\/roadmap'/.test(read('shell.js')), 'no nav item');
|
|
84
|
+
assert.ok(!/'roadmap'/.test(read('craft-mode.js')), 'no craft tab');
|
|
85
|
+
assert.ok(!/'roadmap'/.test(fs.readFileSync(path.join(ROOT, 'modules/onboarding/founding-mode.js'), 'utf8')), 'no founding room');
|
|
86
|
+
assert.ok(!/href="\/roadmap"/.test(read('work.html')), 'the Tasks board names Goals');
|
|
87
|
+
assert.match(read('work.html'), /See <a href="\/goals">Goals<\/a> for the version's progress/);
|
|
88
|
+
});
|