import json import subprocess from html.parser import HTMLParser from pathlib import Path from urllib.parse import urljoin FRONTEND = Path(__file__).parents[1] / "frontend" COMMANDS = FRONTEND / "commands.js" class ScriptSourceParser(HTMLParser): def __init__(self): super().__init__() self.sources = [] def handle_starttag(self, tag, attrs): if tag == "script": source = dict(attrs).get("src") if source: self.sources.append(source) def test_filtered_command_runs_the_matching_action(): script = f""" const filterCommands = require({json.dumps(str(COMMANDS))}); let action = ''; const commands = [ {{ name: 'Open whiteboard', run: () => {{ action = 'whiteboard'; }} }}, {{ name: 'Refresh now', run: () => {{ action = 'refresh'; }} }}, ]; const matches = filterCommands(commands, 'refresh'); if (matches.length !== 1) throw new Error(`expected one match, got ${{matches.length}}`); matches[0].run(); if (action !== 'refresh') throw new Error(`expected refresh, got ${{action}}`); """ subprocess.run(["node", "-e", script], check=True, capture_output=True, text=True) def test_command_script_resolves_inside_dashboard_subpath(): parser = ScriptSourceParser() parser.feed((FRONTEND / "index.html").read_text()) command_source = next(source for source in parser.sources if source.endswith("commands.js")) assert urljoin( "https://forge.alexanderwhitestone.com/dashboard/", command_source ) == "https://forge.alexanderwhitestone.com/dashboard/static/commands.js" def test_remote_command_search_ignores_stale_responses(): script = f""" const filterCommands = require({json.dumps(str(COMMANDS))}); (async () => {{ const pending = new Map(); const states = []; const controller = filterCommands.createGlobalSearchController({{ delay: 0, search: query => new Promise(resolve => pending.set(query, resolve)), onState: state => states.push(state), }}); controller.setQuery('mobile'); await new Promise(resolve => setTimeout(resolve, 0)); controller.setQuery('release'); await new Promise(resolve => setTimeout(resolve, 0)); pending.get('release')([{{ title: 'Current result' }}]); await new Promise(resolve => setTimeout(resolve, 0)); pending.get('mobile')([{{ title: 'Stale result' }}]); await new Promise(resolve => setTimeout(resolve, 0)); const ready = states.filter(state => state.status === 'ready'); if (ready.length !== 1) throw new Error(`expected one ready state, got ${{ready.length}}`); if (ready[0].query !== 'release' || ready[0].items[0].title !== 'Current result') {{ throw new Error(`stale response replaced current state: ${{JSON.stringify(ready)}}`); }} }})().catch(error => {{ console.error(error); process.exit(1); }}); """ subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True, ) def test_command_selection_wraps_for_arrow_keys(): script = f""" const commands = require({json.dumps(str(COMMANDS))}); if (commands.nextSelection(-1, 'ArrowDown', 3) !== 0) throw new Error('should select first'); if (commands.nextSelection(2, 'ArrowDown', 3) !== 0) throw new Error('should wrap down'); if (commands.nextSelection(0, 'ArrowUp', 3) !== 2) throw new Error('should wrap up'); if (commands.nextSelection(1, 'Enter', 3) !== 1) throw new Error('other keys should not move'); """ subprocess.run(["node", "-e", script], check=True, capture_output=True, text=True) def test_palette_exposes_accessible_global_work_search_under_dashboard_subpath(): html = (FRONTEND / "index.html").read_text() assert 'role="combobox"' in html assert 'aria-controls="cmd-results"' in html assert 'role="listbox"' in html assert "fetch('api/v1/search?q='" in html assert urljoin( "https://forge.alexanderwhitestone.com/dashboard/", "api/v1/search?q=mobile", ) == "https://forge.alexanderwhitestone.com/dashboard/api/v1/search?q=mobile"