Files
odysseus/tests/test_editor_bridge_js.py
T
pewdiepie-archdaemonandClaude Opus 5.5 5fac693c1c feat(photo-editor): Ajax Image Editor client (Photoshop-parity tools)
Photoshop-parity editor client: layers, selection/masking, brushes,
smart filters, LUT import, Quick Edit, mobile gestures, workspaces,
batch operations, and the service-worker versioning (v978->v1006).

Source: vendor/photocraft (ad1cff44) photo-editor client files, merged
onto upstream/dev (a8c147b2). Conflict resolution documented in
pr_work/A_conflicts.md; editor files taken from vendor/photocraft
(upstream's editor is an earlier snapshot of the same feature).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 11:57:35 +00:00

80 lines
3.3 KiB
Python

import json
import subprocess
import textwrap
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MODULE = (ROOT / 'static/js/editor/editor-bridge.js').as_uri()
def run_node(body):
script = textwrap.dedent(f"""
import {{ startEditorBridge, BRIDGE_VERSION }} from {json.dumps(MODULE)};
{body}
""")
result = subprocess.run(
['node', '--input-type=module', '-e', script],
cwd=ROOT, text=True, capture_output=True, check=True,
)
return json.loads(result.stdout)
def test_bridge_runs_queued_command_and_posts_result():
out = run_node("""
const calls = [];
const posts = [];
class FakeES {
constructor(url) { this.url = url; FakeES.last = this; }
close() { this.closed = true; }
}
const control = { invoke(op, params) {
calls.push(op);
if (op === 'screenshot') return { ok: true, result: { dataUrl: 'data:image/png;base64,ZZ' } };
return { ok: true, result: 'r:' + op };
} };
const fetchImpl = async (url, opts) => { posts.push({ url, body: JSON.parse(opts.body) }); return { ok: true, json: async () => ({ ok: true }) }; };
const bridge = startEditorBridge({ control, session: 's1', baseUrl: '/x', EventSourceImpl: FakeES, fetchImpl });
FakeES.last.onmessage({ data: JSON.stringify({ id: 'c1', op: 'history.undo', params: {} }) });
await new Promise(r => setTimeout(r, 25));
console.log(JSON.stringify({ esUrl: FakeES.last.url, calls, posts, session: bridge.session, version: BRIDGE_VERSION }));
""")
assert out['esUrl'] == '/x/api/editor/bridge/events?session=s1'
assert 'history.undo' in out['calls']
assert 'screenshot' in out['calls']
assert out['posts'][0]['url'] == '/x/api/editor/bridge/result'
assert out['posts'][0]['body']['id'] == 'c1'
assert out['posts'][0]['body']['ok'] is True
assert out['posts'][0]['body']['screenshot'].startswith('data:image/png')
assert out['session'] == 's1'
assert out['version'] == '1.0'
def test_bridge_reports_op_failure_and_stop_closes_stream():
out = run_node("""
const posts = [];
class FakeES {
constructor(url) { this.url = url; FakeES.last = this; }
close() { this.closed = true; }
}
const control = { invoke(op) {
if (op === 'screenshot') return { ok: true, result: { dataUrl: 'data:image/png;base64,ZZ' } };
return { ok: false, error: 'nope' };
} };
const fetchImpl = async (url, opts) => { posts.push(JSON.parse(opts.body)); return { ok: true, json: async () => ({}) }; };
const bridge = startEditorBridge({ control, session: 'default', EventSourceImpl: FakeES, fetchImpl });
FakeES.last.onmessage({ data: JSON.stringify({ id: 'x', op: 'layers.delete', params: { id: 'layer-9' } }) });
await new Promise(r => setTimeout(r, 25));
bridge.stop();
console.log(JSON.stringify({ body: posts[0], closed: !!FakeES.last.closed }));
""")
assert out['body']['ok'] is False
assert out['body']['error'] == 'nope'
assert out['body']['result'] is None
assert out['closed'] is True
def test_bridge_module_is_precached_for_offline_use():
sw = (ROOT / 'static/sw.js').read_text(encoding='utf-8')
assert "'/static/js/editor/editor-bridge.js'" in sw