Skip to content

test(studio): add T5c review-fix gaps in manualEditsDomPatches characterization#1259

Merged
vanceingalls merged 1 commit into
mainfrom
06-07-test_studio_add_t5c_review-fix_gaps_in_manualeditsdompatches_characterization
Jun 8, 2026
Merged

test(studio): add T5c review-fix gaps in manualEditsDomPatches characterization#1259
vanceingalls merged 1 commit into
mainfrom
06-07-test_studio_add_t5c_review-fix_gaps_in_manualeditsdompatches_characterization

Conversation

@vanceingalls
Copy link
Copy Markdown
Collaborator

@vanceingalls vanceingalls commented Jun 7, 2026

Summary

T5 (part 3 of 3) — gaps identified in max-setting code review of T5a/T5b, fixed here.

Findings addressed

Important (concrete bug-survival gaps):

  1. Box-size clear ordered assertion — the existing test used expect.arrayContaining on a partial element, leaving the interleaved restore-then-null loop order completely untested. Replaced with a full toEqual on populatedBoxEl() asserting all 30 ops in exact order.

  2. Motion clear input-independencebuildClearMotionPatches takes _element (intentionally ignored) but the test only called it with div(). A future refactor accidentally reading the element would pass on an empty element while failing on a populated one. Fixed by asserting buildClearMotionPatches(populatedMotionEl()) produces the same output as buildClearMotionPatches(div()).

