Files
odysseus/tests/test_tour_hint_dismiss_js.py
T
pewdiepie-archdaemon 2e8413a54a Preserve preview harness, editor, email and task improvements
Snapshot current maintainer-preview application changes and regression fixtures for integration into lab. Excludes local runtime data, evaluation outputs and source backups. Focused Python regression selection: 140 passed; full suite not certified.
2026-10-01 01:34:26 +00:00

70 lines
2.6 KiB
Python

"""Exercise tutorial dismissal against the real shared Escape stack."""
import shutil
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
pytestmark = pytest.mark.skipif(not shutil.which('node'), reason='node unavailable')
def test_tutorial_hints_dismiss_one_at_a_time_and_release_every_close_path():
stack = (ROOT / 'static/js/escMenuStack.js').read_text()
helper = (ROOT / 'static/js/tourHintDismiss.js').read_text().split('\n', 1)[1]
script = stack + '\n' + helper + r'''
import assert from 'node:assert/strict';
const timers = new Map();
let nextTimer = 0;
globalThis.setTimeout = (fn, delay) => { const id = ++nextTimer; timers.set(id, {fn, delay}); return id; };
globalThis.clearTimeout = id => timers.delete(id);
function hint() {
const listeners = new Map();
const classes = new Set();
return {
isConnected: true,
classList: {add: name => classes.add(name), contains: name => classes.has(name)},
querySelector: () => ({
addEventListener: (type, fn) => listeners.set(type, fn),
removeEventListener: type => listeners.delete(type),
}),
click: () => listeners.get('click')?.(),
remove() { this.isConnected = false; },
};
}
let windowClosed = 0;
registerEscapeLayer(() => windowClosed++);
const first = hint(), second = hint();
let firstClosed = 0, secondClosed = 0;
bindTourHintDismiss(first, {timeout: 14000, onDismiss: () => firstClosed++});
bindTourHintDismiss(second, {timeout: 6500, onDismiss: () => secondClosed++});
assert.equal(dismissTopEscapeLayer(), true);
assert.equal(secondClosed, 1);
assert.equal(firstClosed, 0);
assert.equal(windowClosed, 0);
assert.equal(second.classList.contains('tour-hint-out'), true);
second._dismiss();
second.click();
assert.equal(secondClosed, 1);
assert.equal([...timers.values()].some(t => t.delay === 6500), false);
first.click();
assert.equal(firstClosed, 1);
assert.equal(_openMenuCount(), 1);
assert.equal([...timers.values()].some(t => t.delay === 14000), false);
assert.equal(dismissTopEscapeLayer(), true);
assert.equal(windowClosed, 1);
const timed = hint();
let timedClosed = 0;
bindTourHintDismiss(timed, {timeout: 100, onDismiss: () => timedClosed++});
[...timers.values()].find(t => t.delay === 100).fn();
assert.equal(timedClosed, 1);
assert.equal(dismissTopEscapeLayer(), false);
const detached = hint();
bindTourHintDismiss(detached);
detached.remove();
assert.equal(dismissTopEscapeLayer(), false);
'''
result = subprocess.run(['node', '--input-type=module'], input=script,
text=True, capture_output=True, timeout=15)
assert result.returncode == 0, result.stderr