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

62 lines
2.6 KiB
Python

import json
import subprocess
import textwrap
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MODULE = (ROOT / 'static/js/editor/aspect-fit.js').as_uri()
def run_node(body):
script = textwrap.dedent(f"""
import * as A 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_aspect_fit_preserves_subject_ratio_within_two_percent():
# A 900x600 canvas with a subject (face) whose bbox has ratio 0.62, ask for
# a 1080x1350 (4:5) Instagram post. The crop-to-aspect step then the uniform
# scale must keep the subject bbox w/h ratio within 2%.
out = run_node("""
const docW=900, docH=600;
const subj={x:330,y:120,w:260,h:420}; // ratio 0.619
const crop=A.aspectFitCropRect({width:docW,height:docH,focusX:subj.x+subj.w/2,focusY:subj.y+subj.h/2,targetAspect:1080/1350});
// subject clipped to the crop window
const ix=Math.max(subj.x,crop.x), iy=Math.max(subj.y,crop.y);
const ix2=Math.min(subj.x+subj.w,crop.x+crop.width), iy2=Math.min(subj.y+subj.h,crop.y+crop.height);
const sx=(ix2-ix)*(1080/crop.width), sy=(iy2-iy)*(1350/crop.height); // uniform scale
console.log(JSON.stringify({ crop, subjRatio:subj.w/subj.h, afterRatio:sx/sy }));
""")
assert abs(out['afterRatio'] - out['subjRatio']) / out['subjRatio'] <= 0.02, out
# The crop really is 4:5 and not the full 3:2 canvas.
assert abs(out['crop']['width'] / out['crop']['height'] - 1080 / 1350) <= 0.01
def test_aspect_fit_no_crop_when_aspect_already_matches():
out = run_node("""
const r=A.aspectFitCropRect({width:1080,height:1350,targetAspect:1080/1350});
console.log(JSON.stringify(r));
""")
assert out == {'x': 0, 'y': 0, 'width': 1080, 'height': 1350}
def test_aspect_fit_centres_on_focus_and_clamps_into_bounds():
out = run_node("""
// Tall subject near the left edge -> crop stays inside the document.
const crop=A.aspectFitCropRect({width:900,height:600,focusX:60,focusY:300,targetAspect:4/5});
const edge=A.aspectFitCropRect({width:900,height:600,focusX:450,focusY:300,targetAspect:4/5});
console.log(JSON.stringify({crop,edge}));
""")
# focus at x=60 -> centred crop would start at -180 -> clamped to 0
assert out['crop']['x'] == 0
assert out['crop']['height'] == 600 and out['crop']['width'] == 480
# centred focus -> starts at 210
assert out['edge']['x'] == 210