Files
odysseus/tests/test_editor_render_cancellation_js.py
T

116 lines
4.8 KiB
Python

import json
import subprocess
import textwrap
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MODULE = (ROOT / "static/js/editor/render-cancellation.js").as_uri()
EDITOR_SOURCE = ROOT / "static/js/galleryEditor.js"
ADJUSTMENT_SOURCE = ROOT / "static/js/editor/adjustment-layer.js"
EFFECTS_SOURCE = ROOT / "static/js/editor/effects.js"
def run_node(body: str):
script = textwrap.dedent(
f"""
import {{createRenderGeneration}} 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_new_render_generation_invalidates_previous_work():
result = run_node(
"""
const generations = createRenderGeneration();
const first = generations.begin();
const second = generations.begin();
console.log(JSON.stringify({first:first.isCurrent(), second:second.isCurrent()}));
"""
)
assert result == {"first": False, "second": True}
def test_cancel_only_invalidates_the_cancelled_generation():
result = run_node(
"""
const generations = createRenderGeneration();
const render = generations.begin();
render.cancel();
const next = generations.begin();
console.log(JSON.stringify({cancelled:render.isCurrent(), next:next.isCurrent()}));
"""
)
assert result == {"cancelled": False, "next": True}
def test_live_composite_coalesces_worker_generations_to_one_latest_rerender():
editor = EDITOR_SOURCE.read_text()
assert "let _asyncCompositeInFlight = null;" in editor
assert "let _asyncCompositeQueued = false;" in editor
assert "_renderGeneration.invalidate();" in editor
assert "_asyncCompositeQueued = true;" in editor
assert "if (_asyncCompositeQueued)" in editor
def test_editor_close_invalidates_async_composite_lifecycle():
editor = EDITOR_SOURCE.read_text()
close_start = editor.index("export function closeEditor(")
close_source = editor[close_start:]
assert "_renderGeneration.invalidate();" in close_source
assert "_asyncCompositeQueued = false;" in close_source
assert "_asyncCompositeInFlight = null;" in close_source
def test_stale_adjustment_worker_errors_do_not_run_full_resolution_fallback():
adjustment = ADJUSTMENT_SOURCE.read_text()
assert "worker.onerror = () => finish(shouldContinue() ? applyAdjustment(source, adjustment) : null);" in adjustment
assert "return shouldContinue() ? applyAdjustment(source, adjustment) : null;" in adjustment
assert "if (!shouldContinue()) return false;\n adjusted = renderWithLayerMasks" in adjustment
def test_stale_effect_worker_errors_do_not_run_full_resolution_fallback():
effects = EFFECTS_SOURCE.read_text()
assert "return shouldContinue() ? renderEffects(source, effects, shouldContinue) : null;" in effects
assert "worker.onerror = () => finish(shouldContinue() ? renderEffects(source, effects, shouldContinue) : null);" in effects
assert "if (!shouldContinue()) {\n sourceBitmap.close?.();" in effects
def test_autosave_thumbnail_uses_only_a_completed_composite_surface():
editor = EDITOR_SOURCE.read_text()
assert "state.documentRenderReady = false;" in editor
assert "state.documentRenderReady = true;" in editor
assert "state.documentRenderReady && state.documentCompositeCanvas" in editor
assert ": flatten();" in editor
def test_autosave_thumbnail_encoding_can_leave_the_main_thread():
editor = EDITOR_SOURCE.read_text()
assert "async function _buildThumbnailAsync()" in editor
assert "new URL('./editor/thumbnail-worker.js', import.meta.url)" in editor
assert "const thumbnailPromise = _buildThumbnailAsync();" in editor
assert "const savePromise = doSave();" in editor
assert "state.persistInFlight = savePromise" in editor
def test_autosave_serializes_canvas_payloads_from_an_immutable_metadata_snapshot():
editor = EDITOR_SOURCE.read_text()
assert "function _serializationSnapshot(source, canvasProxy)" in editor
assert "new URL('./editor/serialization-worker.js', import.meta.url)" in editor
assert "const payload = _serializeEditorDocument(snapshot);" in editor
assert "const encoded = await _encodeSerializedCanvases(canvases);" in editor
assert "const payloadPromise = _buildDraftPayloadAsync();" in editor
def test_autosave_captures_payload_and_thumbnail_before_close_can_clear_state():
editor = EDITOR_SOURCE.read_text()
assert "const payloadPromise = _buildDraftPayloadAsync();" in editor
assert "const thumbnailPromise = _buildThumbnailAsync();" in editor
assert "const [payload, thumbnail] = await Promise.all([payloadPromise, thumbnailPromise]);" in editor