clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

A shelf link followed on its owner page opens that shelf #26

4 files changed, +36 -15
Msite/assets/ui.ts15++++++++++++---
Mtest/page.ts25++++++++++++++++---------
Mtest/scenarios/shelves-fragment.sh9+++++++--
Mtest/scenarios/times-relative.sh2+-
M site/assets/ui.ts +12 -3
M test/page.ts +16 -9
@@ -4,10 +4,10 @@
44 // hidden, text, moving children, and selectors made of tags, classes and55 // attributes, descendants and lists.66 //7-// node test/page.ts shelves <page.html> [#fragment] [click <selector>]...7+// node test/page.ts shelves <page.html> [#fragment] [click <selector> | go <#fragment>]...88 //9-// After loading the page at the fragment and clicking each selector in10-// turn, prints the shelf panels that show, a line each:9+// After loading the page at the fragment, then clicking each selector or10+// going to each fragment on the page (hashchange) in turn, prints the shelf panels that show, a line each:1111 // `<head strip>: <repo> <repo>...`, the repos as their rows show them.1212 import fs from 'node:fs';1313 import vm from 'node:vm';
@@ -197,9 +197,11 @@ function load(file: string, hash: string) {
197197 },198198 });199199 const noop = (): void => {};200+ const location = { hash };200201 const page = vm.createContext({201202 document: doc,202- location: { hash },203+ window: { addEventListener: (type: string, f: (e: object) => void) => (handlers['window ' + type] ??= []).push(f) },204+ location,203205 localStorage: { getItem: () => null, setItem: noop },204206 matchMedia: () => ({ matches: false }),205207 setInterval: noop,
@@ -221,7 +223,11 @@ function load(file: string, hash: string) {
221223 if (!target) throw new Error(`test/page.ts: nothing on the page matches ${selector}`);222224 for (const f of handlers.click ?? []) f({ type: 'click', target, preventDefault: noop });223225 };224- return { doc, click };226+ const go = (to: string): void => {227+ location.hash = to;228+ for (const f of handlers['window hashchange'] ?? []) f({ type: 'hashchange' });229+ };230+ return { doc, click, go };225231 }226232 227233 const shown = (e: El): boolean => !e.closest('[hidden]');
@@ -229,14 +235,15 @@ const words = (s: string): string => s.replace(/\s+/g, ' ').trim();
229235 230236 const [mode, file, ...rest] = process.argv.slice(2);231237 if (mode !== 'shelves' || !file) {232- console.error('usage: node test/page.ts shelves <page.html> [#fragment] [click <selector>]...');238+ console.error('usage: node test/page.ts shelves <page.html> [#fragment] [click <selector> | go <#fragment>]...');233239 process.exit(2);234240 }235241 const hash = rest[0]?.startsWith('#') ? rest.shift()! : '';236-const { doc, click } = load(file, hash);242+const { doc, click, go } = load(file, hash);237243 for (let i = 0; i < rest.length; i += 2) {238- if (rest[i] !== 'click' || !rest[i + 1]) throw new Error(`test/page.ts: expected click <selector>, got ${rest.slice(i).join(' ')}`);239- click(rest[i + 1]);244+ if (rest[i] === 'click' && rest[i + 1]) click(rest[i + 1]);245+ else if (rest[i] === 'go' && rest[i + 1]) go(rest[i + 1]);246+ else throw new Error(`test/page.ts: expected click <selector> or go <#fragment>, got ${rest.slice(i).join(' ')}`);240247 }241248 for (const sec of doc.querySelectorAll('section.shelf').filter(shown)) {242249 const names = sec.querySelectorAll('.card').filter(shown).map((c) => words(c.querySelector('.card-name')!.textContent).replace(' / ', '/'));
M test/scenarios/shelves-fragment.sh +7 -2
M test/scenarios/times-relative.sh +1 -1