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

75 lines
3.2 KiB
Python

"""Select Subject (offline one-click subject selection).
Static contract tests: the offline saliency engine, its wiring into the
`selection.subject` control op, the Select-menu entry and the sw cache bump.
"""
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
MODULE = ROOT / "static" / "js" / "editor" / "select-subject.js"
EDITOR = ROOT / "static" / "js" / "galleryEditor.js"
TOPBAR = ROOT / "static" / "js" / "editor" / "build" / "topbar.js"
WIRE = ROOT / "static" / "js" / "editor" / "wire-topbar-menus.js"
SW = ROOT / "static" / "sw.js"
def test_module_exists_and_is_offline():
src = MODULE.read_text()
assert "export function computeSubjectMask(" in src
assert "fetch(" not in src
assert "XMLHttpRequest" not in src
def test_engine_uses_border_background_otsu_and_largest_blob():
src = MODULE.read_text()
# background estimated from the border cells
assert "Background colour = average of the border cells" in src
assert "addCell(sx, 0);" in src
assert "addCell(sx, sh - 1);" in src
assert "addCell(0, sy);" in src
assert "addCell(sw - 1, sy);" in src
# Otsu threshold helper
assert "function otsuThreshold(hist, total) {" in src
assert "const threshold = (otsuThreshold(hist, sal.length) / 255) * maxSal;" in src
# largest connected component (4-neighbour flood fill)
assert "Keep the largest connected component (4-neighbour flood fill)." in src
assert "if (!best || group.length > best.length) best = group;" in src
assert "if (!best || best.length < 0.005 * sw * sh) return null;" in src
# guard when there is no distinct subject
assert "if (maxSal < 0.12) return null;" in src
# smooth upsample into a full-size mask
assert "mctx.imageSmoothingQuality = 'high';" in src
assert "mctx.drawImage(small, 0, 0, width, height);" in src
def test_editor_imports_and_helper():
src = EDITOR.read_text()
assert "import { computeSubjectMask as _computeSubjectMask } from './editor/select-subject.js';" in src
assert "function _applyOfflineSubjectSelection(layer, mode = 'replace') {" in src
assert "_computeSubjectMask(data, w, h, {});" in src
assert "_commitSelectionMask(mask, layer, mode, 'subject');" in src
def test_control_op_prefers_offline_saliency():
src = EDITOR.read_text()
assert "'selection.subject':" in src
assert "const offline = _applyOfflineSubjectSelection(layer, mode);" in src
assert "if (offline) return { mode, method: 'saliency', width: offline.width, height: offline.height };" in src
# still has the SAM + approximate fallbacks
assert "method: 'sam'" in src
assert "method: 'approx'" in src
assert "_samWithRetry(layer, { text }, mode, null);" in src
def test_select_menu_entry_and_wiring():
topbar = TOPBAR.read_text()
assert 'data-selection-action="subject"' in topbar
assert '<button class="dropdown-item-compact" data-selection-action="subject"><span>Subject</span></button>' in topbar
wire = WIRE.read_text()
assert "else if (action === 'subject') { window.geEditorControl?.invoke?.('selection.subject', {}); handled = true; }" in wire
def test_sw_cache_name_bumped():
assert re.search(r"odysseus-v\d+-pr40-editor-precache", SW.read_text())