mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-25 14:01:36 +01:00
59 lines
3.1 KiB
Python
59 lines
3.1 KiB
Python
"""The page calls showPage() while its first script is still running, before names declared
|
|
later (in that script or the second one) exist. Touching one of them there throws, and the
|
|
rest of the page's setup never runs: opening Frame Control at #games did exactly that."""
|
|
import json
|
|
import pathlib
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import unittest
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
|
STUBS = ['$', 'toggleLive', 'scrollToY', 'scroller', 'loadMacView', 'loadSteamPipewire', 'loadPanels', 'loadPanelSwitcher', 'loadExtensions', 'loadDecky', 'loadHeadsetPanel', 'loadBackups', 'loadStorage', 'loadPower', 'loadTitles', 'showAndroidTab',
|
|
'loadAndroid', 'loadDisplays', 'setGameSource', 'requestAnimationFrame']
|
|
|
|
RUN = r'''
|
|
const vm = require('vm');
|
|
const {code, hashes} = JSON.parse(require('fs').readFileSync(0, 'utf8'));
|
|
const failures = [];
|
|
for (const hash of hashes) {
|
|
const context = vm.createContext({
|
|
location: {hash}, document: {querySelectorAll: () => [], title: ''}, live: false,
|
|
});
|
|
try { vm.runInContext(code, context); }
|
|
catch (e) { failures.push(`${hash}: ${e.message}`); }
|
|
}
|
|
console.log(JSON.stringify(failures));
|
|
'''
|
|
|
|
|
|
@unittest.skipUnless(shutil.which('node'), 'Node runs the page code')
|
|
class PageStartup(unittest.TestCase):
|
|
def test_opening_any_page_or_section_at_startup_runs(self):
|
|
page = (ROOT / 'ui/index.html').read_text(encoding='utf-8')
|
|
first, second = re.findall(r'<script>(.*?)</script>', page, re.S)[:2]
|
|
# The routing tables and the workspace helpers showPage uses, up to showPage itself.
|
|
start = first.index('function showPage(')
|
|
tables = first[first.index('const PAGES ='):start]
|
|
show = first[start:first.index('\n}\n', start) + 3]
|
|
# Everything declared after the startup call is still uninitialised when it runs.
|
|
call = re.search(r'^showPage\(\);', first, re.M).end()
|
|
later = re.findall(r'^(?:const|let)\s+(\w+)', first[call:] + second, re.M)
|
|
later = [n for n in dict.fromkeys(later) if n not in STUBS and n not in ('PAGES', 'SECTION_PAGE', 'page')]
|
|
self.assertIn('link', later) # the name that broke #games
|
|
stubs = ''.join(f'function {n}() {{ return {{ classList: {{ toggle() {{}} }}, scrollIntoView() {{}} }}; }}\n'
|
|
for n in STUBS if n != '$')
|
|
node = '{ classList: { toggle() {} }, scrollIntoView() {}, append() {}, prepend() {}, setAttribute() {}, removeAttribute() {} }'
|
|
code = (f'const $ = id => id === "nowhere" ? null : {node};\nconst gm = {{src: "owned"}};\n' + stubs + tables + show +
|
|
'showPage();\n' + ''.join(f'let {n};\n' for n in later))
|
|
hashes = ['', '#home', '#games', '#android', '#tools', '#settings', '#devices', '#control', '#nowhere']
|
|
hashes += ['#' + k for k in re.findall(r'(\w+): ?"', tables[:tables.index('const workspace')])]
|
|
r = subprocess.run(['node', '-e', RUN], input=json.dumps({'code': code, 'hashes': hashes}),
|
|
capture_output=True, text=True)
|
|
self.assertEqual(r.returncode, 0, r.stderr)
|
|
self.assertEqual(json.loads(r.stdout), [])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|