Suggestions (edge-case branches, reachable in production):

  1. Empty-string coercionbuildClear* functions use origVal || null to coerce an empty-string saved attribute into a null op value (meaning "remove the style, don't set it to ""). Added coercion tests for pathOffset (translate), boxSize (width), and rotation (rotate).

  2. Rotation clear with absent STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR — the null !== null branch in the ternary produces value: null via a distinct path from the coercion path. Added explicit test.

Stack

miguel-heygen
miguel-heygen previously approved these changes Jun 8, 2026
Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR delivers the tightening pass I expected after T5a/b.

Three gaps closed:

  1. buildClearBoxSizePatches full ordered toEqual — 30 ops, exact interleaved restore-then-null order. The previous arrayContaining would have passed if ops were reordered or extra ops were silently added. The full ordered assertion is the right level of strictness for a clear-patches characterization.

  2. buildClearMotionPatches input-independencepopulatedMotionEl() produces the same output as div(). This is the only place in the suite where the function's underscore parameter is tested behaviorally. Critical — without this, a future refactor that accidentally reads the element wouldn't be caught.

  3. Empty-string coercionSTUDIO_ORIGINAL_*_ATTR=""value: null via the origVal || null pattern. Three cases covered (translate, width, rotate). This pins the contract that empty attributes mean "remove the property", not "set it to an empty string".

No issues. Solid closure pass.

Copy link
Copy Markdown

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round-1 review of T5c (review-fix gaps). Walked the delta from #1258 (+69/-16) and verified each of the four called-out gaps is closed by a concrete test, with the implementation paths in manualEditsDomPatches.ts cross-referenced. This PR is the load-bearing-fix layer of the T5 stack — every issue I would have raised on #1257 and #1258 is addressed here, plus two coercion-path gaps I didn't have on my own list.

What's strong (the four called-out gaps)

1. Box-size clear ordered assertion. Lines 213-247, full toEqual over populatedBoxEl() asserting all 30 ops in exact interleaved restore-then-null order. The previous expect.arrayContaining left the order of the for (const [attrName, styleProp] of BOX_SIZE_ORIG_ATTRS) loop in buildClearBoxSizePatches:155-161 completely untested. Now locked in. ✓

2. Motion clear input-independence. Lines 381-389, the two-call expect(…).toEqual(expected); expect(buildClearMotionPatches(populatedMotionEl())).toEqual(expected); pattern. Matches my expected fix shape — a future refactor that accidentally reads _element would pass on bare div() but fail on the populated case. ✓

3. Empty-string coercion paths. Three tests (pathOffset translate at line 121-126, boxSize width at 248-253, rotation rotate at 333-338) covering the origVal || null coercion in buildClear* functions. The semantics — "an empty saved attribute means remove the style, not set it to """ — is now an explicit invariant rather than an implementation detail. ✓

4. Rotation clear with absent STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR. Line 327-330, asserts the absent-attr path through the origRotationTransformOrigin !== null ? origRotationTransformOrigin || null : null ternary at manualEditsDomPatches.ts:200. That ternary has a distinct null pathway from the coercion path (absent vs. empty-string → null), and now both are covered. ✓

Concerns

[nit — toHaveLength(17) is a magic number] Line 261:

it("clear: bare element emits only null ops — no style restores fire when orig attrs are absent", () => {
  const ops = buildClearBoxSizePatches(div());
  // 3 fixed (studio-width, studio-height, box-size marker) + 14 attr-null pushes (one per BOX_SIZE_ORIG_ATTR)
  expect(ops).toHaveLength(17);
  expect(ops.every((op) => op.value === null)).toBe(true);
});

The 17 is explained in the comment, but it's tied to BOX_SIZE_ORIG_ATTRS.length + 3 and would break opaquely if a new orig attr is added in the next refactor. Two options:

  • Compute the expected length inline: expect(ops).toHaveLength(BOX_SIZE_ORIG_ATTRS.length + 3) — but that requires importing the array, which inverts the test/impl coupling.
  • Drop the length check, keep the "every op is null" check. The shape expect(ops.every((op) => op.value === null)).toBe(true) covers the real invariant (no style restores fire on bare element); the length is incidental.

Lean toward the second — the load-bearing assertion is "no restores fire," not "exactly 17 ops emitted."

[nit — ops.find(o => o.property === "width")?.value is fragile to duplicates] Used in three places (lines 124, 251, 336). If a future refactor accidentally emits two ops with the same property (e.g. one inline-style and one attribute, both named "width"), .find() returns only the first and the test passes even though the second op is wrong. Lower-risk pattern: expect(ops.filter(o => o.property === "width" && o.type === "inline-style")).toEqual([{ … }]). Not blocking — duplicates would surface in the toEqual populated tests in the same describe block.

Verdict

Clean review-fix PR. The four called-out gaps are all closed with the right fix shape, and the two new coercion tests (pathOffset / rotation empty-string) cover paths I hadn't called out on the earlier PRs. The stack converges cleanly at this layer. Two small nits on assertion shape that are non-blocking. Leaving as a comment.

Review by Rames D Jusso

vanceingalls added a commit that referenced this pull request Jun 8, 2026
…on (#1257)

## Summary

T5 (part 1 of 3) — characterization test suite for `manualEditsDomPatches.ts`.

The source module exports 8 functions (4 build/clear pairs) that write and restore draft-marker attributes and inline styles onto iframe elements before source-patch operations. It had zero tests.

This PR covers the **pathOffset** and **boxSize** pairs with 4 patterns each:

- **populated** — fully-configured element produces exact expected `PatchOperation[]` in declaration order
- **empty** — bare element yields only the mandatory marker attribute op
- **clear restores originals** — `buildClear*` reads `STUDIO_ORIGINAL_*` attrs and produces correct restore values
- **build/clear symmetry** — every `{type, property}` key that `build*` can emit is also addressed by `buildClear*`; an orphan here means a property stranded in committed source HTML

Uses `@vitest-environment happy-dom` matching the Studio package convention. Element setup via `document.createElement` + `style.setProperty` / `setAttribute`.

## Stack

- **#1257** (this PR) — pathOffset + boxSize pairs
- **#1258** — rotation + motion pairs
- **#1259** — review-fix gaps (ordered clear assertion, coercion paths, edge cases)
@vanceingalls vanceingalls force-pushed the 06-07-test_studio_add_t5b_rotation_motion_build-patches_characterization branch 2 times, most recently from b258756 to 0bf71d2 Compare June 8, 2026 02:08
@vanceingalls vanceingalls force-pushed the 06-07-test_studio_add_t5c_review-fix_gaps_in_manualeditsdompatches_characterization branch from c365c95 to e79343e Compare June 8, 2026 02:08
Base automatically changed from 06-07-test_studio_add_t5b_rotation_motion_build-patches_characterization to main June 8, 2026 02:15
@vanceingalls vanceingalls dismissed miguel-heygen’s stale review June 8, 2026 02:15

The base branch was changed.

…terization

Fixes four gaps identified in max-setting code review:
- Box-size clear: replace arrayContaining with full ordered toEqual (30 ops)
- Box-size / pathOffset / rotation clear: add empty-string coercion tests
  (origVal||null must produce null, not set property to "")
- Rotation clear: add test for absent STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR
- Motion clear: prove input-independence by calling with both empty and populated
  element and asserting identical output
@vanceingalls vanceingalls force-pushed the 06-07-test_studio_add_t5c_review-fix_gaps_in_manualeditsdompatches_characterization branch from e79343e to 2b1c7c8 Compare June 8, 2026 02:18
@vanceingalls vanceingalls merged commit 5b29bea into main Jun 8, 2026
35 checks passed
@vanceingalls vanceingalls deleted the 06-07-test_studio_add_t5c_review-fix_gaps_in_manualeditsdompatches_characterization branch June 8, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants