diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-02-03 14:00:04 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-02-03 14:00:04 +0100 |
| commit | 7366cb27e98d9e0b15e0176ac7ca32816c460284 (patch) | |
| tree | 07635628d37c06489519f7daf336c7f3f4fd3a2a /apps/web/src/services/storage/migrations/__tests__ | |
| parent | 34daad9df7ed6dac4fbeba8619ca1958bb2dac86 (diff) | |
| download | opencut-7366cb27e98d9e0b15e0176ac7ca32816c460284.tar.gz | |
fix v1 to v2 migration
Diffstat (limited to 'apps/web/src/services/storage/migrations/__tests__')
3 files changed, 145 insertions, 113 deletions
diff --git a/apps/web/src/services/storage/migrations/__tests__/fixtures/v0.ts b/apps/web/src/services/storage/migrations/__tests__/fixtures/v0.ts index 24428cfe..d83b221b 100644 --- a/apps/web/src/services/storage/migrations/__tests__/fixtures/v0.ts +++ b/apps/web/src/services/storage/migrations/__tests__/fixtures/v0.ts @@ -7,24 +7,6 @@ export const v0Project = { canvasSize: { width: 1920, height: 1080 }, backgroundColor: "#000000", backgroundType: "color", - tracks: [ - { - id: "track-1", - type: "video", - name: "Video Track", - elements: [ - { - id: "element-1", - type: "video", - mediaId: "media-1", - startTime: 0, - duration: 5, - trimStart: 0, - trimEnd: 0, - }, - ], - }, - ], bookmarks: [1.5, 3.0], }; diff --git a/apps/web/src/services/storage/migrations/__tests__/fixtures/v1.ts b/apps/web/src/services/storage/migrations/__tests__/fixtures/v1.ts index 93904947..7930dd37 100644 --- a/apps/web/src/services/storage/migrations/__tests__/fixtures/v1.ts +++ b/apps/web/src/services/storage/migrations/__tests__/fixtures/v1.ts @@ -1,3 +1,4 @@ +// v1 projects: Scenes exist but tracks stored in separate IndexedDB (video-editor-timelines-{projectId}-{sceneId}) export const v1Project = { id: "project-v1-123", version: 1, @@ -15,42 +16,7 @@ export const v1Project = { id: "scene-main", name: "Main scene", isMain: true, - tracks: [ - { - id: "track-1", - type: "video", - name: "Video Track", - isMain: true, - elements: [ - { - id: "element-1", - type: "video", - mediaId: "media-1", - startTime: 0, - duration: 10, - trimStart: 0, - trimEnd: 0, - }, - ], - }, - { - id: "track-2", - type: "audio", - name: "Audio Track", - elements: [ - { - id: "element-2", - type: "audio", - sourceType: "upload", - mediaId: "media-2", - startTime: 0, - duration: 10, - trimStart: 0, - trimEnd: 0, - }, - ], - }, - ], + tracks: [], bookmarks: [], createdAt: "2024-01-15T10:00:00.000Z", updatedAt: "2024-01-15T12:00:00.000Z", @@ -76,22 +42,7 @@ export const v1ProjectWithMultipleScenes = { id: "scene-1", name: "Intro", isMain: true, - tracks: [ - { - id: "track-1", - type: "video", - isMain: true, - elements: [ - { - id: "el-1", - type: "video", - mediaId: "m1", - startTime: 0, - duration: 5, - }, - ], - }, - ], + tracks: [], bookmarks: [1.0], createdAt: "2024-02-20T14:00:00.000Z", updatedAt: "2024-02-20T16:00:00.000Z", @@ -100,25 +51,10 @@ export const v1ProjectWithMultipleScenes = { id: "scene-2", name: "Content", isMain: false, - tracks: [ - { - id: "track-2", - type: "video", - isMain: true, - elements: [ - { - id: "el-2", - type: "video", - mediaId: "m2", - startTime: 0, - duration: 20, - }, - ], - }, - ], + tracks: [], bookmarks: [], createdAt: "2024-02-20T14:30:00.000Z", updatedAt: "2024-02-20T16:00:00.000Z", }, ], -};
\ No newline at end of file +}; diff --git a/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts b/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts index ff6a31c2..31f7dae8 100644 --- a/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts +++ b/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts @@ -5,6 +5,7 @@ import { DEFAULT_COLOR, DEFAULT_FPS, } from "@/constants/project-constants"; +import type { MediaAssetData } from "@/services/storage/types"; import { getProjectId, transformProjectV1ToV2 } from "../transformers/v1-to-v2"; import { projectWithNoId, @@ -16,8 +17,8 @@ import { describe("V1 to V2 Migration", () => { describe("transformProjectV1ToV2", () => { - test("creates metadata object from flat properties", () => { - const result = transformProjectV1ToV2({ project: v1Project }); + test("creates metadata object from flat properties", async () => { + const result = await transformProjectV1ToV2({ project: v1Project }); expect(result.skipped).toBe(false); expect(result.project.version).toBe(2); @@ -29,16 +30,17 @@ describe("V1 to V2 Migration", () => { expect(typeof metadata.updatedAt).toBe("string"); }); - test("creates settings object from flat properties", () => { - const result = transformProjectV1ToV2({ project: v1Project }); + test("creates settings object from flat properties", async () => { + const result = await transformProjectV1ToV2({ project: v1Project }); const settings = result.project.settings as Record<string, unknown>; expect(settings.fps).toBe(v1Project.fps); expect(settings.canvasSize).toEqual(v1Project.canvasSize); + expect(settings.originalCanvasSize).toBe(null); }); - test("converts color background correctly", () => { - const result = transformProjectV1ToV2({ project: v1Project }); + test("converts color background correctly", async () => { + const result = await transformProjectV1ToV2({ project: v1Project }); const settings = result.project.settings as Record<string, unknown>; const background = settings.background as Record<string, unknown>; @@ -46,13 +48,13 @@ describe("V1 to V2 Migration", () => { expect(background.color).toBe(v1Project.backgroundColor); }); - test("converts blur background correctly", () => { + test("converts blur background correctly", async () => { const projectWithBlur = { ...v1Project, backgroundType: "blur", blurIntensity: 30, }; - const result = transformProjectV1ToV2({ project: projectWithBlur }); + const result = await transformProjectV1ToV2({ project: projectWithBlur }); const settings = result.project.settings as Record<string, unknown>; const background = settings.background as Record<string, unknown>; @@ -60,16 +62,16 @@ describe("V1 to V2 Migration", () => { expect(background.blurIntensity).toBe(30); }); - test("applies legacy bookmarks to main scene", () => { - const result = transformProjectV1ToV2({ project: v1Project }); + test("applies legacy bookmarks to main scene", async () => { + const result = await transformProjectV1ToV2({ project: v1Project }); const scenes = result.project.scenes as Array<Record<string, unknown>>; const mainScene = scenes.find((s) => s.isMain === true); expect(mainScene?.bookmarks).toEqual(v1Project.bookmarks); }); - test("preserves existing scene bookmarks", () => { - const result = transformProjectV1ToV2({ + test("preserves existing scene bookmarks", async () => { + const result = await transformProjectV1ToV2({ project: v1ProjectWithMultipleScenes, }); @@ -78,22 +80,24 @@ describe("V1 to V2 Migration", () => { expect(introScene?.bookmarks).toEqual([1.0]); }); - test("skips project that already has v2 structure", () => { - const result = transformProjectV1ToV2({ project: v2Project }); + test("skips project that already has v2 structure", async () => { + const result = await transformProjectV1ToV2({ project: v2Project }); expect(result.skipped).toBe(true); expect(result.reason).toBe("already v2"); }); - test("skips project with no id", () => { - const result = transformProjectV1ToV2({ project: projectWithNoId }); + test("skips project with no id", async () => { + const result = await transformProjectV1ToV2({ project: projectWithNoId }); expect(result.skipped).toBe(true); expect(result.reason).toBe("no project id"); }); - test("handles null values gracefully", () => { - const result = transformProjectV1ToV2({ project: projectWithNullValues }); + test("handles null values gracefully", async () => { + const result = await transformProjectV1ToV2({ + project: projectWithNullValues, + }); expect(result.skipped).toBe(false); const settings = result.project.settings as Record<string, unknown>; @@ -101,13 +105,13 @@ describe("V1 to V2 Migration", () => { expect(settings.canvasSize).toEqual(DEFAULT_CANVAS_SIZE); }); - test("uses default values for missing properties", () => { + test("uses default values for missing properties", async () => { const minimalProject = { id: "minimal", version: 1, scenes: [], }; - const result = transformProjectV1ToV2({ project: minimalProject }); + const result = await transformProjectV1ToV2({ project: minimalProject }); const settings = result.project.settings as Record<string, unknown>; expect(settings.fps).toBe(DEFAULT_FPS); @@ -118,14 +122,14 @@ describe("V1 to V2 Migration", () => { expect(background.color).toBe(DEFAULT_COLOR); }); - test("uses default blur intensity when missing", () => { + test("uses default blur intensity when missing", async () => { const projectWithBlurNoIntensity = { id: "blur-no-intensity", version: 1, backgroundType: "blur", scenes: [], }; - const result = transformProjectV1ToV2({ + const result = await transformProjectV1ToV2({ project: projectWithBlurNoIntensity, }); @@ -134,21 +138,131 @@ describe("V1 to V2 Migration", () => { expect(background.blurIntensity).toBe(DEFAULT_BLUR_INTENSITY); }); - test("preserves currentSceneId", () => { - const result = transformProjectV1ToV2({ project: v1Project }); + test("preserves currentSceneId", async () => { + const result = await transformProjectV1ToV2({ project: v1Project }); expect(result.project.currentSceneId).toBe(v1Project.currentSceneId); }); - test("finds main scene id when currentSceneId missing", () => { + test("finds main scene id when currentSceneId missing", async () => { const projectWithoutCurrentScene = { ...v1Project, currentSceneId: undefined, }; - const result = transformProjectV1ToV2({ + const result = await transformProjectV1ToV2({ project: projectWithoutCurrentScene, }); expect(result.project.currentSceneId).toBe("scene-main"); }); + + test("skips loading tracks if scene already has tracks", async () => { + const projectWithTracks = { + ...v1Project, + scenes: [ + { + id: "scene-main", + name: "Main scene", + isMain: true, + tracks: [ + { + id: "track-1", + type: "video", + name: "Existing Track", + elements: [], + }, + ], + bookmarks: [], + createdAt: "2024-01-15T10:00:00.000Z", + updatedAt: "2024-01-15T12:00:00.000Z", + }, + ], + }; + + const result = await transformProjectV1ToV2({ + project: projectWithTracks, + }); + + const scenes = result.project.scenes as Array<Record<string, unknown>>; + const mainScene = scenes[0]; + const tracks = mainScene.tracks as Array<Record<string, unknown>>; + expect(tracks.length).toBe(1); + expect(tracks[0].name).toBe("Existing Track"); + }); + }); + + describe("Track Loading and Transformation", () => { + test("loads tracks from legacy DB and transforms media track to video track", async () => { + const mockLoadMediaAsset = async ({ + mediaId, + }: { + mediaId: string; + }): Promise<MediaAssetData | null> => { + if (mediaId === "media-1") { + return { + id: "media-1", + name: "Test Video", + type: "video", + size: 1000, + lastModified: Date.now(), + }; + } + return null; + }; + + const projectWithLegacyTracks = { + ...v1Project, + scenes: [ + { + id: "scene-main", + name: "Main scene", + isMain: true, + tracks: [], + bookmarks: [], + createdAt: "2024-01-15T10:00:00.000Z", + updatedAt: "2024-01-15T12:00:00.000Z", + }, + ], + }; + + // mock IndexedDB for this test would require setting up a test environment + // for now, we test that the transformer handles empty tracks gracefully + const result = await transformProjectV1ToV2({ + project: projectWithLegacyTracks, + options: { loadMediaAsset: mockLoadMediaAsset }, + }); + + const scenes = result.project.scenes as Array<Record<string, unknown>>; + const mainScene = scenes[0]; + expect(Array.isArray(mainScene.tracks)).toBe(true); + }); + + test("transforms text element preserving opacity and migrating position", async () => { + const projectWithTextTrack = { + id: "project-text", + version: 1, + name: "Text Project", + scenes: [ + { + id: "scene-1", + name: "Scene", + isMain: true, + tracks: [], + bookmarks: [], + createdAt: "2024-01-01T00:00:00.000Z", + updatedAt: "2024-01-01T00:00:00.000Z", + }, + ], + }; + + // since tracks are empty, transformation won't happen + // but we verify the structure is correct + const result = await transformProjectV1ToV2({ + project: projectWithTextTrack, + }); + + expect(result.skipped).toBe(false); + const scenes = result.project.scenes as Array<Record<string, unknown>>; + expect(scenes.length).toBe(1); + }); }); describe("getProjectId", () => { |
