Files
odysseus/tests/test_editor_canvas_context_menu.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

119 lines
3.9 KiB
Python

import json
import subprocess
import textwrap
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MODULE = (ROOT / "static/js/editor/canvas-context-menu.js").as_uri()
STATE = (ROOT / "static/js/editor/state.js").as_uri()
def run_node(body: str):
result = subprocess.run(
["node", "--input-type=module", "-e", textwrap.dedent(body)],
cwd=ROOT,
text=True,
capture_output=True,
check=True,
)
return json.loads(result.stdout)
def labels(items):
out = []
for item in items:
if item.get("type") == "sep":
out.append("—")
else:
out.append(item["label"])
return out
def menu_for(tool: str, selection: bool = False):
return run_node(
f"""
import {{ state }} from {json.dumps(STATE)};
import {{ buildCanvasMenuItems }} from {json.dumps(MODULE)};
state.editorOpen = true;
state.tool = {json.dumps(tool)};
state.wandMask = {json.dumps({"w": 1, "h": 1} if selection else None)};
state.brushPixel = false;
const items = buildCanvasMenuItems(state.tool);
const out = items.map((i) => ({{ type: i.type || 'item', label: i.label || '', disabled: !!i.disabled, hint: i.hint || '' }}));
console.log(JSON.stringify(out));
"""
)
def test_paint_tool_menu_has_brush_controls():
items = menu_for("brush")
ls = labels(items)
assert "Increase Brush Size" in ls
assert "Decrease Brush Size" in ls
assert "Brush Preset" in ls
assert "Pixel Brush" in ls
# Common edit actions are always available.
assert "Undo" in ls and "Redo" in ls
assert "Rotate 90° CW" in ls
def test_pixel_brush_entry_is_checked_when_pixel_mode_active():
items = run_node(
f"""
import {{ state }} from {json.dumps(STATE)};
import {{ buildCanvasMenuItems }} from {json.dumps(MODULE)};
state.tool = 'brush';
state.brushPixel = true;
const items = buildCanvasMenuItems('brush');
const pixel = items.find((i) => i.label === 'Pixel Brush');
console.log(JSON.stringify({{ checked: !!pixel.checked }}));
"""
)
assert items["checked"] is True
def test_selection_tool_menu_has_selection_ops():
ls = labels(menu_for("marquee", selection=True))
assert "Select All" in ls
assert "Deselect" in ls
assert "Inverse Selection" in ls
assert "Feather…" in ls
assert "Expand" in ls
assert "Contract" in ls
def test_selection_items_disabled_without_selection():
items = menu_for("brush", selection=False)
by_label = {i["label"]: i for i in items if i["type"] == "item"}
assert by_label["Cut"]["disabled"] is True
assert by_label["Copy"]["disabled"] is True
assert by_label["Paste"]["disabled"] is False
def test_crop_and_transform_tools_have_context_actions():
crop = labels(menu_for("crop"))
assert "Apply Crop" in crop
assert "Cancel Crop" in crop
transform = labels(menu_for("transform"))
assert "Commit Transform" in transform
assert "Cancel Transform" in transform
def test_context_menu_is_wired_into_canvas_events():
events = (ROOT / "static/js/editor/canvas-events.js").read_text()
assert "import { wireCanvasContextMenu } from './canvas-context-menu.js';" in events
assert "wireCanvasContextMenu(canvasArea, { signal });" in events
module = (ROOT / "static/js/editor/canvas-context-menu.js").read_text()
assert "export function buildCanvasMenuItems(" in module
assert "export function openCanvasContextMenu(" in module
assert "window.geEditorControl?.invoke(" in module
assert "canvas.addEventListener('contextmenu'" in module
def test_context_menu_styles_exist():
css = (ROOT / "static/css/13-image-editor.css").read_text()
assert ".ge-canvas-menu {" in css
assert ".ge-canvas-menu-item" in css
assert ".ge-canvas-menu-sub" in css