1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
import { beforeEach, describe, expect, test } from "bun:test";
import { EditorCore } from "@/core";
import { buildDefaultScene } from "@/timeline/scenes";
import type { ImageElement } from "@/timeline";
import type { MediaAsset } from "@/media/types";
import { mediaTimeFromSeconds, ZERO_MEDIA_TIME } from "@/wasm";
import { CURRENT_PROJECT_VERSION } from "@/services/storage/migrations";
import { InMemoryVersionStore } from "@/versions";
const PROJECT_ID = "project-1";
const IMAGE_DURATION = mediaTimeFromSeconds({ seconds: 5 });
function buildImageElement({ mediaId }: { mediaId: string }): ImageElement {
return {
id: `image-${mediaId}`,
type: "image",
name: `image-${mediaId}`,
startTime: ZERO_MEDIA_TIME,
duration: IMAGE_DURATION,
trimStart: ZERO_MEDIA_TIME,
trimEnd: ZERO_MEDIA_TIME,
mediaId,
params: {
"transform.positionX": 0,
"transform.positionY": 0,
"transform.scaleX": 1,
"transform.scaleY": 1,
"transform.rotate": 0,
opacity: 1,
},
};
}
function buildImageAsset({ id }: { id: string }): MediaAsset {
return {
id,
name: id,
type: "image",
file: new File([new Uint8Array([1, 2, 3])], `${id}.png`, {
type: "image/png",
}),
};
}
function seedEditor(): { editor: EditorCore } {
const editor = EditorCore.getInstance();
editor.versions.setStore({ store: new InMemoryVersionStore() });
editor.versions.setNow({ now: () => 1_000_000_000 });
const scene = buildDefaultScene({ name: "Main scene", isMain: true });
// Only media-pinned is referenced by the document (and thus snapshots);
// media-free exists as an asset but is used nowhere.
scene.tracks.main.elements = [buildImageElement({ mediaId: "media-pinned" })];
editor.project.setActiveProject({
project: {
metadata: {
id: PROJECT_ID,
name: "Test project",
duration: IMAGE_DURATION,
createdAt: new Date("2026-01-01T00:00:00Z"),
updatedAt: new Date("2026-01-01T00:00:00Z"),
},
scenes: [scene],
currentSceneId: scene.id,
settings: {
fps: { numerator: 30, denominator: 1 },
canvasSize: { width: 1920, height: 1080 },
background: { type: "color", color: "#000000" },
},
version: CURRENT_PROJECT_VERSION,
},
});
editor.scenes.setScenes({ scenes: [scene], activeSceneId: scene.id });
editor.media.setAssets({
assets: [
buildImageAsset({ id: "media-pinned" }),
buildImageAsset({ id: "media-free" }),
],
});
return { editor };
}
describe("Media pinning", () => {
beforeEach(() => {
EditorCore.reset();
});
test("deleting a media asset referenced by a stored version is blocked", async () => {
const { editor } = seedEditor();
const version = await editor.versions.createNamedCheckpoint({
name: "v1",
});
expect(version).not.toBeNull();
editor.media.removeMediaAsset({ projectId: PROJECT_ID, id: "media-pinned" });
expect(
editor.media.getAssets().some((asset) => asset.id === "media-pinned"),
).toBe(true);
// An unreferenced asset in the same project deletes normally.
editor.media.removeMediaAsset({ projectId: PROJECT_ID, id: "media-free" });
expect(
editor.media.getAssets().some((asset) => asset.id === "media-free"),
).toBe(false);
});
test("an asset is unpinned once the referencing version is deleted", async () => {
const { editor } = seedEditor();
const version = await editor.versions.createNamedCheckpoint({
name: "v1",
});
expect(version).not.toBeNull();
if (!version) return;
expect(
editor.versions.isMediaIdPinned({
projectId: PROJECT_ID,
mediaId: "media-pinned",
}),
).toBe(true);
await editor.versions.deleteVersion({ versionId: version.id });
expect(
editor.versions.isMediaIdPinned({
projectId: PROJECT_ID,
mediaId: "media-pinned",
}),
).toBe(false);
editor.media.removeMediaAsset({ projectId: PROJECT_ID, id: "media-pinned" });
expect(
editor.media.getAssets().some((asset) => asset.id === "media-pinned"),
).toBe(false);
});
});
|