@bongos/core 1.25.2 → 1.25.4
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 +55 -40
- package/docs/copy-inventory.md +30 -29
- package/docs/copy-registry.json +78 -58
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +5 -3
- package/docs/page-readings.json +44 -44
- package/modules/hall-ui/public/builders.js +3 -27
- package/modules/hall-ui/public/genesis-home.js +54 -4
- package/modules/hall-ui/public/hall-render.js +5 -119
- package/modules/hall-ui/public/hall-widgets.js +3 -3
- package/modules/hall-ui/public/index-genesis-votes.states.json +61 -0
- package/modules/hall-ui/public/index.html +0 -7
- package/modules/hall-ui/public/shell-game.js +22 -4
- package/modules/hall-ui/public/style.css +3 -73
- package/modules/hall-ui/public/visor.css +31 -1
- package/modules/hall-ui/public/visor.js +53 -3
- package/modules/hall-ui/records/home-reshuffle.md +1 -1
- package/modules/hall-ui/records/visor.md +10 -0
- package/modules/onboarding/founding-mode.js +4 -1
- package/modules/onboarding/genesis-home.js +57 -2
- package/modules/onboarding/routes/onboarding.js +2 -0
- package/modules/ui-design/kit/fixtures/founding-genesis-votes.json +193 -0
- package/modules/ui-design/kit/fixtures/me-founding-demo.json +0 -1
- package/modules/ui-design/kit/fixtures/me-founding.json +0 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/module-api.js +1 -1
- package/tests/founding_mode.mjs +13 -3
- package/tests/genesis_home.mjs +92 -4
- package/tests/hall_board_world.mjs +7 -35
- package/tests/hall_home_ships_bounded.mjs +11 -27
- package/tests/hall_landing_boot.mjs +7 -8
- package/tests/hall_nav.mjs +2 -2
- package/tests/hall_visor.mjs +9 -2
- package/tests/hall_visor_profile.mjs +150 -0
- package/tests/leaderboard_roles.mjs +3 -17
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
// table and a count. This pins the bounded shape:
|
|
8
8
|
//
|
|
9
9
|
// 1. the route answers a small page plus `total`, counting only when it must;
|
|
10
|
-
// 2.
|
|
11
|
-
//
|
|
12
|
-
// newest ships or the count moved.
|
|
10
|
+
// 2. Home asks for no ships at all (task 1005249 removed the last count it
|
|
11
|
+
// showed); the Profile page reads its own ledger.
|
|
13
12
|
//
|
|
14
13
|
// (builders.js's own request is pinned in tests/hall_landing_boot.mjs, which
|
|
15
14
|
// already boots the real page.) DB-free: vm + stubs.
|
|
@@ -120,31 +119,16 @@ function loadRender({ view = 'home' } = {}) {
|
|
|
120
119
|
return { r, el, fetched, showView };
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
assert.equal(r.HOME_SHIPS_LIMIT,
|
|
130
|
-
r.
|
|
131
|
-
await new Promise((done) => setTimeout(done, 0));
|
|
132
|
-
assert.deepEqual(fetched, [], 'Home never asks for the full history');
|
|
133
|
-
assert.equal(el('standing-shipped').textContent, '3000', 'the chip shows the server total, not rows held');
|
|
134
|
-
assert.equal(el('recent-ships tbody').innerHTML, '', 'the ledger is untouched until its view opens');
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// task 1005050: the full ledger was the Accomplishments view's, and that view
|
|
138
|
-
// folded into the Profile page (whose Works ledger reads its own record). Home
|
|
139
|
-
// now NEVER asks for the whole history — not on a view change, not on a refresh.
|
|
140
|
-
test('Home never downloads the whole history, whatever view event or refresh arrives', async () => {
|
|
141
|
-
const { r, el, fetched, showView } = loadRender({ view: 'standing' });
|
|
142
|
-
r.setRecentShips(homePage(3000));
|
|
122
|
+
// task 1005249: the Standing chip, the last thing on Home that showed a shipped
|
|
123
|
+
// count, is gone, so Home keeps no ships at all — not a small page, not a count —
|
|
124
|
+
// and asks for none, whatever view event arrives.
|
|
125
|
+
test('Home keeps and asks for no ships at all, whatever view event arrives', async () => {
|
|
126
|
+
const { r, fetched, showView } = loadRender({ view: 'home' });
|
|
127
|
+
assert.equal(r.setRecentShips, undefined, 'no ships setter');
|
|
128
|
+
assert.equal(r.HOME_SHIPS_LIMIT, undefined, 'no small page size');
|
|
129
|
+
assert.equal(r.loadShipLedger, undefined, 'no ledger loader');
|
|
143
130
|
showView('standing');
|
|
144
|
-
r.setRecentShips(homePage(3001, 3001)); // this builder shipped
|
|
145
131
|
await new Promise((done) => setTimeout(done, 0));
|
|
146
|
-
assert.deepEqual(fetched, [], 'no
|
|
147
|
-
assert.equal(el('standing-shipped').textContent, '3001', 'the chip follows the newest total');
|
|
148
|
-
assert.equal(r.loadShipLedger, undefined, 'the ledger loader went with the view');
|
|
132
|
+
assert.deepEqual(fetched, [], 'no ships read from Home');
|
|
149
133
|
});
|
|
150
134
|
|
|
@@ -91,10 +91,9 @@ async function boot({ meStatus, settle = false }) {
|
|
|
91
91
|
wirePager: () => {},
|
|
92
92
|
},
|
|
93
93
|
OTBHallRender: () => ({
|
|
94
|
-
analyticsState: {},
|
|
94
|
+
analyticsState: {},
|
|
95
95
|
shipsPager: { page: 1 },
|
|
96
|
-
|
|
97
|
-
HOME_SHIPS_LIMIT: 5, setRecentShips: spy('setRecentShips'), populateShipVersionFilter: spy('populateShipVersionFilter'),
|
|
96
|
+
populateShipVersionFilter: spy('populateShipVersionFilter'),
|
|
98
97
|
mountHallWidgets: spy('mountHallWidgets'),
|
|
99
98
|
renderLanding: spy('renderLanding'),
|
|
100
99
|
renderCurveBody: spy('renderCurveBody'), renderGreeting: spy('renderGreeting'),
|
|
@@ -154,10 +153,10 @@ test('a signed-in builder still gets the hall', async () => {
|
|
|
154
153
|
|
|
155
154
|
// task 1004854: Home used to download the builder's WHOLE ship history here, on
|
|
156
155
|
// every load and every live refresh — ~3.4 s of database time for 3,000 ships, to
|
|
157
|
-
// fill a five-row table and a count.
|
|
158
|
-
|
|
156
|
+
// fill a five-row table and a count. Since task 1005249 it shows no ship count at
|
|
157
|
+
// all (the Standing chip went), so it asks for no ships and no leaderboard.
|
|
158
|
+
test('Home asks for no ships and no leaderboard', async () => {
|
|
159
159
|
const calls = await boot({ meStatus: 200, settle: true });
|
|
160
|
-
const
|
|
161
|
-
assert.deepEqual(
|
|
162
|
-
assert.ok(calls.rendered.includes('setRecentShips'), 'the small page is handed to the renderer');
|
|
160
|
+
const reads = calls.requests.filter((r) => r.includes('/me/recent-ships') || r.includes('/leaderboard'));
|
|
161
|
+
assert.deepEqual(reads, [], `Home still read: ${reads.join(', ')}`);
|
|
163
162
|
});
|
package/tests/hall_nav.mjs
CHANGED
|
@@ -905,9 +905,9 @@ test('Accomplishments is gone from the sidebar, and every old link lands on your
|
|
|
905
905
|
}
|
|
906
906
|
// No view is left for them to open on the index.
|
|
907
907
|
assert.ok(!/id="view-standing"/.test(fs.readFileSync(path.join(HALL, 'index.html'), 'utf8')), 'the view is gone');
|
|
908
|
-
// Home's Standing card
|
|
908
|
+
// Home's Standing card went in task 1005249: the visor's you-block opens the profile now
|
|
909
|
+
// (tests/hall_visor_profile.mjs runs it).
|
|
909
910
|
const render = fs.readFileSync(path.join(HALL, 'hall-render.js'), 'utf8');
|
|
910
|
-
assert.match(render, /View full profile →/);
|
|
911
911
|
assert.ok(!/\$\('#profile-body'\)/.test(render), 'the Accomplishments band is not drawn any more');
|
|
912
912
|
const fn = render.slice(render.indexOf(' function renderProfile(builder) {'), render.indexOf(' function renderSystemNotices('));
|
|
913
913
|
assert.match(fn, /renderSystemNotices\(\{ rebaseTasks, need, actionNeeds, cost \}\)/, 'Home still gets every notice');
|
package/tests/hall_visor.mjs
CHANGED
|
@@ -503,9 +503,16 @@ test('the glass is the kit\'s: the cut accent button, ice hints with no box, the
|
|
|
503
503
|
assert.equal(g.placeShadow, 'none', 'type on a plate needs no halo');
|
|
504
504
|
assert.equal(g.radius, '0px', 'no round thing on the glass');
|
|
505
505
|
assert.match(g.spriteClip, /^polygon\(/, 'the sprite sits in a cut box');
|
|
506
|
+
// task 1005249: you (left of the foot row) are a link to your profile now, so the
|
|
507
|
+
// stops run in reading order: you, then the accent button. Both take the inset ring.
|
|
508
|
+
const focused = () => page.evaluate(() => { const a = document.activeElement; const s = getComputedStyle(a); return { cls: a.className, outline: s.outlineStyle, ring: s.boxShadow }; });
|
|
506
509
|
await page.keyboard.press('Tab');
|
|
507
|
-
const
|
|
508
|
-
assert.equal(
|
|
510
|
+
const me = await focused();
|
|
511
|
+
assert.equal(me.cls, 'visor__me', 'you are the first stop, reading left to right');
|
|
512
|
+
assert.match(me.ring, /inset/, 'you take the inset ring too');
|
|
513
|
+
await page.keyboard.press('Tab');
|
|
514
|
+
const f = await focused();
|
|
515
|
+
assert.equal(f.cls, 'visor__main', 'the accent button is the next stop');
|
|
509
516
|
assert.equal(f.outline, 'none');
|
|
510
517
|
assert.match(f.ring, /inset/, 'focus is the inset two-tone ring (--press-focus)');
|
|
511
518
|
await ctx.close();
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// tests/hall_visor_profile.mjs — you on the visor (task 1005249).
|
|
2
|
+
//
|
|
3
|
+
// The owner's ask (2026-11-12): the Standing chip that sat top right of Home is gone;
|
|
4
|
+
// the visor's bottom-left you-block shows your details on hover and opens your whole
|
|
5
|
+
// profile, in the game's window over the place, on click. This runs the real visor,
|
|
6
|
+
// the real shell-game window and the real dialog in a browser, plus the pure facts in
|
|
7
|
+
// a vm, and checks Home's page no longer carries the chip.
|
|
8
|
+
//
|
|
9
|
+
// Run: node --test tests/hall_visor_profile.mjs
|
|
10
|
+
import assert from 'node:assert/strict';
|
|
11
|
+
import { test, after } from 'node:test';
|
|
12
|
+
import { createRequire } from 'node:module';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import vm from 'node:vm';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
|
|
18
|
+
const require = createRequire(import.meta.url);
|
|
19
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
20
|
+
const HALL = path.join(ROOT, 'modules/hall-ui/public');
|
|
21
|
+
const read = (f) => fs.readFileSync(path.join(HALL, f), 'utf8');
|
|
22
|
+
|
|
23
|
+
// ---------- the facts, in a vm ----------
|
|
24
|
+
function loadVisor(me) {
|
|
25
|
+
const doc = {
|
|
26
|
+
readyState: 'complete', body: null, documentElement: { dataset: {}, classList: { contains: () => false, toggle() {}, remove() {} } },
|
|
27
|
+
addEventListener() {}, querySelector: () => null, createElement: () => ({}),
|
|
28
|
+
};
|
|
29
|
+
const win = { document: doc, location: { pathname: '/', assign() {} }, addEventListener() {}, OTBCraftMode: { me: () => me } };
|
|
30
|
+
win.window = win;
|
|
31
|
+
vm.runInNewContext(read('visor.js'), win);
|
|
32
|
+
return win.OTBVisor;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test('the card\'s facts come from /me: name, handle, rank, credits, karma, preferred work', () => {
|
|
36
|
+
const me = { builder: { display_name: 'Percy Jackson', github_login: 'percy', rank: 'metic', total_credits: 14441, karma: 96.4, preferred_disciplines: ['artisan', 'ui'] } };
|
|
37
|
+
const f = JSON.parse(JSON.stringify(loadVisor(me).meFacts(me)));
|
|
38
|
+
assert.deepEqual(f, { name: 'Percy Jackson', handle: '@percy', rank: 'Metic', credits: 14441, karma: 96, prefs: 'artisan, ui' });
|
|
39
|
+
const bare = JSON.parse(JSON.stringify(loadVisor(null).meFacts({ builder: { github_login: 'x' } })));
|
|
40
|
+
assert.equal(bare.name, 'x', 'no display name: the login');
|
|
41
|
+
assert.equal(bare.karma, 0);
|
|
42
|
+
assert.equal(bare.prefs, '', 'no preference: empty, and the card says "Not set yet"');
|
|
43
|
+
assert.equal(loadVisor(null).meFacts(null), null, 'before /me answers there is nothing to show');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('Home carries no Standing chip any more, and no sheet styles one', () => {
|
|
47
|
+
assert.doesNotMatch(read('index.html'), /hall-standing|home-standing-body/);
|
|
48
|
+
assert.doesNotMatch(read('style.css'), /\.hall-standing|\.home-who|\.home-facts/);
|
|
49
|
+
assert.doesNotMatch(read('hall-render.js'), /home-standing-body|hall-standing|leaderboardPlace/);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ---------- in a browser ----------
|
|
53
|
+
const lib = require(path.join(ROOT, 'modules', 'ui-design', 'kit', 'lib.js'));
|
|
54
|
+
let browser = null;
|
|
55
|
+
try { browser = await lib.launchBrowser(); } catch (e) { if (!(e instanceof lib.NoBrowserError)) throw e; }
|
|
56
|
+
after(() => browser && browser.close());
|
|
57
|
+
const skip = !browser && 'no system Edge or Chrome';
|
|
58
|
+
|
|
59
|
+
const ORIGIN = 'http://hall.test';
|
|
60
|
+
const TYPES = { '.js': 'text/javascript', '.css': 'text/css', '.png': 'image/png', '.svg': 'image/svg+xml' };
|
|
61
|
+
const SCENE = `<script>
|
|
62
|
+
window.OTBCraftMode = { character() { return 'artisan'; }, current() { return null; },
|
|
63
|
+
me() { return { builder: { display_name: 'Percy Jackson', github_login: 'percy', rank: 'metic', total_credits: 14441, karma: 96, preferred_disciplines: [] } }; },
|
|
64
|
+
spriteSrc(id) { return '/builders/characters/' + id + '.png'; } };
|
|
65
|
+
window.__visorScene = { name: 'test', place: 'Test city', actions: [], keys: [] };
|
|
66
|
+
</script>`;
|
|
67
|
+
const PAGE = (withWindow) => `<!doctype html><html data-theme="dark" data-faces="helm"><head><meta charset="utf-8">
|
|
68
|
+
<link rel="stylesheet" href="/builders/style.css"><link rel="stylesheet" href="/builders/visor.css"></head>
|
|
69
|
+
<body>${SCENE}<script src="/builders/hall-dialog.js"></script>${withWindow ? '<script src="/builders/shell-game.js"></script>' : ''}<script src="/builders/visor.js"></script></body></html>`;
|
|
70
|
+
|
|
71
|
+
async function open({ withWindow = true } = {}) {
|
|
72
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } });
|
|
73
|
+
await ctx.route(ORIGIN + '/**', (route) => {
|
|
74
|
+
const p = new URL(route.request().url()).pathname;
|
|
75
|
+
if (p === '/') return route.fulfill({ contentType: 'text/html', body: PAGE(withWindow) });
|
|
76
|
+
if (p === '/profile') return route.fulfill({ contentType: 'text/html', body: '<!doctype html><title>profile</title><p id="pp">your profile</p><script src="/builders/shell-game.js"></script>' }); // the real page carries the game shell, which hands an Escape up
|
|
77
|
+
const f = path.join(HALL, p.replace(/^\/builders\//, ''));
|
|
78
|
+
if (p.startsWith('/builders/') && TYPES[path.extname(p)] && fs.existsSync(f)) return route.fulfill({ contentType: TYPES[path.extname(p)], body: fs.readFileSync(f) });
|
|
79
|
+
return route.fulfill({ status: 404, body: '' });
|
|
80
|
+
});
|
|
81
|
+
const page = await ctx.newPage();
|
|
82
|
+
const errors = [];
|
|
83
|
+
page.on('pageerror', (e) => errors.push(String(e.message)));
|
|
84
|
+
await page.addInitScript(() => { try { localStorage.setItem('hall-game', 'on'); document.documentElement.dataset.game = 'on'; window.OTB = { escapeHtml: (s) => String(s) }; } catch (_) { /* none */ } });
|
|
85
|
+
await page.goto(ORIGIN + '/');
|
|
86
|
+
await page.waitForFunction(() => !!document.querySelector('.visor__me'));
|
|
87
|
+
return { ctx, page, errors };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
test('hovering you on the visor shows your details; at rest they are hidden', { skip }, async () => {
|
|
91
|
+
const { ctx, page, errors } = await open();
|
|
92
|
+
const shown = () => page.evaluate(() => getComputedStyle(document.querySelector('.visor__card')).visibility);
|
|
93
|
+
assert.equal(await shown(), 'hidden', 'nothing over the scene at rest');
|
|
94
|
+
await page.hover('.visor__me');
|
|
95
|
+
await page.waitForFunction(() => getComputedStyle(document.querySelector('.visor__card')).opacity === '1');
|
|
96
|
+
assert.equal(await shown(), 'visible');
|
|
97
|
+
const text = await page.evaluate(() => [...document.querySelectorAll('.visor__card > span')].map((s) => s.innerText.replace(/\s+/g, ' ').trim()));
|
|
98
|
+
assert.match(text[0], /Percy Jackson @percy/i);
|
|
99
|
+
assert.match(text[1], /Rank Metic/i);
|
|
100
|
+
assert.match(text[1], /Credits 14,441/i);
|
|
101
|
+
assert.match(text[1], /Karma 96/i);
|
|
102
|
+
assert.match(text[1], /Preferred work Not set yet/i);
|
|
103
|
+
assert.match(text[2], /Open your profile/i);
|
|
104
|
+
const box = await page.evaluate(() => { const r = document.querySelector('.visor__card').getBoundingClientRect(); return { top: r.top, left: r.left }; });
|
|
105
|
+
assert.ok(box.top >= 0 && box.left >= 0, 'the card stays on screen');
|
|
106
|
+
assert.deepEqual(errors, []);
|
|
107
|
+
await ctx.close();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('clicking you opens your whole profile in the window over the place; Escape closes it', { skip }, async () => {
|
|
111
|
+
const { ctx, page, errors } = await open();
|
|
112
|
+
await page.click('.visor__me');
|
|
113
|
+
await page.waitForFunction(() => window.OTBOptionsOverlay.isOpen());
|
|
114
|
+
const w = await page.evaluate(() => ({
|
|
115
|
+
url: location.pathname,
|
|
116
|
+
src: document.querySelector('.opt-window__frame').getAttribute('src'),
|
|
117
|
+
label: document.querySelector('.opt-window__box').getAttribute('aria-label'),
|
|
118
|
+
close: document.querySelector('.opt-window__x').getAttribute('aria-label'),
|
|
119
|
+
}));
|
|
120
|
+
assert.equal(w.url, '/', 'the place stays: no navigation');
|
|
121
|
+
assert.equal(w.src, '/profile?embed=1');
|
|
122
|
+
assert.equal(w.label, 'Your profile');
|
|
123
|
+
assert.equal(w.close, 'Close your profile');
|
|
124
|
+
// the frame's own shell must be up before an Escape inside it can be handed up
|
|
125
|
+
await page.waitForFunction(() => { const f = document.querySelector('.opt-window__frame'); try { return !!(f && f.contentDocument.readyState === 'complete' && f.contentDocument.documentElement.dataset.embed === 'on'); } catch (_) { return false; } });
|
|
126
|
+
await page.keyboard.press('Escape');
|
|
127
|
+
await page.waitForFunction(() => !window.OTBOptionsOverlay.isOpen());
|
|
128
|
+
// the same window still opens Options under its own name
|
|
129
|
+
await page.evaluate(() => window.OTBOptionsOverlay.open(''));
|
|
130
|
+
assert.equal(await page.evaluate(() => document.querySelector('.opt-window__box').getAttribute('aria-label')), 'Options');
|
|
131
|
+
assert.equal(await page.evaluate(() => document.querySelector('.opt-window__frame').getAttribute('src')), '/settings?embed=1');
|
|
132
|
+
assert.deepEqual(errors, []);
|
|
133
|
+
await ctx.close();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('with no window to draw, you are still a link to your profile', { skip }, async () => {
|
|
137
|
+
const { ctx, page, errors } = await open({ withWindow: false });
|
|
138
|
+
const r = await page.evaluate(() => {
|
|
139
|
+
const me = document.querySelector('.visor__me');
|
|
140
|
+
const ev = new MouseEvent('click', { bubbles: true, cancelable: true });
|
|
141
|
+
// stop the browser before it leaves, once the visor has had its say
|
|
142
|
+
window.addEventListener('click', (e) => { window.__left = !e.defaultPrevented; e.preventDefault(); });
|
|
143
|
+
me.querySelector('.visor__name').dispatchEvent(ev);
|
|
144
|
+
return { href: me.getAttribute('href'), followed: window.__left };
|
|
145
|
+
});
|
|
146
|
+
assert.equal(r.href, '/profile');
|
|
147
|
+
assert.equal(r.followed, true, 'the visor lets the link take you to the profile page');
|
|
148
|
+
assert.deepEqual(errors, []);
|
|
149
|
+
await ctx.close();
|
|
150
|
+
});
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// that craft earned, and a dash, not a 0, for a builder with no AI sessions.
|
|
17
17
|
// (Two crafts since task 1005141: the engineer is the artisan, and the
|
|
18
18
|
// ideator folded into the artist, so idea credits rank as artist credits.)
|
|
19
|
-
//
|
|
19
|
+
// 5. THE STATUS PAGE: the real function, run. (4, the Standing chip, went in task 1005249.)
|
|
20
20
|
|
|
21
21
|
import { strict as assert } from 'node:assert';
|
|
22
22
|
import { createRequire } from 'node:module';
|
|
@@ -234,7 +234,7 @@ await test('the Artists toggle shows only artists, ranked by the credits that cr
|
|
|
234
234
|
assert.equal(r.rows.length, FIX.leaderboard.length, 'Everyone brings every builder back');
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
-
// ---- 4. the standing card
|
|
237
|
+
// ---- 4. (the standing card, gone; extract() serves section 5) ---------------------
|
|
238
238
|
|
|
239
239
|
function extract(src, start, end) {
|
|
240
240
|
const a = src.indexOf(start);
|
|
@@ -243,21 +243,7 @@ function extract(src, start, end) {
|
|
|
243
243
|
return src.slice(a, b);
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
const src = read('modules/hall-ui/public/hall-render.js');
|
|
248
|
-
const code = extract(src, 'const lbState = { rows: [] };', ' return { lbState');
|
|
249
|
-
const sandbox = { rows: FIX.leaderboard };
|
|
250
|
-
vm.createContext(sandbox);
|
|
251
|
-
vm.runInContext(`${code}; lbState.rows = rows; this.place = leaderboardPlace; this.text = rolePlaceText;`, sandbox);
|
|
252
|
-
const art = sandbox.place('builder-7');
|
|
253
|
-
assert.equal(art.role.craft, 'artist');
|
|
254
|
-
assert.equal(sandbox.text(art), `#2 as an artist`, 'fourth overall, second among artists: something new, so it is said');
|
|
255
|
-
const top = sandbox.place(FIX.leaderboard[0].github_login);
|
|
256
|
-
assert.equal(sandbox.text(top), '', 'the top artisan is #1 either way, so nothing is added');
|
|
257
|
-
// task 1005054: the stats strip (and its rank tile) left Home; the Standing chip's card carries the role place
|
|
258
|
-
assert.match(src, /\$\{rolePlaceText\(rollPlace\) \? `<span title="Your place among the/);
|
|
259
|
-
assert.match(read('modules/hall-ui/public/builders.js'), /getJSON\(`\$\{API\}\/public\/leaderboard`\)/, 'the hall reads the list with the roles');
|
|
260
|
-
});
|
|
246
|
+
// (Its test of the Standing chip's role place went with the chip in task 1005249.)
|
|
261
247
|
|
|
262
248
|
// ---- 5. the status page -----------------------------------------------------------
|
|
263
249
|
|