@bongos/core 1.24.48 → 1.24.50
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 +152 -112
- package/config/branding.neutral.json +2 -1
- package/docs/copy-inventory.md +1108 -1108
- package/docs/copy-registry.json +1557 -1557
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +5 -4
- package/docs/page-readings.json +681 -681
- package/modules/hall-ui/public/ai-studio.html +2 -2
- package/modules/hall-ui/public/atlas.html +2 -2
- package/modules/hall-ui/public/blockers.html +2 -2
- package/modules/hall-ui/public/board-room.html +2 -2
- package/modules/hall-ui/public/city.html +1 -1
- package/modules/hall-ui/public/collab.html +2 -2
- package/modules/hall-ui/public/commands.html +2 -2
- package/modules/hall-ui/public/copy-desk.html +2 -2
- package/modules/hall-ui/public/deploy.html +2 -2
- package/modules/hall-ui/public/goal-map.html +2 -2
- package/modules/hall-ui/public/goals.html +2 -2
- package/modules/hall-ui/public/government.html +2 -2
- package/modules/hall-ui/public/hall-render.js +1 -1
- package/modules/hall-ui/public/idea.html +2 -2
- package/modules/hall-ui/public/ideas.html +2 -2
- package/modules/hall-ui/public/index.html +5 -5
- package/modules/hall-ui/public/market-watch-item.html +2 -2
- package/modules/hall-ui/public/market-watch-rules.html +2 -2
- package/modules/hall-ui/public/market-watch.html +2 -2
- package/modules/hall-ui/public/modules.html +2 -2
- package/modules/hall-ui/public/money.html +2 -2
- package/modules/hall-ui/public/my-agents.html +2 -2
- package/modules/hall-ui/public/primer.html +2 -2
- package/modules/hall-ui/public/profile.html +2 -2
- package/modules/hall-ui/public/project-settings.html +2 -2
- package/modules/hall-ui/public/roster.html +2 -2
- package/modules/hall-ui/public/settings.html +2 -2
- package/modules/hall-ui/public/shell.js +1 -1
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/studio.html +2 -2
- package/modules/hall-ui/public/style.css +7 -9
- package/modules/hall-ui/public/task.html +2 -2
- package/modules/hall-ui/public/thinking.html +2 -2
- package/modules/hall-ui/public/tweak-editor.html +2 -2
- package/modules/hall-ui/public/watch.html +2 -2
- package/modules/hall-ui/public/welcome-boot.css +3 -4
- package/modules/hall-ui/public/welcome-boot.js +5 -3
- package/modules/hall-ui/public/work.html +2 -2
- package/modules/public-landing/public/account.html +1 -5
- package/modules/public-landing/public/assets/cosmos.css +7 -6
- package/modules/public-landing/public/community.html +1 -5
- package/modules/public-landing/public/connections.html +1 -5
- package/modules/public-landing/public/contact.html +1 -5
- package/modules/public-landing/public/guild.html +1 -5
- package/modules/public-landing/public/index.html +5 -8
- package/modules/public-landing/public/invitations.html +1 -5
- package/modules/public-landing/public/privacy.html +1 -5
- package/modules/public-landing/public/projects.html +1 -5
- package/modules/public-landing/public/terms.html +1 -5
- package/modules/raise/views/gate.html +4 -3
- package/modules/raise/views/nda.html +2 -1
- package/modules/raise/views/page.html +4 -3
- package/modules/status-ui/public/explore.html +14 -4
- package/modules/status-ui/public/index.html +26 -126
- package/modules/status-ui/public/index.states.json +3 -3
- package/modules/status-ui/public/status.js +53 -27
- package/modules/status-ui/public/style.css +640 -833
- package/modules/ui-design/public/brand/apple-touch-icon.png +0 -0
- package/modules/ui-design/public/brand/favicon.svg +1 -0
- package/modules/ui-design/public/brand/mark-body.svg +1 -0
- package/modules/ui-design/public/brand/mark-dark.svg +1 -0
- package/modules/ui-design/public/brand/mark-dot.svg +1 -0
- package/modules/ui-design/public/brand/mark-light.svg +1 -0
- package/modules/ui-design/public/brand/mark-mono.svg +1 -0
- package/modules/ui-design/public/brand/mark.svg +21 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/bongos/fitness.js +1 -2
- package/scripts/bongos/gen-brand.js +99 -0
- package/scripts/gds/gen-brand.js +7 -0
- package/scripts/status-mirror-sync.js +44 -45
- package/src/bongos/platform-visibility-gate.js +6 -3
- package/src/bongos/prelaunch-gate.js +6 -3
- package/src/bongos/serve-internal.js +9 -7
- package/src/brand-mark.js +96 -0
- package/src/branding.js +2 -0
- package/src/hall-look.js +7 -7
- package/src/module-api.js +1 -1
- package/tests/brand_mark.mjs +195 -0
- package/tests/cloud_bongos_pack.mjs +5 -2
- package/tests/hall_loading.mjs +8 -10
- package/tests/loading_mark.mjs +7 -13
- package/tests/nav_permission_atoms.mjs +2 -1
- package/tests/raise.mjs +3 -1
- package/tests/status_page_chrome.mjs +195 -0
- package/tests/welcome_boot.mjs +5 -2
- package/modules/raise/public/favicon.svg +0 -1
- package/modules/raise/public/logo-ink.svg +0 -1
- package/modules/raise/public/logo-night.svg +0 -1
- package/modules/status-ui/public/cursors/arrow.png +0 -0
- package/modules/status-ui/public/cursors/hand-pointer.png +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// tests/brand_mark.mjs — one logo and one favicon set, served from one place (task 1005088,
|
|
2
|
+
// goal 1000138 "One Cloud Bongos design system").
|
|
3
|
+
//
|
|
4
|
+
// The ring-and-dot mark used to be pasted ~22 times (the landing pages, the gates, the hall
|
|
5
|
+
// shell, hall-render, welcome-boot, raise, three loaders' masks) beside four favicon schemes.
|
|
6
|
+
// Now it lives in modules/ui-design/public/brand/mark.svg and nothing else draws it by hand.
|
|
7
|
+
// What this pins, DB-free and browser-free:
|
|
8
|
+
// * grep finds ONE source: no file outside the brand folder carries the mark's geometry
|
|
9
|
+
// (comments stripped), and the brand folder's variants are exactly what
|
|
10
|
+
// scripts/bongos/gen-brand.js writes from the source (so they are outputs, not copies);
|
|
11
|
+
// * every page uses it: the landing and hub pages, the hall shell, hall-render and the
|
|
12
|
+
// welcome reference mark.svg; the three loaders mask with mark-body/mark-dot; raise shows
|
|
13
|
+
// mark-light/mark-dark; the gates and a looked hall's favicon draw it through
|
|
14
|
+
// src/brand-mark.js; every page's fallback favicon and the pack's favicon are the one
|
|
15
|
+
// favicon.svg, with the apple-touch-icon beside it;
|
|
16
|
+
// * a project's own logo (theme.logo) still takes the mark's place.
|
|
17
|
+
//
|
|
18
|
+
// Run: node --test tests/brand_mark.mjs
|
|
19
|
+
|
|
20
|
+
import { test } from 'node:test';
|
|
21
|
+
import assert from 'node:assert/strict';
|
|
22
|
+
import fs from 'node:fs';
|
|
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 read = (...p) => fs.readFileSync(path.join(ROOT, ...p), 'utf8');
|
|
30
|
+
const BRAND = '/modules/ui-design/public/brand/';
|
|
31
|
+
const BRAND_DIR = path.join('modules', 'ui-design', 'public', 'brand');
|
|
32
|
+
|
|
33
|
+
// The mark's fingerprints: the ring's arc and the moon's centre, in any quoting or spacing.
|
|
34
|
+
const FINGERPRINTS = [/A\s*78[\s,]+78\b/, /cx=\\?['"]156\\?['"]\s+cy=\\?['"]46\\?['"]/, /\bcx%3D%27156/];
|
|
35
|
+
// Strip comments so a doc line naming the arc is not a copy (block, line and HTML comments).
|
|
36
|
+
const decomment = (s) => s
|
|
37
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
38
|
+
.replace(/<!--[\s\S]*?-->/g, '')
|
|
39
|
+
.replace(/(^|[^:'"\\])\/\/.*$/gm, '$1');
|
|
40
|
+
|
|
41
|
+
const SCAN_ROOTS = ['src', 'modules', 'scripts', 'config', 'public', 'public-docs', 'server.js'];
|
|
42
|
+
const TEXT = /\.(js|mjs|cjs|html|css|json|svg)$/;
|
|
43
|
+
function walk(rel, out = []) {
|
|
44
|
+
const abs = path.join(ROOT, rel);
|
|
45
|
+
if (!fs.existsSync(abs)) return out;
|
|
46
|
+
const st = fs.statSync(abs);
|
|
47
|
+
if (st.isFile()) { if (TEXT.test(rel)) out.push(rel); return out; }
|
|
48
|
+
for (const name of fs.readdirSync(abs)) {
|
|
49
|
+
if (name === 'node_modules' || name.startsWith('.')) continue;
|
|
50
|
+
walk(path.join(rel, name), out);
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Pure: the files (outside the brand folder) that carry the mark's geometry.
|
|
56
|
+
function pastedCopies(files, readFile) {
|
|
57
|
+
return files.filter((f) => !f.startsWith(BRAND_DIR + path.sep))
|
|
58
|
+
.filter((f) => { const code = decomment(readFile(f)); return FINGERPRINTS.some((re) => re.test(code)); });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
test('grep finds one source for the mark: no pasted copy outside the brand folder', () => {
|
|
62
|
+
const files = SCAN_ROOTS.flatMap((r) => walk(r));
|
|
63
|
+
assert.ok(files.length > 500, 'the scan reads the tree');
|
|
64
|
+
assert.deepEqual(pastedCopies(files, (f) => read(f)), [],
|
|
65
|
+
'draw the mark by reference to modules/ui-design/public/brand/mark.svg (or src/brand-mark.js on the server), never a pasted copy');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('the guard is real: a pasted copy in any of the three spellings is found', () => {
|
|
69
|
+
const fake = {
|
|
70
|
+
'modules/x/public/a.html': '<svg><path d="M137 31 A78 78 0 1 0 170 66"/></svg>',
|
|
71
|
+
'modules/x/public/b.css': ".l{--s:url(\"data:image/svg+xml,%3Csvg%3E%3Ccircle cx='156' cy='46' r='12'/%3E%3C/svg%3E\")}",
|
|
72
|
+
'src/c.js': "const s = \"<path class='r' d='M137 31A78 78 0 1 0 170 66'/>\";",
|
|
73
|
+
'src/d.js': '// the arc A78 78 is the ring\nconst ok = 1;',
|
|
74
|
+
[path.join(BRAND_DIR, 'mark.svg')]: '<path d="M137 31 A78 78 0 1 0 170 66"/>',
|
|
75
|
+
};
|
|
76
|
+
assert.deepEqual(pastedCopies(Object.keys(fake), (f) => fake[f]).sort(),
|
|
77
|
+
['modules/x/public/a.html', 'modules/x/public/b.css', 'src/c.js']);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('the source carries the three parts and their two colours', () => {
|
|
81
|
+
const src = read(BRAND_DIR, 'mark.svg');
|
|
82
|
+
for (const id of ['mark', 'mark-ring', 'mark-disc', 'mark-dot']) assert.match(src, new RegExp(`id="${id}"`));
|
|
83
|
+
// A browser reads a referenced SVG as XML: a comment holding "--" (say, a custom property's
|
|
84
|
+
// name) makes the whole file unreadable and every page's mark silently vanishes.
|
|
85
|
+
for (const [, body] of src.matchAll(/<!--([\s\S]*?)-->/g)) assert.ok(!body.includes('--'), 'no "--" inside an XML comment');
|
|
86
|
+
assert.equal((src.match(/<!--/g) || []).length, (src.match(/-->/g) || []).length, 'every comment closes');
|
|
87
|
+
const { markParts } = require('../src/brand-mark.js');
|
|
88
|
+
const parts = markParts(src);
|
|
89
|
+
assert.ok(parts, 'src/brand-mark.js reads the parts');
|
|
90
|
+
assert.match(parts.ring + parts.disc, /var\(--mark-ink,currentColor\)/);
|
|
91
|
+
assert.match(parts.dot, /var\(--mark-dot,var\(--accent,currentColor\)\)/);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('the brand folder holds exactly what gen-brand.js writes from the source (light, dark, mono, favicon)', () => {
|
|
95
|
+
const { brandFiles, packColors } = require('../scripts/bongos/gen-brand.js');
|
|
96
|
+
const files = brandFiles(read(BRAND_DIR, 'mark.svg'), packColors());
|
|
97
|
+
for (const [name, text] of Object.entries(files)) {
|
|
98
|
+
assert.equal(read(BRAND_DIR, name), text, `${name} is stale — run node scripts/bongos/gen-brand.js`);
|
|
99
|
+
}
|
|
100
|
+
const c = packColors();
|
|
101
|
+
assert.match(files['mark-light.svg'], new RegExp(c.ink));
|
|
102
|
+
assert.match(files['mark-light.svg'], new RegExp(c.accent));
|
|
103
|
+
assert.match(files['mark-dark.svg'], /#e2e1e4/);
|
|
104
|
+
assert.doesNotMatch(files['mark-mono.svg'], new RegExp(c.accent), 'mono is one ink');
|
|
105
|
+
assert.match(files['favicon.svg'], /prefers-color-scheme:dark/, 'the tab icon has its night twin');
|
|
106
|
+
const png = fs.readFileSync(path.join(ROOT, BRAND_DIR, 'apple-touch-icon.png'));
|
|
107
|
+
assert.equal(png.toString('ascii', 1, 4), 'PNG');
|
|
108
|
+
assert.deepEqual([png.readUInt32BE(16), png.readUInt32BE(20)], [180, 180], 'the home-screen icon is 180x180');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('every landing and hub page draws the mark by reference', () => {
|
|
112
|
+
const dir = path.join('modules', 'public-landing', 'public');
|
|
113
|
+
const pages = fs.readdirSync(path.join(ROOT, dir)).filter((n) => n.endsWith('.html'))
|
|
114
|
+
.filter((n) => read(dir, n).includes('class="bongosMark"'));
|
|
115
|
+
assert.ok(pages.length >= 10, `the landing pages with a lockup (${pages.length})`);
|
|
116
|
+
for (const n of pages) {
|
|
117
|
+
assert.match(read(dir, n), new RegExp(`<svg class="bongosMark"[^>]*><use href="${BRAND}mark\\.svg#mark"/></svg>`), n);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('the hall shell, hall-render and the welcome reference the mark, and a project logo still wins', () => {
|
|
122
|
+
const shell = read('modules', 'hall-ui', 'public', 'shell.js');
|
|
123
|
+
assert.ok(shell.includes(`<use href="${BRAND}mark.svg#mark"/>`), 'the sidebar mark');
|
|
124
|
+
assert.match(shell, /brand\.theme && brand\.theme\.logo\s*\?\s*'<img class="brand-mark__logo"/, 'the logo override');
|
|
125
|
+
const render = read('modules', 'hall-ui', 'public', 'hall-render.js');
|
|
126
|
+
assert.ok(render.includes(`<use href="${BRAND}mark.svg#mark"/>`), 'the signed-out card mark');
|
|
127
|
+
assert.match(render, /logo\s*\?\s*`<img class="landing__logo"/, 'the logo override');
|
|
128
|
+
const wb = read('modules', 'hall-ui', 'public', 'welcome-boot.js');
|
|
129
|
+
assert.ok(wb.includes(`var MARK = '${BRAND}mark.svg'`), 'the welcome banner');
|
|
130
|
+
for (const part of ['#mark-ring', '#mark-disc', '#mark-dot']) assert.ok(wb.includes(part), `the welcome animates ${part}`);
|
|
131
|
+
assert.match(wb, /logo \? '<img class="wb__logo"/, 'the logo override');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('the three loaders cut their shapes from the brand files', () => {
|
|
135
|
+
for (const [file, v] of [
|
|
136
|
+
[['modules', 'hall-ui', 'public', 'style.css'], '--hl-shape'],
|
|
137
|
+
[['modules', 'status-ui', 'public', 'style.css'], '--lm-shape'],
|
|
138
|
+
[['modules', 'public-landing', 'public', 'assets', 'cosmos.css'], '--lm-shape'],
|
|
139
|
+
]) {
|
|
140
|
+
const css = read(...file);
|
|
141
|
+
assert.ok(new RegExp(`${v}:\\s*url\\("${BRAND}mark-body\\.svg"\\)`).test(css), `${file.join('/')} ring and planet`);
|
|
142
|
+
assert.ok(new RegExp(`${v}:\\s*url\\("${BRAND}mark-dot\\.svg"\\)`).test(css), `${file.join('/')} moon`);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('raise shows the shared logo pair and favicon', () => {
|
|
147
|
+
for (const v of ['gate', 'page', 'nda']) {
|
|
148
|
+
const html = read('modules', 'raise', 'views', `${v}.html`);
|
|
149
|
+
assert.ok(html.includes(`<link rel="icon" href="${BRAND}favicon.svg"`), `${v}: favicon`);
|
|
150
|
+
assert.ok(html.includes(`<link rel="apple-touch-icon" href="${BRAND}apple-touch-icon.png">`), `${v}: touch icon`);
|
|
151
|
+
if (html.includes('class="logo')) {
|
|
152
|
+
assert.ok(html.includes(`src="${BRAND}mark-light.svg"`) && html.includes(`src="${BRAND}mark-dark.svg"`), `${v}: logo pair`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('the gates draw the mark from the source and still load nothing', () => {
|
|
158
|
+
const { markSvg } = require('../src/brand-mark.js');
|
|
159
|
+
const svg = markSvg({ className: 'mark' });
|
|
160
|
+
assert.match(svg, /^<svg class="mark" viewBox="0 0 200 200" aria-hidden="true"><path [^>]*\/><circle [^>]*\/><circle [^>]*\/><\/svg>$/);
|
|
161
|
+
assert.doesNotMatch(svg, /\sid=/, 'an inline copy carries no ids');
|
|
162
|
+
for (const f of ['prelaunch-gate.js', 'platform-visibility-gate.js']) {
|
|
163
|
+
const src = read('src', 'bongos', f);
|
|
164
|
+
assert.ok(src.includes("${markSvg({ className: 'mark' })}"), `${f} draws the mark`);
|
|
165
|
+
assert.match(src, /\.mark\{[^}]*--mark-ink:#17181a;--mark-dot:#fb9569\}/, `${f} colours it`);
|
|
166
|
+
assert.match(src, /prefers-color-scheme:dark\)\{\.mark\{--mark-ink:#e2e1e4\}\}/, `${f} night twin`);
|
|
167
|
+
assert.doesNotMatch(src, /<use href=/, `${f} references nothing — it is served before any asset`);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('one favicon: every page fallback and the pack point at favicon.svg; a look or a logo overrides', () => {
|
|
172
|
+
for (const dir of [['modules', 'hall-ui', 'public'], ['modules', 'status-ui', 'public']]) {
|
|
173
|
+
for (const n of fs.readdirSync(path.join(ROOT, ...dir)).filter((x) => x.endsWith('.html'))) {
|
|
174
|
+
const icons = read(...dir, n).match(/<link rel="icon"[^>]*>/g) || [];
|
|
175
|
+
for (const l of icons) assert.ok(l.includes(`href="${BRAND}favicon.svg"`), `${dir.join('/')}/${n}: ${l.slice(0, 80)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const pack = JSON.parse(read('config', 'branding.neutral.json')).theme.ui;
|
|
179
|
+
assert.equal(pack.favicon, `${BRAND}favicon.svg`);
|
|
180
|
+
assert.equal(pack.appleTouchIcon, `${BRAND}apple-touch-icon.png`);
|
|
181
|
+
|
|
182
|
+
const { themeMarkup } = require('../src/bongos/serve-internal.js');
|
|
183
|
+
const m = themeMarkup({ favicon: pack.favicon, appleTouchIcon: pack.appleTouchIcon, fonts: { links: [] } });
|
|
184
|
+
assert.ok(m.includes(`<link rel="icon" href="${BRAND}favicon.svg">`));
|
|
185
|
+
assert.ok(m.includes(`<link rel="apple-touch-icon" href="${BRAND}apple-touch-icon.png">`));
|
|
186
|
+
assert.ok(!themeMarkup({ favicon: 'x', fonts: { links: [] } }).includes('apple-touch-icon'), 'no touch icon unless the pack names one');
|
|
187
|
+
|
|
188
|
+
const { applyLook } = require('../src/hall-look.js');
|
|
189
|
+
const looked = applyLook({ theme: { ui: { ...pack } }, project: { look: 'custom', lookAccent: '#3366cc' } },
|
|
190
|
+
() => ({ colors: { ink: '#101010', accent: '#fb9569' }, fonts: {} }));
|
|
191
|
+
assert.match(looked.theme.ui.favicon, /^data:image\/svg\+xml;utf8,<svg /);
|
|
192
|
+
assert.match(looked.theme.ui.favicon, /%233366cc/, 'drawn in the look\'s accent');
|
|
193
|
+
assert.match(looked.theme.ui.favicon, /stroke-width='24'/, 'the small cut');
|
|
194
|
+
assert.equal(looked.theme.ui.appleTouchIcon, '', 'the pack-coloured touch icon steps aside');
|
|
195
|
+
});
|
|
@@ -43,7 +43,9 @@ test('R74: the vanilla pack carries the Cloud Bongos LOOK (chrome palette, Manro
|
|
|
43
43
|
assert.match(c.theme.fonts.display, /Manrope/);
|
|
44
44
|
assert.match(c.theme.fonts.body, /Manrope/);
|
|
45
45
|
assert.ok(c.theme.fonts.links.some((l) => /JetBrains\+Mono/.test(l) && /Manrope/.test(l)), 'loads Manrope + JetBrains Mono');
|
|
46
|
-
|
|
46
|
+
// the one favicon set (task 1005088): the moon-in-the-gap mark, served from the brand folder
|
|
47
|
+
assert.equal(c.theme.favicon, '/modules/ui-design/public/brand/favicon.svg', 'the moon-in-the-gap favicon, not the Greek Α');
|
|
48
|
+
assert.equal(c.theme.appleTouchIcon, '/modules/ui-design/public/brand/apple-touch-icon.png');
|
|
47
49
|
assert.doesNotMatch(c.theme.favicon, /font-family='serif'>Α/);
|
|
48
50
|
// the backdrop is the TOKEN, not a hex: the served body follows the mode,
|
|
49
51
|
// because the sheets' dark blocks are (0,2,0) and outrank the injected :root.
|
|
@@ -60,7 +62,8 @@ test('R74: serving the hall on a vanilla boot injects the Cloud Bongos chrome th
|
|
|
60
62
|
// palette override + the token-valued body backdrop + bongo favicon + the type links
|
|
61
63
|
assert.match(out, /:root\{[^}]*--accent:#fb9569;/);
|
|
62
64
|
assert.match(out, /body\{background:var\(--bg\);\}/);
|
|
63
|
-
assert.match(out, /<link rel="icon" href="
|
|
65
|
+
assert.match(out, /<link rel="icon" href="\/modules\/ui-design\/public\/brand\/favicon\.svg">/);
|
|
66
|
+
assert.match(out, /<link rel="apple-touch-icon" href="\/modules\/ui-design\/public\/brand\/apple-touch-icon\.png">/);
|
|
64
67
|
assert.match(out, /fonts\.googleapis\.com\/css2\?family=JetBrains\+Mono/);
|
|
65
68
|
// and it lands AFTER the stylesheet so the Cloud Bongos look wins
|
|
66
69
|
assert.ok(out.indexOf('/builders/style.css') < out.indexOf('--accent:#fb9569'));
|
package/tests/hall_loading.mjs
CHANGED
|
@@ -36,13 +36,11 @@ test('the loader is drawn from the Bongos mark, in three sizes', () => {
|
|
|
36
36
|
for (const sel of ['.hall-loading {', '.hall-loading::before {', '.hall-loading::after {', '.hall-loading--page {', '.hall-loading--inline {']) {
|
|
37
37
|
assert.ok(LOADER.includes(sel), `${sel} is declared`);
|
|
38
38
|
}
|
|
39
|
-
// The
|
|
40
|
-
|
|
41
|
-
assert.ok(
|
|
42
|
-
assert.ok(
|
|
43
|
-
assert.ok(LOADER.includes(
|
|
44
|
-
assert.ok(SHELL.includes('<circle cx="156" cy="46" r="12" fill="#fb9569"/>'), 'the brand mark\'s dot');
|
|
45
|
-
assert.ok(LOADER.includes("circle cx='156' cy='46' r='12'"), 'is the loader\'s dot');
|
|
39
|
+
// The shell's brand mark and the loader are cut from the one mark (task 1005088), so the
|
|
40
|
+
// loader IS the logo: the shell references mark.svg, the masks are its ring-and-core and dot.
|
|
41
|
+
assert.ok(SHELL.includes('<use href="/modules/ui-design/public/brand/mark.svg#mark"/>'), 'the brand mark is the one mark');
|
|
42
|
+
assert.ok(LOADER.includes('--hl-shape: url("/modules/ui-design/public/brand/mark-body.svg")'), 'the loader\'s ring and core');
|
|
43
|
+
assert.ok(LOADER.includes('--hl-shape: url("/modules/ui-design/public/brand/mark-dot.svg")'), 'the loader\'s dot');
|
|
46
44
|
});
|
|
47
45
|
|
|
48
46
|
test('it moves in CSS only, on transform and filter only, and rests on the logo', () => {
|
|
@@ -74,9 +72,9 @@ test('every colour is a token; the black in the masks is alpha, not paint', () =
|
|
|
74
72
|
for (const p of paints) assert.match(p, /^(var\(--[a-z-]+\)|color-mix\(in srgb, var\(--ink\) \d+%, var\(--bg-card\)\))$/, `${p} is a token or a mix of one`);
|
|
75
73
|
// The one accent is the dot: the live thing on a waiting page (the One Warm Thing Rule).
|
|
76
74
|
assert.equal((css.match(/var\(--accent\)/g) || []).length, 1, 'the accent paints the dot alone');
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
// The two masks are the brand folder's one-ink shapes (task 1005088): only their alpha counts.
|
|
76
|
+
const masks = [...css.matchAll(/url\("\/modules\/ui-design\/public\/brand\/(mark-[a-z]+\.svg)"\)/g)].map((m) => m[1]);
|
|
77
|
+
assert.deepEqual(masks, ['mark-body.svg', 'mark-dot.svg'], 'two masks: the ring and core, and the dot');
|
|
80
78
|
});
|
|
81
79
|
|
|
82
80
|
test('the page loaders step aside by themselves, and never show without :has()', () => {
|
package/tests/loading_mark.mjs
CHANGED
|
@@ -38,7 +38,8 @@ const SURFACES = {
|
|
|
38
38
|
status: { css: STATUS_CSS, loader: section(STATUS_CSS, '/* ---------- The loader (task 1005053') },
|
|
39
39
|
};
|
|
40
40
|
const flat = (s) => s.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\s+/g, ' ');
|
|
41
|
-
|
|
41
|
+
// The masks are the brand folder's one-ink shapes (task 1005088), named by file.
|
|
42
|
+
const masksOf = (css) => [...css.matchAll(/url\("\/modules\/ui-design\/public\/brand\/(mark-[a-z]+\.svg)"\)/g)].map((m) => m[1]);
|
|
42
43
|
function keyframes(css, name) {
|
|
43
44
|
const m = css.match(new RegExp(`@keyframes ${name} ?\\{`));
|
|
44
45
|
assert.ok(m, `@keyframes ${name} exists`);
|
|
@@ -48,16 +49,11 @@ function keyframes(css, name) {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
test('the apex loader is the bar\'s own Bongos mark, and the status loader is the same mark', () => {
|
|
51
|
-
// The apex bar
|
|
52
|
-
// two masks
|
|
52
|
+
// The apex bar and the loader are cut from the one mark (task 1005088): the bar references
|
|
53
|
+
// mark.svg, and the loader's two masks are its ring-and-core and its dot, so the loader IS the logo.
|
|
53
54
|
const bar = read(LANDING, 'account.html');
|
|
54
|
-
|
|
55
|
-
assert.
|
|
56
|
-
assert.match(bar, /<circle cx="156" cy="46" r="12" style="fill:var\(--accent\)"\/>/);
|
|
57
|
-
const [ring, dot] = masksOf(SURFACES.apex.loader);
|
|
58
|
-
assert.ok(ring.includes(`d='${arc}'`), 'the ring is the bar mark\'s arc');
|
|
59
|
-
assert.ok(ring.includes("<circle cx='100' cy='100' r='55'/>"), 'the core is the bar mark\'s core');
|
|
60
|
-
assert.ok(dot.includes("<circle cx='156' cy='46' r='12'/>"), 'the dot is the bar mark\'s dot');
|
|
55
|
+
assert.match(bar, /<svg class="bongosMark"[^>]*><use href="\/modules\/ui-design\/public\/brand\/mark\.svg#mark"\/><\/svg>/);
|
|
56
|
+
assert.deepEqual(masksOf(SURFACES.apex.loader), ['mark-body.svg', 'mark-dot.svg']);
|
|
61
57
|
// The status page has no Bongos mark of its own: its loader carries the apex's two
|
|
62
58
|
// masks byte for byte, so the two surfaces cannot drift into two logos.
|
|
63
59
|
assert.deepEqual(masksOf(SURFACES.status.loader), masksOf(SURFACES.apex.loader));
|
|
@@ -91,9 +87,7 @@ for (const [name, { css, loader }] of Object.entries(SURFACES)) {
|
|
|
91
87
|
for (const p of paints) assert.match(p, /^(var\(--[a-z-]+\)|color-mix\(in srgb, ?var\(--ink\) 30%, ?var\(--bg-card\)\))$/, `${p} is a token or a mix of one`);
|
|
92
88
|
// The one accent is the dot: the live thing on a waiting page (the One Warm Thing Rule).
|
|
93
89
|
assert.equal((css.match(/var\(--accent\)/g) || []).length, 1, 'the accent paints the dot alone');
|
|
94
|
-
|
|
95
|
-
assert.equal(masks.length, 2, 'two masks: the ring and core, and the dot');
|
|
96
|
-
for (const m of masks) assert.doesNotMatch(m.replace(/stroke='black'|fill='none'/g, ''), /fill='|stroke='|#/, 'a mask carries no colour of its own');
|
|
90
|
+
assert.deepEqual(masksOf(loader), ['mark-body.svg', 'mark-dot.svg'], 'two masks: the ring and core, and the dot');
|
|
97
91
|
});
|
|
98
92
|
|
|
99
93
|
test(`${name}: the dot shares the ring's grid cell, so no page padding can knock it off`, () => {
|
|
@@ -101,7 +101,8 @@ test('the hall-preview fixture carries every atom /me resolves', () => {
|
|
|
101
101
|
// task 1005050: Accomplishments left it (folded into Profile; its hashes redirect);
|
|
102
102
|
// task 1005051: Roadmap left it (folded into Goals; /roadmap redirects).
|
|
103
103
|
// task 1005070: the remembered nav (the rail paints before /me).
|
|
104
|
-
|
|
104
|
+
// task 1005088: the sidebar mark is drawn by reference to the one brand mark.
|
|
105
|
+
const SHELL_SHA = '2845bfedfaf7846f';
|
|
105
106
|
|
|
106
107
|
test('shell.js has not changed without its cache-bust being bumped', () => {
|
|
107
108
|
const actual = crypto.createHash('sha256').update(SHELL).digest('hex').slice(0, 16);
|
package/tests/raise.mjs
CHANGED
|
@@ -157,7 +157,9 @@ test('a stranger gets the gate, never the copy', async () => {
|
|
|
157
157
|
assert.doesNotMatch(html, /gamification of capitalism/);
|
|
158
158
|
assert.equal(r.headers.get('x-robots-tag'), 'noindex, nofollow, noarchive');
|
|
159
159
|
assert.equal((await get('/raise/media/x.mp4')).status, 401);
|
|
160
|
-
|
|
160
|
+
// the gate's logo is the shared one (task 1005088), served on every host, never a raise copy
|
|
161
|
+
assert.match(html, /src="\/modules\/ui-design\/public\/brand\/mark-light\.svg"/, 'the gate needs its logo');
|
|
162
|
+
assert.equal((await get('/raise/assets/logo-ink.svg')).status, 404, 'the old pasted copy is gone');
|
|
161
163
|
assert.match(await (await get('/raise/nda')).text(), /CONFIDENTIALITY|Confidential/i);
|
|
162
164
|
});
|
|
163
165
|
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// tests/status_page_chrome.mjs — the status page on the chrome world (task 1005089,
|
|
2
|
+
// goal 1000138; DESIGN.md "Naming": the owner moved the status page onto this world,
|
|
3
|
+
// keeping its fixed status vocabulary as the hall does).
|
|
4
|
+
//
|
|
5
|
+
// What this pins, DB-free and browser-free:
|
|
6
|
+
// * nothing of the retired Greek ledger look survives in the surface: no parchment
|
|
7
|
+
// colours, no Cinzel / EB Garamond, no bronze cursors, no amphora or laurel
|
|
8
|
+
// sprite, no double-ruled frame;
|
|
9
|
+
// * every page title carries the product name from the branding pack;
|
|
10
|
+
// * the sheet declares no page :root, writes its dark tier twice byte-identical,
|
|
11
|
+
// and carries the hall's status vocabulary value for value;
|
|
12
|
+
// * explore is served through the branding injection, so it loads the pack's faces;
|
|
13
|
+
// * the off-box mirror IS the live page: the server's response for "/" on the
|
|
14
|
+
// status host, and the mirror's index built from the same source, are equal once
|
|
15
|
+
// each side's own injections are taken out — and every file the page links is one
|
|
16
|
+
// the mirror publishes.
|
|
17
|
+
//
|
|
18
|
+
// Run: node --test tests/status_page_chrome.mjs
|
|
19
|
+
|
|
20
|
+
import { test } from 'node:test';
|
|
21
|
+
import assert from 'node:assert/strict';
|
|
22
|
+
import fs from 'node:fs';
|
|
23
|
+
import http from 'node:http';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { createRequire } from 'node:module';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
|
|
28
|
+
const require = createRequire(import.meta.url);
|
|
29
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
30
|
+
const DIR = path.join(ROOT, 'modules', 'status-ui', 'public');
|
|
31
|
+
const read = (...p) => fs.readFileSync(path.join(ROOT, ...p), 'utf8').replace(/\r\n/g, '\n');
|
|
32
|
+
const decomment = (s) => s.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
33
|
+
const CSS = read('modules', 'status-ui', 'public', 'style.css');
|
|
34
|
+
const CODE = decomment(CSS);
|
|
35
|
+
|
|
36
|
+
function surfaceFiles(dir = DIR, out = []) {
|
|
37
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
38
|
+
const p = path.join(dir, e.name);
|
|
39
|
+
if (e.isDirectory()) surfaceFiles(p, out);
|
|
40
|
+
else if (/\.(html|css|js)$/.test(e.name)) out.push(p);
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The retired look, as literals. The parchment hexes are the old sheet's :root.
|
|
46
|
+
const RETIRED = [
|
|
47
|
+
/Cinzel/i, /Garamond/i, /cursors\//, /hand-pointer\.png/, /sigil-amphora/, /orn-(laurel|column|coin|scroll|map|diamond)/,
|
|
48
|
+
/#ece0c2|#f6ecd2|#ddc99c|#e3d0a4|#2a2110|#6e5b3a|#735f3d|#8b6f2c|#b89a55|#4a7d5c|#2f5a3f|#2d6e7e|#a85530/i,
|
|
49
|
+
/rgba\(\s*(139, 111, 44|60, 40, 10|42, 33, 16|243, 230, 198)/,
|
|
50
|
+
/upper-roman/,
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
test('nothing of the Greek ledger look is left in the status surface', () => {
|
|
54
|
+
const hits = [];
|
|
55
|
+
for (const f of surfaceFiles()) {
|
|
56
|
+
const text = fs.readFileSync(f, 'utf8');
|
|
57
|
+
for (const re of RETIRED) if (re.test(text)) hits.push(`${path.relative(ROOT, f)}: ${re}`);
|
|
58
|
+
}
|
|
59
|
+
assert.deepEqual(hits, []);
|
|
60
|
+
assert.equal(fs.existsSync(path.join(DIR, 'cursors')), false, 'the bronze cursors are gone');
|
|
61
|
+
// no double-ruled frame: a panel is one hairline, no inset rule, no shadow
|
|
62
|
+
const panel = CODE.match(/\n\.tablet \{([^}]*)\}/);
|
|
63
|
+
assert.ok(panel, 'the panel rule exists');
|
|
64
|
+
assert.match(panel[1], /border: var\(--line\) solid var\(--rule-soft\);/);
|
|
65
|
+
assert.match(panel[1], /border-radius: var\(--ds-radius-panel\);/);
|
|
66
|
+
assert.doesNotMatch(panel[1], /box-shadow/);
|
|
67
|
+
assert.doesNotMatch(CODE, /\.tablet::(before|after)/, 'no carved corner ornaments');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('every page title carries the product name from the pack', () => {
|
|
71
|
+
const pages = fs.readdirSync(DIR).filter((f) => f.endsWith('.html'));
|
|
72
|
+
assert.ok(pages.length >= 2);
|
|
73
|
+
for (const p of pages) {
|
|
74
|
+
const title = fs.readFileSync(path.join(DIR, p), 'utf8').match(/<title>([^<]*)<\/title>/);
|
|
75
|
+
assert.ok(title, `${p} has a title`);
|
|
76
|
+
assert.match(title[1], /\{\{productName\}\}/, `${p}: the title names the product`);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('the page draws the shared mark and favicon, and names no face of its own', () => {
|
|
81
|
+
const html = read('modules', 'status-ui', 'public', 'index.html');
|
|
82
|
+
assert.ok(html.includes('<use href="/modules/ui-design/public/brand/mark.svg#mark"/>'), 'the top bar draws the one mark by reference');
|
|
83
|
+
assert.ok(html.includes('<link rel="icon" href="/modules/ui-design/public/brand/favicon.svg"'));
|
|
84
|
+
assert.doesNotMatch(html, /fonts\.googleapis\.com/, 'the faces come from the pack splice, not the page');
|
|
85
|
+
assert.ok(html.indexOf('/modules/hall-ui/public/controls.css') > html.indexOf('/modules/ui-design/public/tokens.css'), 'the shared controls follow the scales');
|
|
86
|
+
assert.ok(html.indexOf('/modules/hall-ui/public/controls.css') < html.indexOf('href="/style.css'), 'and precede the page sheet');
|
|
87
|
+
// exactly one header (the top bar), one main, one h1
|
|
88
|
+
assert.equal((html.match(/<header\b/g) || []).length, 1);
|
|
89
|
+
assert.equal((html.match(/<main\b/g) || []).length, 1);
|
|
90
|
+
assert.equal((html.match(/<h1\b/g) || []).length, 1);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('the sheet declares no page :root, and its dark tier is written twice, byte-identical', () => {
|
|
94
|
+
assert.doesNotMatch(CODE, /(^|[\s,}]):root\b/, 'no :root — the pack splices the only one');
|
|
95
|
+
assert.match(CODE, /:where\(html\) \{/, 'the fallback contract tier sits at zero specificity');
|
|
96
|
+
const switchBody = CODE.match(/\nhtml\[data-theme="dark"\] \{([^}]*)\}/);
|
|
97
|
+
const mediaBody = CODE.match(/@media \(prefers-color-scheme: dark\) \{\s*html:not\(\[data-theme\]\) \{([^}]*)\}/);
|
|
98
|
+
assert.ok(switchBody && mediaBody, 'both dark twins exist');
|
|
99
|
+
const norm = (s) => s.split('\n').map((l) => l.trim()).filter(Boolean).join('\n');
|
|
100
|
+
assert.equal(norm(mediaBody[1]), norm(switchBody[1]));
|
|
101
|
+
// the dark tier re-pins no accent (the Unpinned Accent Rule)
|
|
102
|
+
assert.doesNotMatch(switchBody[1], /--(accent|accent-deep|sea|terracotta):/);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('the status vocabulary is the hall\'s, value for value, in both modes', () => {
|
|
106
|
+
const hall = decomment(read('modules', 'hall-ui', 'public', 'style.css'));
|
|
107
|
+
const NAMES = ['--ok', '--ok-weak', '--warn', '--warn-weak', '--danger', '--danger-weak', '--info', '--info-weak'];
|
|
108
|
+
const valuesIn = (body) => Object.fromEntries(NAMES.map((n) => {
|
|
109
|
+
const m = body.match(new RegExp(`\\n\\s*${n}:\\s*([^;]+);`));
|
|
110
|
+
assert.ok(m, `${n} is declared`);
|
|
111
|
+
return [n, m[1].trim()];
|
|
112
|
+
}));
|
|
113
|
+
const lightOf = (src, open) => { const at = src.indexOf(open); assert.ok(at > -1, open); return src.slice(at, src.indexOf('}', at)); };
|
|
114
|
+
assert.deepEqual(valuesIn(lightOf(CODE, '\nhtml {')), valuesIn(lightOf(hall, '\n:root {')));
|
|
115
|
+
assert.deepEqual(valuesIn(lightOf(CODE, '\nhtml[data-theme="dark"] {')), valuesIn(lightOf(hall, '\nhtml[data-theme="dark"] {')));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ── served ────────────────────────────────────────────────────────────────────
|
|
119
|
+
// node:http, not fetch: fetch silently drops a Host override (tests/module_asset_subdomain.mjs).
|
|
120
|
+
function get(port, p, host) {
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
const req = http.request({ host: '127.0.0.1', port, path: p, headers: { Host: host } }, (res) => {
|
|
123
|
+
let body = '';
|
|
124
|
+
res.setEncoding('utf8');
|
|
125
|
+
res.on('data', (c) => { body += c; });
|
|
126
|
+
res.on('end', () => resolve({ status: res.statusCode, body }));
|
|
127
|
+
});
|
|
128
|
+
req.on('error', reject);
|
|
129
|
+
req.end();
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async function withServer(fn) {
|
|
133
|
+
const express = require('express');
|
|
134
|
+
const { mountInternalSurfaces } = require('../src/bongos/serve-internal.js');
|
|
135
|
+
const app = express();
|
|
136
|
+
mountInternalSurfaces(app);
|
|
137
|
+
const server = await new Promise((resolve) => { const s = app.listen(0, () => resolve(s)); });
|
|
138
|
+
try { return await fn(server.address().port); } finally { await new Promise((r) => server.close(r)); }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
test('explore is served through the branding injection: the pack\'s faces, the product name', async () => {
|
|
142
|
+
const { clientBranding } = require('../src/branding.js');
|
|
143
|
+
const brand = clientBranding();
|
|
144
|
+
await withServer(async (port) => {
|
|
145
|
+
for (const p of ['/explore', '/explore.html']) {
|
|
146
|
+
const r = await get(port, p, 'status.example.com');
|
|
147
|
+
assert.equal(r.status, 200, p);
|
|
148
|
+
// the href is attribute-escaped on the way in (themeMarkup), so & arrives as &
|
|
149
|
+
for (const href of brand.theme.fonts.links) assert.ok(r.body.includes(`<link rel="stylesheet" href="${href.replace(/&/g, '&')}">`), `${p} loads the pack's faces`);
|
|
150
|
+
assert.ok(r.body.includes(`<title>Explore — projects on the map · ${brand.identity.productName}</title>`), `${p} names the product`);
|
|
151
|
+
assert.doesNotMatch(r.body, /\{\{productName\}\}/);
|
|
152
|
+
}
|
|
153
|
+
// an asset still comes off disk
|
|
154
|
+
assert.equal((await get(port, '/explore.js', 'status.example.com')).status, 200);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('the off-box mirror is the live page, and publishes every file the page links', async () => {
|
|
159
|
+
const mirror = require('../scripts/status-mirror-sync.js');
|
|
160
|
+
const { stampHtml } = require('../src/asset-version.js');
|
|
161
|
+
const { clientBranding, branding } = require('../src/branding.js');
|
|
162
|
+
const brand = clientBranding();
|
|
163
|
+
const src = stampHtml(read('modules', 'status-ui', 'public', 'index.html'), { assetDir: DIR, urlPrefix: '/' });
|
|
164
|
+
const apiBase = branding().domains.publicOrigin || 'https://origin.example.test';
|
|
165
|
+
const built = mirror.buildMirrorIndex({ srcHtml: src, apiBase, statusDataUrl: 'https://data.example.test/status.json', brand });
|
|
166
|
+
|
|
167
|
+
const live = await withServer(async (port) => (await get(port, '/', 'status.example.com')).body);
|
|
168
|
+
assert.ok(live.includes(brand.theme.fonts.links[0].replace(/&/g, '&')), 'the live page splices the pack');
|
|
169
|
+
|
|
170
|
+
// each side minus its own injections: the server's client bundle + globals, and the
|
|
171
|
+
// mirror's three off-box additions + its globals
|
|
172
|
+
const liveCore = live
|
|
173
|
+
.replace(/<script src="\/lib\/bongos-client\.js[^"]*"><\/script>/, '')
|
|
174
|
+
.replace(/<script>window\.__BRANDING__=[\s\S]*?<\/script>/, '');
|
|
175
|
+
const mirrorCore = built
|
|
176
|
+
.replace(/ {2}<!-- #737: off-box mirror runs cross-origin against the droplet\. -->\n {2}<script>window\.STATUS_API_BASE = [^\n]*<\/script>\n {2}<script>window\.__BRANDING__ = [^\n]*<\/script>\n/, '')
|
|
177
|
+
.replace(/\n<!-- Off-box availability banner \(ADR 0029\)[\s\S]*?<div id="offbox-status" role="status" aria-live="polite"><\/div>\n/, '')
|
|
178
|
+
.replace(/<!-- Off-box availability banner driver \(ADR 0029\)[\s\S]*?<\/script>\n/, '');
|
|
179
|
+
// the origin token is the one value the two resolve from different places on a
|
|
180
|
+
// checkout with no domains; normalise it before comparing
|
|
181
|
+
const norm = (s) => s.split(apiBase).join('{{origin}}');
|
|
182
|
+
assert.equal(norm(mirrorCore), norm(liveCore));
|
|
183
|
+
|
|
184
|
+
// every root-absolute file the page links is published: its own files are copied
|
|
185
|
+
// from the surface dir, the shared ones at the same path
|
|
186
|
+
const refs = [...src.matchAll(/\b(?:href|src)="(\/[^"?#]+)/g)].map((m) => m[1]).filter((u) => !u.startsWith('//'));
|
|
187
|
+
assert.ok(refs.length >= 5);
|
|
188
|
+
for (const u of refs) {
|
|
189
|
+
const rel = u.slice(1).replace(/#.*$/, '');
|
|
190
|
+
const own = ['status.js', 'style.css'].includes(rel);
|
|
191
|
+
const shared = mirror.SHARED_ASSETS.some((a) => rel === a || rel.startsWith(`${a}/`));
|
|
192
|
+
assert.ok(own || shared, `the mirror publishes ${u}`);
|
|
193
|
+
assert.ok(fs.existsSync(path.join(own ? DIR : ROOT, rel)), `${u} exists to publish`);
|
|
194
|
+
}
|
|
195
|
+
});
|
package/tests/welcome_boot.mjs
CHANGED
|
@@ -79,8 +79,11 @@ test('the banner is the logo, named from the branding pack, never a hardcoded wo
|
|
|
79
79
|
assert.equal(W.bannerText, undefined, 'the "Working on the core of" banner is gone');
|
|
80
80
|
const harbor = W.logoHtml({ identity: { worldName: 'Harbor', productName: 'Cloud Bongos' } });
|
|
81
81
|
assert.match(harbor, /<svg class="wb__mark"/, 'no project logo: the Bongos mark');
|
|
82
|
-
|
|
83
|
-
assert.match(harbor,
|
|
82
|
+
// the parts come from the one mark (task 1005088), whose ring carries pathLength="100"
|
|
83
|
+
assert.match(harbor, /<use class="wb__ring" href="\/modules\/ui-design\/public\/brand\/mark\.svg#mark-ring"\/>/, 'the ring can draw in');
|
|
84
|
+
assert.match(harbor, /<use class="wb__planet" href="[^"]*#mark-disc"\/>/, 'the planet');
|
|
85
|
+
assert.match(harbor, /<use class="wb__moonlet" href="[^"]*#mark-dot"\/>/, 'the moon');
|
|
86
|
+
assert.match(fs.readFileSync(path.join(ROOT, 'modules', 'ui-design', 'public', 'brand', 'mark.svg'), 'utf8'), /id="mark-ring"[^>]*pathLength="100"/);
|
|
84
87
|
assert.match(harbor, /<span class="wb__name">Harbor<\/span>/);
|
|
85
88
|
assert.match(W.logoHtml(undefined), /<span class="wb__name">this project<\/span>/, 'no branding: still a name');
|
|
86
89
|
const own = W.logoHtml({ identity: { worldName: 'Harbor' }, theme: { logo: '/logo.png?a="b"' } });
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"><style>.p{fill:#17181a}.r{stroke:#17181a}@media (prefers-color-scheme:dark){.p{fill:#e2e1e4}.r{stroke:#e2e1e4}}</style><path class="r" d="M137 31 A78 78 0 1 0 170 66" fill="none" stroke-width="24" stroke-linecap="round"/><circle class="p" cx="100" cy="100" r="50"/><circle cx="156" cy="46" r="18" fill="#fb9569"/></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" role="img" aria-label="Cloud Bongos"><path d="M137 31 A78 78 0 1 0 170 66" fill="none" stroke="#17181a" stroke-width="15" stroke-linecap="round"/><circle cx="100" cy="100" r="55" fill="#17181a"/><circle cx="156" cy="46" r="12" fill="#fb9569"/></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" role="img" aria-label="Cloud Bongos"><path d="M137 31 A78 78 0 1 0 170 66" fill="none" stroke="#e2e1e4" stroke-width="15" stroke-linecap="round"/><circle cx="100" cy="100" r="55" fill="#e2e1e4"/><circle cx="156" cy="46" r="12" fill="#fb9569"/></svg>
|
|
Binary file
|
|
Binary file
|