@editframe/elements 0.26.2-beta.0 → 0.26.4-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/elements/EFTimegroup.js +7 -2
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/package.json +2 -2
- package/scripts/build-css.js +3 -3
- package/tsdown.config.ts +1 -1
- package/types.json +1 -1
- package/src/elements/ContextProxiesController.ts +0 -124
- package/src/elements/CrossUpdateController.ts +0 -22
- package/src/elements/EFAudio.browsertest.ts +0 -706
- package/src/elements/EFAudio.ts +0 -56
- package/src/elements/EFCaptions.browsertest.ts +0 -1960
- package/src/elements/EFCaptions.ts +0 -823
- package/src/elements/EFImage.browsertest.ts +0 -120
- package/src/elements/EFImage.ts +0 -113
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
- package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
- package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
- package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
- package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
- package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
- package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
- package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
- package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
- package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
- package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
- package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
- package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
- package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
- package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
- package/src/elements/EFMedia.browsertest.ts +0 -872
- package/src/elements/EFMedia.ts +0 -341
- package/src/elements/EFSourceMixin.ts +0 -60
- package/src/elements/EFSurface.browsertest.ts +0 -151
- package/src/elements/EFSurface.ts +0 -142
- package/src/elements/EFTemporal.browsertest.ts +0 -215
- package/src/elements/EFTemporal.ts +0 -800
- package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
- package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
- package/src/elements/EFThumbnailStrip.ts +0 -906
- package/src/elements/EFTimegroup.browsertest.ts +0 -870
- package/src/elements/EFTimegroup.ts +0 -878
- package/src/elements/EFVideo.browsertest.ts +0 -1482
- package/src/elements/EFVideo.ts +0 -564
- package/src/elements/EFWaveform.ts +0 -547
- package/src/elements/FetchContext.browsertest.ts +0 -401
- package/src/elements/FetchMixin.ts +0 -38
- package/src/elements/SampleBuffer.ts +0 -94
- package/src/elements/TargetController.browsertest.ts +0 -230
- package/src/elements/TargetController.ts +0 -224
- package/src/elements/TimegroupController.ts +0 -26
- package/src/elements/durationConverter.ts +0 -35
- package/src/elements/parseTimeToMs.ts +0 -9
- package/src/elements/printTaskStatus.ts +0 -16
- package/src/elements/renderTemporalAudio.ts +0 -108
- package/src/elements/updateAnimations.browsertest.ts +0 -1884
- package/src/elements/updateAnimations.ts +0 -217
- package/src/elements/util.ts +0 -24
- package/src/gui/ContextMixin.browsertest.ts +0 -860
- package/src/gui/ContextMixin.ts +0 -562
- package/src/gui/Controllable.browsertest.ts +0 -258
- package/src/gui/Controllable.ts +0 -41
- package/src/gui/EFConfiguration.ts +0 -40
- package/src/gui/EFControls.browsertest.ts +0 -389
- package/src/gui/EFControls.ts +0 -195
- package/src/gui/EFDial.browsertest.ts +0 -84
- package/src/gui/EFDial.ts +0 -172
- package/src/gui/EFFilmstrip.browsertest.ts +0 -712
- package/src/gui/EFFilmstrip.ts +0 -1349
- package/src/gui/EFFitScale.ts +0 -152
- package/src/gui/EFFocusOverlay.ts +0 -79
- package/src/gui/EFPause.browsertest.ts +0 -202
- package/src/gui/EFPause.ts +0 -73
- package/src/gui/EFPlay.browsertest.ts +0 -202
- package/src/gui/EFPlay.ts +0 -73
- package/src/gui/EFPreview.ts +0 -74
- package/src/gui/EFResizableBox.browsertest.ts +0 -79
- package/src/gui/EFResizableBox.ts +0 -898
- package/src/gui/EFScrubber.ts +0 -151
- package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
- package/src/gui/EFTimeDisplay.ts +0 -55
- package/src/gui/EFToggleLoop.ts +0 -35
- package/src/gui/EFTogglePlay.ts +0 -70
- package/src/gui/EFWorkbench.ts +0 -115
- package/src/gui/PlaybackController.ts +0 -527
- package/src/gui/TWMixin.css +0 -6
- package/src/gui/TWMixin.ts +0 -61
- package/src/gui/TargetOrContextMixin.ts +0 -185
- package/src/gui/currentTimeContext.ts +0 -5
- package/src/gui/durationContext.ts +0 -3
- package/src/gui/efContext.ts +0 -6
- package/src/gui/fetchContext.ts +0 -5
- package/src/gui/focusContext.ts +0 -7
- package/src/gui/focusedElementContext.ts +0 -5
- package/src/gui/playingContext.ts +0 -5
- package/src/otel/BridgeSpanExporter.ts +0 -150
- package/src/otel/setupBrowserTracing.ts +0 -73
- package/src/otel/tracingHelpers.ts +0 -251
- package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
- package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
- package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
- package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
- package/src/transcoding/types/index.ts +0 -312
- package/src/transcoding/utils/MediaUtils.ts +0 -63
- package/src/transcoding/utils/UrlGenerator.ts +0 -68
- package/src/transcoding/utils/constants.ts +0 -36
- package/src/utils/LRUCache.test.ts +0 -274
- package/src/utils/LRUCache.ts +0 -696
|
@@ -1,706 +0,0 @@
|
|
|
1
|
-
import { html, render } from "lit";
|
|
2
|
-
import { HttpResponse, http } from "msw";
|
|
3
|
-
import { afterEach, beforeEach, describe } from "vitest";
|
|
4
|
-
import { assetMSWHandlers } from "../../test/useAssetMSW.js";
|
|
5
|
-
import { test as baseTest } from "../../test/useMSW.js";
|
|
6
|
-
import type { EFAudio } from "./EFAudio.js";
|
|
7
|
-
import "./EFAudio.js";
|
|
8
|
-
import "../gui/EFWorkbench.js";
|
|
9
|
-
import "../gui/EFPreview.js";
|
|
10
|
-
import "./EFTimegroup.js";
|
|
11
|
-
|
|
12
|
-
const test = baseTest.extend({
|
|
13
|
-
setupAssetHandlers: [
|
|
14
|
-
async ({ worker }, use) => {
|
|
15
|
-
// Set up centralized MSW handlers to proxy requests to test assets
|
|
16
|
-
worker.use(...assetMSWHandlers);
|
|
17
|
-
await use(undefined);
|
|
18
|
-
},
|
|
19
|
-
{ auto: true },
|
|
20
|
-
],
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe("EFAudio", () => {
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
// Clean up DOM and localStorage
|
|
26
|
-
while (document.body.children.length) {
|
|
27
|
-
document.body.children[0]?.remove();
|
|
28
|
-
}
|
|
29
|
-
localStorage.clear();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
afterEach(() => {
|
|
33
|
-
// Clean up any remaining elements
|
|
34
|
-
const audios = document.querySelectorAll("ef-audio");
|
|
35
|
-
for (const audio of audios) {
|
|
36
|
-
audio.remove();
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe("basic rendering", () => {
|
|
41
|
-
test("should be defined and render audio element", async ({ expect }) => {
|
|
42
|
-
const container = document.createElement("div");
|
|
43
|
-
render(
|
|
44
|
-
html`
|
|
45
|
-
<ef-workbench>
|
|
46
|
-
<ef-preview>
|
|
47
|
-
<ef-audio></ef-audio>
|
|
48
|
-
</ef-preview>
|
|
49
|
-
</ef-workbench>
|
|
50
|
-
`,
|
|
51
|
-
container,
|
|
52
|
-
);
|
|
53
|
-
document.body.appendChild(container);
|
|
54
|
-
|
|
55
|
-
const element = container.querySelector("ef-audio") as EFAudio;
|
|
56
|
-
// Wait for element to render
|
|
57
|
-
await element.updateComplete;
|
|
58
|
-
|
|
59
|
-
expect(element.tagName).toBe("EF-AUDIO");
|
|
60
|
-
|
|
61
|
-
// Check for rendered audio element
|
|
62
|
-
const renderedAudio = element.shadowRoot?.querySelector("audio");
|
|
63
|
-
expect(renderedAudio).toBeDefined();
|
|
64
|
-
expect(renderedAudio?.tagName).toBe("AUDIO");
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("audio element has correct default properties", async ({ expect }) => {
|
|
68
|
-
const container = document.createElement("div");
|
|
69
|
-
render(
|
|
70
|
-
html`
|
|
71
|
-
<ef-workbench>
|
|
72
|
-
<ef-preview>
|
|
73
|
-
<ef-audio></ef-audio>
|
|
74
|
-
</ef-preview>
|
|
75
|
-
</ef-workbench>
|
|
76
|
-
`,
|
|
77
|
-
container,
|
|
78
|
-
);
|
|
79
|
-
document.body.appendChild(container);
|
|
80
|
-
|
|
81
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
82
|
-
|
|
83
|
-
// Wait for element to render
|
|
84
|
-
await audio.updateComplete;
|
|
85
|
-
|
|
86
|
-
const audioElement = audio.shadowRoot?.querySelector(
|
|
87
|
-
"audio",
|
|
88
|
-
) as HTMLAudioElement;
|
|
89
|
-
|
|
90
|
-
expect(audioElement).toBeDefined();
|
|
91
|
-
expect(audioElement?.controls).toBe(false); // Should not have controls by default
|
|
92
|
-
expect(audioElement?.preload).toBe("metadata");
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test("inherits media properties from EFMedia", async ({ expect }) => {
|
|
96
|
-
const container = document.createElement("div");
|
|
97
|
-
render(
|
|
98
|
-
html`
|
|
99
|
-
<ef-workbench>
|
|
100
|
-
<ef-preview>
|
|
101
|
-
<ef-audio src="/test-audio.mp3"></ef-audio>
|
|
102
|
-
</ef-preview>
|
|
103
|
-
</ef-workbench>
|
|
104
|
-
`,
|
|
105
|
-
container,
|
|
106
|
-
);
|
|
107
|
-
document.body.appendChild(container);
|
|
108
|
-
|
|
109
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
110
|
-
|
|
111
|
-
// Wait for element to render
|
|
112
|
-
await audio.updateComplete;
|
|
113
|
-
|
|
114
|
-
// Should inherit properties from EFMedia base class
|
|
115
|
-
expect(audio.src).toBe("/test-audio.mp3");
|
|
116
|
-
expect(audio.currentTimeMs).toBeDefined();
|
|
117
|
-
expect(audio.durationMs).toBeDefined();
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe("audio asset integration", () => {
|
|
122
|
-
test("integrates with audio asset loading", async ({ expect }) => {
|
|
123
|
-
const container = document.createElement("div");
|
|
124
|
-
render(
|
|
125
|
-
html`
|
|
126
|
-
<ef-preview>
|
|
127
|
-
<ef-audio src="media/bars-n-tone2.mp4" mode="asset"></ef-audio>
|
|
128
|
-
</ef-preview>
|
|
129
|
-
`,
|
|
130
|
-
container,
|
|
131
|
-
);
|
|
132
|
-
document.body.appendChild(container);
|
|
133
|
-
|
|
134
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
135
|
-
await audio.updateComplete;
|
|
136
|
-
|
|
137
|
-
// Add timeout to prevent hanging on fragment index loading
|
|
138
|
-
try {
|
|
139
|
-
await Promise.race([
|
|
140
|
-
audio.fragmentIndexTask.taskComplete,
|
|
141
|
-
new Promise((_, reject) =>
|
|
142
|
-
setTimeout(
|
|
143
|
-
() => reject(new Error("Fragment index loading timed out")),
|
|
144
|
-
3000,
|
|
145
|
-
),
|
|
146
|
-
),
|
|
147
|
-
]);
|
|
148
|
-
} catch (error) {
|
|
149
|
-
// If fragment index fails to load, skip intrinsic duration test
|
|
150
|
-
console.warn(
|
|
151
|
-
"Fragment index loading failed, skipping duration test:",
|
|
152
|
-
error,
|
|
153
|
-
);
|
|
154
|
-
expect(audio.src).toBe("media/bars-n-tone2.mp4");
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
expect(audio.src).toBe("media/bars-n-tone2.mp4");
|
|
159
|
-
|
|
160
|
-
// The audio should have loaded successfully and have a duration > 0
|
|
161
|
-
// We don't test for specific duration since real assets may vary
|
|
162
|
-
expect(audio.intrinsicDurationMs).toBeGreaterThan(0);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test("handles missing audio asset gracefully", async ({ expect }) => {
|
|
166
|
-
const container = document.createElement("div");
|
|
167
|
-
render(
|
|
168
|
-
html`
|
|
169
|
-
<ef-preview>
|
|
170
|
-
<ef-audio src="/nonexistent.mp3"></ef-audio>
|
|
171
|
-
</ef-preview>
|
|
172
|
-
`,
|
|
173
|
-
container,
|
|
174
|
-
);
|
|
175
|
-
document.body.appendChild(container);
|
|
176
|
-
|
|
177
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
178
|
-
|
|
179
|
-
// Should not throw when audio asset is missing
|
|
180
|
-
expect(() => {
|
|
181
|
-
audio.frameTask.run();
|
|
182
|
-
}).not.toThrow();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
test("handles audio loading errors gracefully", async ({
|
|
186
|
-
worker,
|
|
187
|
-
expect,
|
|
188
|
-
}) => {
|
|
189
|
-
// Mock 404 response for audio asset
|
|
190
|
-
worker.use(
|
|
191
|
-
http.get("/@ef-track-fragment-index//error-audio.mp3", () => {
|
|
192
|
-
return new HttpResponse(null, { status: 404 });
|
|
193
|
-
}),
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
const container = document.createElement("div");
|
|
197
|
-
render(
|
|
198
|
-
html`
|
|
199
|
-
<ef-preview>
|
|
200
|
-
<ef-audio src="/error-audio.mp3"></ef-audio>
|
|
201
|
-
</ef-preview>
|
|
202
|
-
`,
|
|
203
|
-
container,
|
|
204
|
-
);
|
|
205
|
-
document.body.appendChild(container);
|
|
206
|
-
|
|
207
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
208
|
-
|
|
209
|
-
// Should handle loading errors gracefully
|
|
210
|
-
expect(() => {
|
|
211
|
-
audio.frameTask.run();
|
|
212
|
-
}).not.toThrow();
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
describe("frame task integration", () => {
|
|
217
|
-
test("frameTask coordinates all required media tasks", async ({
|
|
218
|
-
expect,
|
|
219
|
-
}) => {
|
|
220
|
-
const container = document.createElement("div");
|
|
221
|
-
render(
|
|
222
|
-
html`
|
|
223
|
-
<ef-preview>
|
|
224
|
-
<ef-audio src="/test-audio.mp3"></ef-audio>
|
|
225
|
-
</ef-preview>
|
|
226
|
-
`,
|
|
227
|
-
container,
|
|
228
|
-
);
|
|
229
|
-
document.body.appendChild(container);
|
|
230
|
-
|
|
231
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
232
|
-
|
|
233
|
-
// frameTask should complete without errors
|
|
234
|
-
expect(() => {
|
|
235
|
-
audio.frameTask.run();
|
|
236
|
-
}).not.toThrow();
|
|
237
|
-
|
|
238
|
-
// Should coordinate the expected tasks
|
|
239
|
-
expect(audio.fragmentIndexTask).toBeDefined();
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test("frameTask handles missing dependencies", ({ expect }) => {
|
|
243
|
-
const container = document.createElement("div");
|
|
244
|
-
render(
|
|
245
|
-
html`
|
|
246
|
-
<ef-workbench>
|
|
247
|
-
<ef-preview>
|
|
248
|
-
<ef-audio></ef-audio>
|
|
249
|
-
</ef-preview>
|
|
250
|
-
</ef-workbench>
|
|
251
|
-
`,
|
|
252
|
-
container,
|
|
253
|
-
);
|
|
254
|
-
document.body.appendChild(container);
|
|
255
|
-
|
|
256
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
257
|
-
|
|
258
|
-
// Should handle missing dependencies gracefully
|
|
259
|
-
expect(() => {
|
|
260
|
-
audio.frameTask.run();
|
|
261
|
-
}).not.toThrow();
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
test("frameTask completion triggers timegroup updates", async ({
|
|
265
|
-
expect,
|
|
266
|
-
}) => {
|
|
267
|
-
const container = document.createElement("div");
|
|
268
|
-
render(
|
|
269
|
-
html`
|
|
270
|
-
<ef-preview>
|
|
271
|
-
<ef-timegroup mode="sequence">
|
|
272
|
-
<ef-audio src="media/bars-n-tone2.mp4" mode="asset"></ef-audio>
|
|
273
|
-
</ef-timegroup>
|
|
274
|
-
</ef-preview>
|
|
275
|
-
`,
|
|
276
|
-
container,
|
|
277
|
-
);
|
|
278
|
-
document.body.appendChild(container);
|
|
279
|
-
|
|
280
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
281
|
-
const timegroup = container.querySelector("ef-timegroup");
|
|
282
|
-
await audio.updateComplete;
|
|
283
|
-
|
|
284
|
-
// Wait for fragment index to load
|
|
285
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
286
|
-
|
|
287
|
-
// frameTask should trigger timegroup updates
|
|
288
|
-
await audio.frameTask.run();
|
|
289
|
-
|
|
290
|
-
expect(timegroup).toBeDefined();
|
|
291
|
-
// The frameTask should request updates on the root timegroup
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
describe("audio element behavior", () => {
|
|
296
|
-
test("audio element can be controlled programmatically", async ({
|
|
297
|
-
expect,
|
|
298
|
-
}) => {
|
|
299
|
-
const container = document.createElement("div");
|
|
300
|
-
render(
|
|
301
|
-
html`
|
|
302
|
-
<ef-workbench>
|
|
303
|
-
<ef-preview>
|
|
304
|
-
<ef-audio></ef-audio>
|
|
305
|
-
</ef-preview>
|
|
306
|
-
</ef-workbench>
|
|
307
|
-
`,
|
|
308
|
-
container,
|
|
309
|
-
);
|
|
310
|
-
document.body.appendChild(container);
|
|
311
|
-
|
|
312
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
313
|
-
await audio.updateComplete;
|
|
314
|
-
|
|
315
|
-
const audioElement = audio.shadowRoot?.querySelector(
|
|
316
|
-
"audio",
|
|
317
|
-
) as HTMLAudioElement;
|
|
318
|
-
|
|
319
|
-
// Should be able to control audio element properties
|
|
320
|
-
expect(() => {
|
|
321
|
-
audioElement.volume = 0.5;
|
|
322
|
-
audioElement.muted = true;
|
|
323
|
-
audioElement.loop = false;
|
|
324
|
-
}).not.toThrow();
|
|
325
|
-
|
|
326
|
-
expect(audioElement.volume).toBe(0.5);
|
|
327
|
-
expect(audioElement.muted).toBe(true);
|
|
328
|
-
expect(audioElement.loop).toBe(false);
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test("audio element handles invalid src gracefully", async ({ expect }) => {
|
|
332
|
-
const container = document.createElement("div");
|
|
333
|
-
render(
|
|
334
|
-
html`
|
|
335
|
-
<ef-workbench>
|
|
336
|
-
<ef-preview>
|
|
337
|
-
<ef-audio></ef-audio>
|
|
338
|
-
</ef-preview>
|
|
339
|
-
</ef-workbench>
|
|
340
|
-
`,
|
|
341
|
-
container,
|
|
342
|
-
);
|
|
343
|
-
document.body.appendChild(container);
|
|
344
|
-
|
|
345
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
346
|
-
await audio.updateComplete;
|
|
347
|
-
|
|
348
|
-
const audioElement = audio.shadowRoot?.querySelector(
|
|
349
|
-
"audio",
|
|
350
|
-
) as HTMLAudioElement;
|
|
351
|
-
|
|
352
|
-
// Should handle invalid src without throwing
|
|
353
|
-
expect(() => {
|
|
354
|
-
audioElement.src = "invalid://url";
|
|
355
|
-
}).not.toThrow();
|
|
356
|
-
|
|
357
|
-
expect(() => {
|
|
358
|
-
audioElement.src = "";
|
|
359
|
-
}).not.toThrow();
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
test("audio element events can be handled", async ({ expect }) => {
|
|
363
|
-
const container = document.createElement("div");
|
|
364
|
-
render(
|
|
365
|
-
html`
|
|
366
|
-
<ef-workbench>
|
|
367
|
-
<ef-preview>
|
|
368
|
-
<ef-audio></ef-audio>
|
|
369
|
-
</ef-preview>
|
|
370
|
-
</ef-workbench>
|
|
371
|
-
`,
|
|
372
|
-
container,
|
|
373
|
-
);
|
|
374
|
-
document.body.appendChild(container);
|
|
375
|
-
|
|
376
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
377
|
-
await audio.updateComplete;
|
|
378
|
-
|
|
379
|
-
const audioElement = audio.shadowRoot?.querySelector(
|
|
380
|
-
"audio",
|
|
381
|
-
) as HTMLAudioElement;
|
|
382
|
-
|
|
383
|
-
let eventFired = false;
|
|
384
|
-
|
|
385
|
-
// Should be able to add event listeners
|
|
386
|
-
expect(() => {
|
|
387
|
-
audioElement.addEventListener("loadstart", () => {
|
|
388
|
-
eventFired = true;
|
|
389
|
-
});
|
|
390
|
-
}).not.toThrow();
|
|
391
|
-
|
|
392
|
-
// Trigger a loadstart event
|
|
393
|
-
audioElement.dispatchEvent(new Event("loadstart"));
|
|
394
|
-
expect(eventFired).toBe(true);
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
describe("error handling and edge cases", () => {
|
|
399
|
-
test("handles audio element removal during playback", ({ expect }) => {
|
|
400
|
-
const container = document.createElement("div");
|
|
401
|
-
render(
|
|
402
|
-
html`
|
|
403
|
-
<ef-workbench>
|
|
404
|
-
<ef-preview>
|
|
405
|
-
<ef-audio></ef-audio>
|
|
406
|
-
</ef-preview>
|
|
407
|
-
</ef-workbench>
|
|
408
|
-
`,
|
|
409
|
-
container,
|
|
410
|
-
);
|
|
411
|
-
document.body.appendChild(container);
|
|
412
|
-
|
|
413
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
414
|
-
|
|
415
|
-
// Start some operations
|
|
416
|
-
audio.frameTask.run();
|
|
417
|
-
|
|
418
|
-
// Remove element
|
|
419
|
-
audio.remove();
|
|
420
|
-
|
|
421
|
-
// Should not cause errors
|
|
422
|
-
expect(() => {
|
|
423
|
-
audio.frameTask.run();
|
|
424
|
-
}).not.toThrow();
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
test("handles seek operations on audio", ({ expect }) => {
|
|
428
|
-
const container = document.createElement("div");
|
|
429
|
-
render(
|
|
430
|
-
html`
|
|
431
|
-
<ef-workbench>
|
|
432
|
-
<ef-preview>
|
|
433
|
-
<ef-audio></ef-audio>
|
|
434
|
-
</ef-preview>
|
|
435
|
-
</ef-workbench>
|
|
436
|
-
`,
|
|
437
|
-
container,
|
|
438
|
-
);
|
|
439
|
-
document.body.appendChild(container);
|
|
440
|
-
|
|
441
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
442
|
-
|
|
443
|
-
// Should handle seek operations gracefully
|
|
444
|
-
expect(() => {
|
|
445
|
-
audio.desiredSeekTimeMs = 1000; // Seek to 1 second
|
|
446
|
-
audio.frameTask.run();
|
|
447
|
-
}).not.toThrow();
|
|
448
|
-
|
|
449
|
-
expect(() => {
|
|
450
|
-
audio.desiredSeekTimeMs = -500; // Invalid negative time
|
|
451
|
-
audio.frameTask.run();
|
|
452
|
-
}).not.toThrow();
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
test("handles audio without src gracefully", async ({ expect }) => {
|
|
456
|
-
const container = document.createElement("div");
|
|
457
|
-
render(
|
|
458
|
-
html`
|
|
459
|
-
<ef-workbench>
|
|
460
|
-
<ef-preview>
|
|
461
|
-
<ef-audio></ef-audio>
|
|
462
|
-
</ef-preview>
|
|
463
|
-
</ef-workbench>
|
|
464
|
-
`,
|
|
465
|
-
container,
|
|
466
|
-
);
|
|
467
|
-
document.body.appendChild(container);
|
|
468
|
-
|
|
469
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
470
|
-
await audio.updateComplete;
|
|
471
|
-
|
|
472
|
-
// Should handle missing src gracefully - src defaults to empty string, not null
|
|
473
|
-
expect(audio.src).toBe("");
|
|
474
|
-
|
|
475
|
-
expect(() => {
|
|
476
|
-
audio.frameTask.run();
|
|
477
|
-
}).not.toThrow();
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
test("handles simultaneous frame task executions", async ({ expect }) => {
|
|
481
|
-
const container = document.createElement("div");
|
|
482
|
-
render(
|
|
483
|
-
html`
|
|
484
|
-
<ef-workbench>
|
|
485
|
-
<ef-preview>
|
|
486
|
-
<ef-audio></ef-audio>
|
|
487
|
-
</ef-preview>
|
|
488
|
-
</ef-workbench>
|
|
489
|
-
`,
|
|
490
|
-
container,
|
|
491
|
-
);
|
|
492
|
-
document.body.appendChild(container);
|
|
493
|
-
|
|
494
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
495
|
-
|
|
496
|
-
// Should handle multiple simultaneous executions
|
|
497
|
-
const task1 = audio.frameTask.run();
|
|
498
|
-
const task2 = audio.frameTask.run();
|
|
499
|
-
const task3 = audio.frameTask.run();
|
|
500
|
-
|
|
501
|
-
// All should complete without throwing
|
|
502
|
-
await expect(
|
|
503
|
-
Promise.allSettled([task1, task2, task3]),
|
|
504
|
-
).resolves.toBeDefined();
|
|
505
|
-
});
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
describe("assetId property", () => {
|
|
509
|
-
test("reads from dom attribute", async ({ expect }) => {
|
|
510
|
-
const container = document.createElement("div");
|
|
511
|
-
const audio = document.createElement("ef-audio") as EFAudio;
|
|
512
|
-
container.appendChild(audio);
|
|
513
|
-
document.body.appendChild(container);
|
|
514
|
-
|
|
515
|
-
await audio.updateComplete;
|
|
516
|
-
|
|
517
|
-
// Set attribute after element is fully initialized
|
|
518
|
-
audio.setAttribute("asset-id", "test-audio-asset-456");
|
|
519
|
-
await audio.updateComplete;
|
|
520
|
-
|
|
521
|
-
expect(audio).toBeDefined();
|
|
522
|
-
expect(audio.getAttribute("asset-id")).toBe("test-audio-asset-456");
|
|
523
|
-
expect(audio.assetId).toBe("test-audio-asset-456"); // This is the critical test!
|
|
524
|
-
|
|
525
|
-
container.remove();
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
test("reads assetId from dynamically generated HTML", async ({
|
|
529
|
-
expect,
|
|
530
|
-
}) => {
|
|
531
|
-
// Simulate the exact production scenario
|
|
532
|
-
const cardJoker = { id: "test-card-789" };
|
|
533
|
-
|
|
534
|
-
const container = document.createElement("div");
|
|
535
|
-
container.innerHTML = `
|
|
536
|
-
<ef-timegroup class="w-[480px] h-[270px] relative" mode="fixed" duration="2s">
|
|
537
|
-
<ef-audio asset-id="${cardJoker.id}" id="test-audio"></ef-audio>
|
|
538
|
-
</ef-timegroup>
|
|
539
|
-
`;
|
|
540
|
-
document.body.appendChild(container);
|
|
541
|
-
|
|
542
|
-
const audio = container.querySelector("#test-audio") as EFAudio;
|
|
543
|
-
await audio.updateComplete;
|
|
544
|
-
|
|
545
|
-
expect(audio).toBeDefined();
|
|
546
|
-
expect(audio.getAttribute("asset-id")).toBe("test-card-789");
|
|
547
|
-
expect(audio.assetId).toBe("test-card-789");
|
|
548
|
-
|
|
549
|
-
container.remove();
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
test("works with apiHost and complex DOM structure", async ({ expect }) => {
|
|
553
|
-
// Test complex DOM structure with apiHost to prevent regression
|
|
554
|
-
const testAsset = { id: "production-card-123" };
|
|
555
|
-
|
|
556
|
-
const container = document.createElement("div");
|
|
557
|
-
container.innerHTML = `
|
|
558
|
-
<ef-timegroup class="w-[480px] h-[270px] relative" mode="fixed" duration="2s">
|
|
559
|
-
<ef-audio asset-id="${testAsset.id}" id="test-audio"></ef-audio>
|
|
560
|
-
</ef-timegroup>
|
|
561
|
-
`;
|
|
562
|
-
document.body.appendChild(container);
|
|
563
|
-
|
|
564
|
-
const audio = container.querySelector("#test-audio") as EFAudio;
|
|
565
|
-
await audio.updateComplete;
|
|
566
|
-
|
|
567
|
-
// Critical: assetId must be immediately available - this was the original failing issue
|
|
568
|
-
expect(audio.assetId).toBe("production-card-123");
|
|
569
|
-
expect(audio.getAttribute("asset-id")).toBe("production-card-123");
|
|
570
|
-
|
|
571
|
-
container.remove();
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
test("reads from js property", ({ expect }) => {
|
|
575
|
-
const container = document.createElement("div");
|
|
576
|
-
render(html`<ef-audio></ef-audio>`, container);
|
|
577
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
578
|
-
|
|
579
|
-
audio.assetId = "test-audio-789";
|
|
580
|
-
expect(audio.assetId).toBe("test-audio-789");
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
test("reflects property changes to attribute", async ({ expect }) => {
|
|
584
|
-
const container = document.createElement("div");
|
|
585
|
-
render(html`<ef-audio></ef-audio>`, container);
|
|
586
|
-
document.body.appendChild(container);
|
|
587
|
-
|
|
588
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
589
|
-
await audio.updateComplete;
|
|
590
|
-
|
|
591
|
-
audio.assetId = "test-audio-012";
|
|
592
|
-
await audio.updateComplete;
|
|
593
|
-
expect(audio.getAttribute("asset-id")).toBe("test-audio-012");
|
|
594
|
-
|
|
595
|
-
audio.assetId = null;
|
|
596
|
-
await audio.updateComplete;
|
|
597
|
-
expect(audio.hasAttribute("asset-id")).toBe(false);
|
|
598
|
-
|
|
599
|
-
container.remove();
|
|
600
|
-
});
|
|
601
|
-
});
|
|
602
|
-
|
|
603
|
-
describe.skip("integration with timegroups", () => {
|
|
604
|
-
test("integrates correctly within timegroup structure", async ({
|
|
605
|
-
expect,
|
|
606
|
-
}) => {
|
|
607
|
-
const container = document.createElement("div");
|
|
608
|
-
render(
|
|
609
|
-
html`
|
|
610
|
-
<ef-preview>
|
|
611
|
-
<ef-timegroup mode="sequence">
|
|
612
|
-
<ef-audio src="media/bars-n-tone2.mp4" mode="asset"></ef-audio>
|
|
613
|
-
</ef-timegroup>
|
|
614
|
-
</ef-preview>
|
|
615
|
-
`,
|
|
616
|
-
container,
|
|
617
|
-
);
|
|
618
|
-
document.body.appendChild(container);
|
|
619
|
-
|
|
620
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
621
|
-
const timegroup = container.querySelector("ef-timegroup");
|
|
622
|
-
await audio.updateComplete;
|
|
623
|
-
|
|
624
|
-
expect(timegroup).toBeDefined();
|
|
625
|
-
|
|
626
|
-
// The audio should have loaded successfully within the timegroup
|
|
627
|
-
// We test that it has a valid duration instead of a specific value
|
|
628
|
-
expect(audio.intrinsicDurationMs).toBeGreaterThan(0);
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
test("respects timegroup timing properties", async ({ expect }) => {
|
|
632
|
-
const container = document.createElement("div");
|
|
633
|
-
render(
|
|
634
|
-
html`
|
|
635
|
-
<ef-preview>
|
|
636
|
-
<ef-timegroup mode="contain">
|
|
637
|
-
<ef-audio src="media/bars-n-tone2.mp4" mode="asset" sourcein="1s" sourceout="4s"></ef-audio>
|
|
638
|
-
</ef-timegroup>
|
|
639
|
-
</ef-preview>
|
|
640
|
-
`,
|
|
641
|
-
container,
|
|
642
|
-
);
|
|
643
|
-
document.body.appendChild(container);
|
|
644
|
-
|
|
645
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
646
|
-
await audio.updateComplete;
|
|
647
|
-
|
|
648
|
-
// Wait for fragment index to load
|
|
649
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
650
|
-
|
|
651
|
-
// Should respect sourcein/sourceout timing
|
|
652
|
-
expect(audio.sourceInMs).toBe(1000);
|
|
653
|
-
expect(audio.sourceOutMs).toBe(4000);
|
|
654
|
-
expect(audio.durationMs).toBe(3000); // 4s - 1s
|
|
655
|
-
});
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
describe.skip("audio-specific functionality", () => {
|
|
659
|
-
test("inherits audio analysis capabilities from EFMedia", ({ expect }) => {
|
|
660
|
-
const container = document.createElement("div");
|
|
661
|
-
render(
|
|
662
|
-
html`
|
|
663
|
-
<ef-workbench>
|
|
664
|
-
<ef-preview>
|
|
665
|
-
<ef-audio></ef-audio>
|
|
666
|
-
</ef-preview>
|
|
667
|
-
</ef-workbench>
|
|
668
|
-
`,
|
|
669
|
-
container,
|
|
670
|
-
);
|
|
671
|
-
document.body.appendChild(container);
|
|
672
|
-
|
|
673
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
674
|
-
|
|
675
|
-
// Should inherit audio analysis from EFMedia
|
|
676
|
-
expect(audio.audioBufferTask).toBeDefined();
|
|
677
|
-
expect(audio.frequencyDataTask).toBeDefined();
|
|
678
|
-
expect(audio.byteTimeDomainTask).toBeDefined();
|
|
679
|
-
expect(audio.fftSize).toBeDefined();
|
|
680
|
-
expect(audio.fftGain).toBeDefined();
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
test("can access audio track information", async ({ expect }) => {
|
|
684
|
-
const container = document.createElement("div");
|
|
685
|
-
render(
|
|
686
|
-
html`
|
|
687
|
-
<ef-preview>
|
|
688
|
-
<ef-audio src="media/bars-n-tone2.mp4" mode="asset"></ef-audio>
|
|
689
|
-
</ef-preview>
|
|
690
|
-
`,
|
|
691
|
-
container,
|
|
692
|
-
);
|
|
693
|
-
document.body.appendChild(container);
|
|
694
|
-
|
|
695
|
-
const audio = container.querySelector("ef-audio") as EFAudio;
|
|
696
|
-
await audio.updateComplete;
|
|
697
|
-
|
|
698
|
-
// Wait for fragment index to load
|
|
699
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
700
|
-
|
|
701
|
-
// Should be able to access default audio track
|
|
702
|
-
// We test that the audio loads successfully instead of checking specific track ID
|
|
703
|
-
expect(audio.intrinsicDurationMs).toBeGreaterThan(0);
|
|
704
|
-
});
|
|
705
|
-
});
|
|
706
|
-
});
|