@bongos/core 1.25.26 → 1.25.28
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 +45 -30
- package/docs/copy-inventory.md +14 -11
- package/docs/copy-registry.json +42 -15
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +1 -0
- package/docs/page-readings.json +8 -8
- package/modules/hall-ui/public/home-camera.js +188 -0
- package/modules/hall-ui/public/home-city.css +14 -0
- package/modules/hall-ui/public/home-city.js +60 -5
- package/modules/hall-ui/public/index.html +6 -4
- package/modules/hall-ui/public/visor.css +37 -0
- package/modules/hall-ui/public/visor.js +140 -10
- package/modules/hall-ui/records/home-city.md +11 -0
- package/modules/hall-ui/records/visor.md +6 -0
- package/modules/ui-design/scripts/gen-design-reference.js +3 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +16 -0
- package/scripts/bongos/gen-brand.js +2 -1
- package/src/module-api.js +1 -1
- package/tests/brand_mark.mjs +2 -1
- package/tests/design_counts_ratchet.mjs +21 -0
- package/tests/design_reference.mjs +1 -1
- package/tests/hall_dialog.mjs +20 -1
- package/tests/hall_visor.mjs +74 -0
- package/tests/home_camera.mjs +34 -0
- package/tests/home_city_world.mjs +119 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// tests/home_city_world.mjs — game mode: Home's city is the whole view (task 1005305).
|
|
2
|
+
//
|
|
3
|
+
// Runs city-draw.js + home-city-draw.js + home-camera.js + home-city.js in a real page with
|
|
4
|
+
// html[data-game="on"]: the page holds nothing but the city and cannot scroll, the arrow keys
|
|
5
|
+
// move the world and never past its edge, the time is handed to the visor (OTBVisor.timeline)
|
|
6
|
+
// and a drag on it sends the city back through the weeks, and classic mode is as it was.
|
|
7
|
+
// Skips where no system Edge or Chrome is installed.
|
|
8
|
+
//
|
|
9
|
+
// Run: node --test tests/home_city_world.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 { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
const require = createRequire(import.meta.url);
|
|
18
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
19
|
+
const lib = require(path.join(ROOT, 'modules', 'ui-design', 'kit', 'lib.js'));
|
|
20
|
+
const pub = (f) => fs.readFileSync(path.join(ROOT, 'modules', 'hall-ui', 'public', f), 'utf8');
|
|
21
|
+
const FIXTURE = fs.readFileSync(path.join(ROOT, 'scripts', 'hall-preview', 'fixtures', 'home__city.json'), 'utf8');
|
|
22
|
+
|
|
23
|
+
let browser = null;
|
|
24
|
+
try { browser = await lib.launchBrowser(); } catch (e) { if (!(e instanceof lib.NoBrowserError)) throw e; }
|
|
25
|
+
after(() => browser && browser.close());
|
|
26
|
+
const skip = !browser && 'no system Edge or Chrome';
|
|
27
|
+
|
|
28
|
+
const stubs = `
|
|
29
|
+
window.__CITY = ${FIXTURE};
|
|
30
|
+
window.BongosClient = { createClient: () => ({ request: async (m, p) =>
|
|
31
|
+
(p.startsWith('/api/bongos/home/city') ? { ok: true, status: 200, data: window.__CITY } : { ok: false, status: 404, data: null }) }) };
|
|
32
|
+
window.OTBCraftMode = { iconHtml: () => '', choose: () => {}, current: () => 'artisan' };
|
|
33
|
+
document.documentElement.dataset.craft = 'artisan';
|
|
34
|
+
window.__tl = []; window.__scenes = [];
|
|
35
|
+
window.OTBVisor = { timeline: (t) => window.__tl.push(t), scene: (s) => window.__scenes.push(s), isOpen: () => false };
|
|
36
|
+
window.OTB = { contributeHallWidget(w) { window.__widget = w; } };`;
|
|
37
|
+
|
|
38
|
+
async function boot({ game = true } = {}) {
|
|
39
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 }, reducedMotion: 'reduce' });
|
|
40
|
+
const page = await ctx.newPage();
|
|
41
|
+
await page.setContent(`<!doctype html><html${game ? ' data-game="on"' : ''}><head><style>[hidden]{display:none!important}${pub('home-city.css')}</style></head><body>
|
|
42
|
+
<div class="page-head--hall">Hello</div><section id="home-city" class="scroll home-city" hidden></section>
|
|
43
|
+
<div id="zone-home-main" style="height:3000px">cards</div><footer class="app-foot">foot</footer></body></html>`);
|
|
44
|
+
await page.addScriptTag({ content: stubs });
|
|
45
|
+
for (const f of ['city-draw.js', 'home-city-draw.js', 'home-camera.js', 'home-city.js']) await page.addScriptTag({ content: pub(f) });
|
|
46
|
+
await page.evaluate(() => window.__widget.mount(document.getElementById('home-city')));
|
|
47
|
+
await page.waitForFunction(() => document.querySelector('.hc-svg').childNodes.length > 0);
|
|
48
|
+
return { ctx, page };
|
|
49
|
+
}
|
|
50
|
+
const shown = (page, sel) => page.evaluate((s) => { const e = document.querySelector(s); return !!e && getComputedStyle(e).display !== 'none'; }, sel);
|
|
51
|
+
const worldX = (page) => page.evaluate(() => Math.round(document.querySelector('.hc-svg').getBoundingClientRect().x));
|
|
52
|
+
const lastTl = (page, pick) => page.evaluate(pick);
|
|
53
|
+
|
|
54
|
+
test('game mode: the page is only the city, it cannot scroll, and the world is larger than the view', { skip }, async () => {
|
|
55
|
+
const { ctx, page } = await boot();
|
|
56
|
+
const s = await page.evaluate(() => {
|
|
57
|
+
const r = document.querySelector('.hc-svg').getBoundingClientRect(), h = document.documentElement;
|
|
58
|
+
window.scrollTo(0, 500);
|
|
59
|
+
return { world: h.classList.contains('hc-world'), overflow: getComputedStyle(h).overflowY, scrolled: window.scrollY, w: r.width, h: r.height, x: r.x, y: r.y };
|
|
60
|
+
});
|
|
61
|
+
const parts = {};
|
|
62
|
+
for (const sel of ['.page-head--hall', '#zone-home-main', '.app-foot', '.hc-ladder', '.hc-time', '.hc-svg']) parts[sel] = await shown(page, sel);
|
|
63
|
+
await ctx.close();
|
|
64
|
+
assert.equal(s.world, true);
|
|
65
|
+
assert.equal(s.overflow, 'hidden');
|
|
66
|
+
assert.equal(s.scrolled, 0, 'the page does not scroll');
|
|
67
|
+
assert.ok(s.w > 1280 && s.h > 800, 'the city is a world larger than the view');
|
|
68
|
+
assert.ok(s.x < 0 && s.y < 0, 'the view opens on the middle of it');
|
|
69
|
+
assert.deepEqual(parts, { '.page-head--hall': false, '#zone-home-main': false, '.app-foot': false, '.hc-ladder': false, '.hc-time': false, '.hc-svg': true });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('game mode: the arrow keys move the city, and never past its edge', { skip }, async () => {
|
|
73
|
+
const { ctx, page } = await boot();
|
|
74
|
+
const start = await worldX(page);
|
|
75
|
+
await page.keyboard.down('ArrowRight');
|
|
76
|
+
await page.waitForFunction((x0) => Math.round(document.querySelector('.hc-svg').getBoundingClientRect().x) < x0 - 40, start);
|
|
77
|
+
await page.keyboard.up('ArrowRight');
|
|
78
|
+
await page.keyboard.down('ArrowLeft');
|
|
79
|
+
await page.waitForFunction(() => Math.round(document.querySelector('.hc-svg').getBoundingClientRect().x) === 0, null, { timeout: 15000 });
|
|
80
|
+
await page.waitForTimeout(200);
|
|
81
|
+
const end = await worldX(page);
|
|
82
|
+
const scrolled = await page.evaluate(() => window.scrollX + window.scrollY);
|
|
83
|
+
await page.keyboard.up('ArrowLeft');
|
|
84
|
+
await ctx.close();
|
|
85
|
+
assert.equal(end, 0, 'held against the left edge, the world stops at it');
|
|
86
|
+
assert.equal(scrolled, 0);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('game mode: time is on the visor; a drag sends the city back, and Home registers its rows, keys and the cursor sight', { skip }, async () => {
|
|
90
|
+
const { ctx, page } = await boot();
|
|
91
|
+
const read = () => lastTl(page, () => { const t = window.__tl[window.__tl.length - 1]; return { live: t.live, value: t.value, label: t.label, speed: t.speed, len: document.querySelector('.hc-svg').innerHTML.length }; });
|
|
92
|
+
const before = await read();
|
|
93
|
+
await page.evaluate(() => window.__tl[window.__tl.length - 1].seek(0.3));
|
|
94
|
+
const back = await read();
|
|
95
|
+
await page.evaluate(() => window.__tl[window.__tl.length - 1].faster());
|
|
96
|
+
const sped = (await read()).speed;
|
|
97
|
+
const scene = await page.evaluate(() => { const s = window.__visorScene; return { aim: s.aim, keys: s.keys.map((k) => k.key), rows: s.actions.map((a) => a.label) }; });
|
|
98
|
+
await page.evaluate(() => window.__tl[window.__tl.length - 1].toLive());
|
|
99
|
+
const live = (await read()).live;
|
|
100
|
+
await ctx.close();
|
|
101
|
+
assert.deepEqual([before.live, before.value, before.label, before.speed], [true, 1, 'Live', '1×']);
|
|
102
|
+
assert.equal(back.live, false);
|
|
103
|
+
assert.ok(Math.abs(back.value - 0.3) < 0.01 && back.label !== 'Live', 'the city is where the drag sent it: ' + back.label);
|
|
104
|
+
assert.notEqual(back.len, before.len, 'the city is redrawn for that week');
|
|
105
|
+
assert.equal(sped, '2×');
|
|
106
|
+
assert.equal(scene.aim, true);
|
|
107
|
+
assert.deepEqual(scene.keys, ['Arrows']);
|
|
108
|
+
assert.ok(scene.rows.includes('Play the city over time') && scene.rows.includes('Back to live') && scene.rows.some((r) => r.startsWith('Speed')), scene.rows.join(' | '));
|
|
109
|
+
assert.equal(live, true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('classic mode is as it was: the ladder, the slider and the cards are on the page, and no timeline is handed over', { skip }, async () => {
|
|
113
|
+
const { ctx, page } = await boot({ game: false });
|
|
114
|
+
const s = await page.evaluate(() => ({ world: document.documentElement.classList.contains('hc-world'), tl: window.__tl.filter(Boolean).length, aim: window.__visorScene.aim, transform: document.querySelector('.hc-svg').style.transform }));
|
|
115
|
+
const parts = { ladder: await shown(page, '.hc-ladder'), time: await shown(page, '.hc-time'), cards: await shown(page, '#zone-home-main') };
|
|
116
|
+
await ctx.close();
|
|
117
|
+
assert.deepEqual(s, { world: false, tl: 0, aim: false, transform: '' });
|
|
118
|
+
assert.deepEqual(parts, { ladder: true, time: true, cards: true });
|
|
119
|
+
});
|