From f66cdbfeb2e8b68e70e1f8595c38cacf58210ddf Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Sat, 2 May 2026 01:28:19 +0200 Subject: fix: address review findings in masks substrate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove dead ternary in getPreferredEdges (always returned undefined; the all-false object was never reached by Object.values().some(Boolean)) - Document why drawWithFeather descriptors zero the feather field in the compositor (analytical renderer handles softness itself; zeroing prevents a second JFA feather pass) - Remove legacyType from V30→V31 migration (not consumed anywhere; a down-migration can map type:"freeform" back to "custom" directly) - Add V30→V31 test coverage: skip-guard branches, freeform passthrough, elements without a masks array - Add snap tests for vertical-edge (bottom) and uniform-scale handle paths Co-authored-by: Cursor --- .../migrations/__tests__/v30-to-v31.test.ts | 74 +++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'apps/web/src/services/storage/migrations/__tests__') diff --git a/apps/web/src/services/storage/migrations/__tests__/v30-to-v31.test.ts b/apps/web/src/services/storage/migrations/__tests__/v30-to-v31.test.ts index e5c82ae2..65a1ffed 100644 --- a/apps/web/src/services/storage/migrations/__tests__/v30-to-v31.test.ts +++ b/apps/web/src/services/storage/migrations/__tests__/v30-to-v31.test.ts @@ -50,8 +50,80 @@ describe("V30 to V31 Migration", () => { expect(mask).toMatchObject({ id: "mask-1", type: "freeform", - legacyType: "custom", params, }); + expect(asRecord(mask).legacyType).toBeUndefined(); + }); + + test("leaves a type:freeform mask in a v30 project unchanged", () => { + const result = transformProjectV30ToV31({ + project: { + id: "project-v30-already-freeform", + version: 30, + scenes: [ + { + tracks: { + main: { + elements: [ + { + id: "elem-1", + masks: [{ id: "m1", type: "freeform", params: {} }], + }, + ], + }, + }, + }, + ], + }, + }); + + expect(result.skipped).toBe(false); + const scene = asRecordArray(result.project.scenes)[0]; + const main = asRecord(asRecord(scene.tracks).main); + const element = asRecordArray(main.elements)[0]; + expect(asRecordArray(asRecord(element).masks)[0]).toMatchObject({ + id: "m1", + type: "freeform", + }); + }); + + test("leaves elements without a masks array unchanged", () => { + const result = transformProjectV30ToV31({ + project: { + id: "project-v30-no-masks", + version: 30, + scenes: [ + { + tracks: { + main: { + elements: [{ id: "elem-1", type: "video" }], + }, + }, + }, + ], + }, + }); + + expect(result.skipped).toBe(false); + const scene = asRecordArray(result.project.scenes)[0]; + const main = asRecord(asRecord(scene.tracks).main); + const element = asRecordArray(main.elements)[0]; + expect(element).toMatchObject({ id: "elem-1", type: "video" }); + }); + + test("skips a project that is already v31", () => { + const project = { id: "p1", version: 31, scenes: [] }; + const result = transformProjectV30ToV31({ project }); + expect(result.skipped).toBe(true); + expect(result.reason).toBe("already v31"); + expect(result.project).toBe(project); + }); + + test("skips a project that is not v30", () => { + const project = { id: "p1", version: 29, scenes: [] }; + const result = transformProjectV30ToV31({ project }); + expect(result.skipped).toBe(true); + expect(result.reason).toBe("not v30"); + expect(result.project).toBe(project); }); }); -- cgit v1.3.1