summaryrefslogtreecommitdiff
path: root/apps/web/src/services/storage/migrations/__tests__
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/services/storage/migrations/__tests__')
-rw-r--r--apps/web/src/services/storage/migrations/__tests__/v21-to-v22.test.ts241
-rw-r--r--apps/web/src/services/storage/migrations/__tests__/v5-to-v6.test.ts186
2 files changed, 335 insertions, 92 deletions
diff --git a/apps/web/src/services/storage/migrations/__tests__/v21-to-v22.test.ts b/apps/web/src/services/storage/migrations/__tests__/v21-to-v22.test.ts
new file mode 100644
index 00000000..18b815b0
--- /dev/null
+++ b/apps/web/src/services/storage/migrations/__tests__/v21-to-v22.test.ts
@@ -0,0 +1,241 @@
+import { describe, expect, test } from "bun:test";
+import { transformProjectV21ToV22 } from "../transformers/v21-to-v22";
+
+describe("V21 to V22 Migration", () => {
+ test("migrates legacy animation channels to bindings and component channels", () => {
+ const result = transformProjectV21ToV22({
+ project: {
+ id: "project-v21-animations",
+ version: 21,
+ scenes: [
+ {
+ id: "scene-1",
+ tracks: [
+ {
+ id: "track-1",
+ elements: [
+ {
+ id: "element-1",
+ type: "text",
+ animations: {
+ channels: {
+ opacity: {
+ valueKind: "number",
+ keyframes: [
+ {
+ id: "opacity-1",
+ time: 1,
+ value: 0.5,
+ interpolation: "linear",
+ },
+ ],
+ },
+ "transform.position": {
+ valueKind: "vector",
+ keyframes: [
+ {
+ id: "position-1",
+ time: 2,
+ value: { x: 10, y: 20 },
+ interpolation: "hold",
+ },
+ ],
+ },
+ color: {
+ valueKind: "color",
+ keyframes: [
+ {
+ id: "color-1",
+ time: 3,
+ value: "#ff0000",
+ interpolation: "linear",
+ },
+ ],
+ },
+ "effects.effect-1.params.enabled": {
+ valueKind: "discrete",
+ keyframes: [
+ {
+ id: "enabled-1",
+ time: 4,
+ value: true,
+ interpolation: "hold",
+ },
+ ],
+ },
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ });
+
+ expect(result.skipped).toBe(false);
+ expect(result.project.version).toBe(22);
+
+ const scenes = result.project.scenes as Array<Record<string, unknown>>;
+ const tracks = scenes[0].tracks as Array<Record<string, unknown>>;
+ const elements = tracks[0].elements as Array<Record<string, unknown>>;
+ const animations = elements[0].animations as Record<string, unknown>;
+ const bindings = animations.bindings as Record<string, Record<string, unknown>>;
+ const channels = animations.channels as Record<string, Record<string, unknown>>;
+
+ expect(bindings.opacity).toEqual({
+ path: "opacity",
+ kind: "number",
+ components: [{ key: "value", channelId: "opacity:value" }],
+ });
+ expect(bindings["transform.position"]).toEqual({
+ path: "transform.position",
+ kind: "vector2",
+ components: [
+ { key: "x", channelId: "transform.position:x" },
+ { key: "y", channelId: "transform.position:y" },
+ ],
+ });
+ expect(bindings.color).toEqual({
+ path: "color",
+ kind: "color",
+ colorSpace: "srgb-linear",
+ components: [
+ { key: "r", channelId: "color:r" },
+ { key: "g", channelId: "color:g" },
+ { key: "b", channelId: "color:b" },
+ { key: "a", channelId: "color:a" },
+ ],
+ });
+ expect(bindings["effects.effect-1.params.enabled"]).toEqual({
+ path: "effects.effect-1.params.enabled",
+ kind: "discrete",
+ components: [
+ {
+ key: "value",
+ channelId: "effects.effect-1.params.enabled:value",
+ },
+ ],
+ });
+
+ expect(channels["opacity:value"]).toEqual({
+ kind: "scalar",
+ keys: [
+ {
+ id: "opacity-1",
+ time: 1,
+ value: 0.5,
+ segmentToNext: "linear",
+ tangentMode: "flat",
+ },
+ ],
+ });
+ expect(channels["transform.position:x"]).toEqual({
+ kind: "scalar",
+ keys: [
+ {
+ id: "position-1",
+ time: 2,
+ value: 10,
+ segmentToNext: "step",
+ tangentMode: "flat",
+ },
+ ],
+ });
+ expect(channels["transform.position:y"]).toEqual({
+ kind: "scalar",
+ keys: [
+ {
+ id: "position-1",
+ time: 2,
+ value: 20,
+ segmentToNext: "step",
+ tangentMode: "flat",
+ },
+ ],
+ });
+ expect(channels["color:r"]).toEqual({
+ kind: "scalar",
+ keys: [
+ {
+ id: "color-1",
+ time: 3,
+ value: 1,
+ segmentToNext: "linear",
+ tangentMode: "flat",
+ },
+ ],
+ });
+ expect(channels["color:g"]).toEqual({
+ kind: "scalar",
+ keys: [
+ {
+ id: "color-1",
+ time: 3,
+ value: 0,
+ segmentToNext: "linear",
+ tangentMode: "flat",
+ },
+ ],
+ });
+ expect(channels["color:b"]).toEqual({
+ kind: "scalar",
+ keys: [
+ {
+ id: "color-1",
+ time: 3,
+ value: 0,
+ segmentToNext: "linear",
+ tangentMode: "flat",
+ },
+ ],
+ });
+ expect(channels["color:a"]).toEqual({
+ kind: "scalar",
+ keys: [
+ {
+ id: "color-1",
+ time: 3,
+ value: 1,
+ segmentToNext: "linear",
+ tangentMode: "flat",
+ },
+ ],
+ });
+ expect(channels["effects.effect-1.params.enabled:value"]).toEqual({
+ kind: "discrete",
+ keys: [
+ {
+ id: "enabled-1",
+ time: 4,
+ value: true,
+ },
+ ],
+ });
+ });
+
+ test("skips projects already on v22", () => {
+ const result = transformProjectV21ToV22({
+ project: {
+ id: "project-v22",
+ version: 22,
+ },
+ });
+
+ expect(result.skipped).toBe(true);
+ expect(result.reason).toBe("already v22");
+ });
+
+ test("skips projects not on v21", () => {
+ const result = transformProjectV21ToV22({
+ project: {
+ id: "project-v20",
+ version: 20,
+ },
+ });
+
+ expect(result.skipped).toBe(true);
+ expect(result.reason).toBe("not v21");
+ });
+});
diff --git a/apps/web/src/services/storage/migrations/__tests__/v5-to-v6.test.ts b/apps/web/src/services/storage/migrations/__tests__/v5-to-v6.test.ts
index 66e87713..b05d1837 100644
--- a/apps/web/src/services/storage/migrations/__tests__/v5-to-v6.test.ts
+++ b/apps/web/src/services/storage/migrations/__tests__/v5-to-v6.test.ts
@@ -1,92 +1,94 @@
-import { describe, expect, test } from "bun:test";
-import { transformProjectV5ToV6 } from "../transformers/v5-to-v6";
-import { v5Project } from "./fixtures";
-
-describe("V5 to V6 Migration", () => {
- test("converts number bookmarks to Bookmark objects", async () => {
- const result = transformProjectV5ToV6({
- project: v5Project as Parameters<
- typeof transformProjectV5ToV6
- >[0]["project"],
- });
-
- expect(result.skipped).toBe(false);
- expect(result.project.version).toBe(6);
-
- const mainScene = (
- result.project.scenes as Array<{ bookmarks: unknown[] }>
- )[0];
- expect(mainScene.bookmarks).toEqual([
- { time: 2.0 },
- { time: 5.5 },
- { time: 12.0 },
- ]);
-
- const introScene = (
- result.project.scenes as Array<{ bookmarks: unknown[] }>
- )[1];
- expect(introScene.bookmarks).toEqual([]);
- });
-
- test("skips projects that are already v6", () => {
- const result = transformProjectV5ToV6({
- project: {
- ...v5Project,
- version: 6,
- scenes: [
- {
- ...(v5Project as { scenes: unknown[] }).scenes[0],
- bookmarks: [{ time: 2 }, { time: 5 }],
- },
- ],
- } as Parameters<typeof transformProjectV5ToV6>[0]["project"],
- });
-
- expect(result.skipped).toBe(true);
- expect(result.reason).toBe("already v6");
- });
-
- test("skips projects with no id", () => {
- const result = transformProjectV5ToV6({
- project: {
- version: 5,
- scenes: [],
- } as Parameters<typeof transformProjectV5ToV6>[0]["project"],
- });
-
- expect(result.skipped).toBe(true);
- expect(result.reason).toBe("no project id");
- });
-
- test("preserves existing Bookmark objects with note, color, duration", () => {
- const projectWithRichBookmarks = {
- ...v5Project,
- version: 5,
- scenes: [
- {
- ...(v5Project as { scenes: Array<Record<string, unknown>> })
- .scenes[0],
- bookmarks: [
- { time: 1, note: "Intro", color: "#ef4444" },
- { time: 5.5, duration: 2 },
- ],
- },
- ],
- };
-
- const result = transformProjectV5ToV6({
- project: projectWithRichBookmarks as Parameters<
- typeof transformProjectV5ToV6
- >[0]["project"],
- });
-
- expect(result.skipped).toBe(false);
- const mainScene = (
- result.project.scenes as Array<{ bookmarks: unknown[] }>
- )[0];
- expect(mainScene.bookmarks).toEqual([
- { time: 1, note: "Intro", color: "#ef4444" },
- { time: 5.5, duration: 2 },
- ]);
- });
-});
+import { describe, expect, test } from "bun:test";
+import { transformProjectV5ToV6 } from "../transformers/v5-to-v6";
+import { v5Project } from "./fixtures";
+
+describe("V5 to V6 Migration", () => {
+ test("converts number bookmarks to Bookmark objects", async () => {
+ const result = transformProjectV5ToV6({
+ project: v5Project as Parameters<
+ typeof transformProjectV5ToV6
+ >[0]["project"],
+ });
+
+ expect(result.skipped).toBe(false);
+ expect(result.project.version).toBe(6);
+
+ const mainScene = (
+ result.project.scenes as Array<{ bookmarks: unknown[] }>
+ )[0];
+ expect(mainScene.bookmarks).toEqual([
+ { time: 2.0 },
+ { time: 5.5 },
+ { time: 12.0 },
+ ]);
+
+ const introScene = (
+ result.project.scenes as Array<{ bookmarks: unknown[] }>
+ )[1];
+ expect(introScene.bookmarks).toEqual([]);
+ });
+
+ test("skips projects that are already v6", () => {
+ const firstScene = (v5Project as { scenes: Array<Record<string, unknown>> })
+ .scenes[0];
+ const result = transformProjectV5ToV6({
+ project: {
+ ...v5Project,
+ version: 6,
+ scenes: [
+ {
+ ...firstScene,
+ bookmarks: [{ time: 2 }, { time: 5 }],
+ },
+ ],
+ } as Parameters<typeof transformProjectV5ToV6>[0]["project"],
+ });
+
+ expect(result.skipped).toBe(true);
+ expect(result.reason).toBe("already v6");
+ });
+
+ test("skips projects with no id", () => {
+ const result = transformProjectV5ToV6({
+ project: {
+ version: 5,
+ scenes: [],
+ } as Parameters<typeof transformProjectV5ToV6>[0]["project"],
+ });
+
+ expect(result.skipped).toBe(true);
+ expect(result.reason).toBe("no project id");
+ });
+
+ test("preserves existing Bookmark objects with note, color, duration", () => {
+ const projectWithRichBookmarks = {
+ ...v5Project,
+ version: 5,
+ scenes: [
+ {
+ ...(v5Project as { scenes: Array<Record<string, unknown>> })
+ .scenes[0],
+ bookmarks: [
+ { time: 1, note: "Intro", color: "#ef4444" },
+ { time: 5.5, duration: 2 },
+ ],
+ },
+ ],
+ };
+
+ const result = transformProjectV5ToV6({
+ project: projectWithRichBookmarks as Parameters<
+ typeof transformProjectV5ToV6
+ >[0]["project"],
+ });
+
+ expect(result.skipped).toBe(false);
+ const mainScene = (
+ result.project.scenes as Array<{ bookmarks: unknown[] }>
+ )[0];
+ expect(mainScene.bookmarks).toEqual([
+ { time: 1, note: "Intro", color: "#ef4444" },
+ { time: 5.5, duration: 2 },
+ ]);
+ });
+});