@editframe/elements 0.18.27-beta.0 → 0.19.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.
Files changed (73) hide show
  1. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +10 -0
  2. package/dist/elements/EFMedia/AssetMediaEngine.js +13 -1
  3. package/dist/elements/EFMedia/JitMediaEngine.d.ts +10 -0
  4. package/dist/elements/EFMedia/JitMediaEngine.js +12 -0
  5. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +16 -12
  6. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +1 -1
  7. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +0 -4
  8. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +1 -1
  9. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +0 -4
  10. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +1 -1
  11. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +3 -2
  12. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +16 -12
  13. package/dist/elements/EFMedia.d.ts +2 -3
  14. package/dist/elements/EFMedia.js +0 -4
  15. package/dist/elements/EFTemporal.d.ts +9 -6
  16. package/dist/elements/EFTemporal.js +15 -12
  17. package/dist/elements/EFTimegroup.browsertest.d.ts +26 -0
  18. package/dist/elements/EFTimegroup.d.ts +13 -15
  19. package/dist/elements/EFTimegroup.js +123 -67
  20. package/dist/elements/EFVideo.d.ts +5 -1
  21. package/dist/elements/EFVideo.js +16 -8
  22. package/dist/elements/EFWaveform.js +2 -3
  23. package/dist/elements/FetchContext.browsertest.d.ts +0 -0
  24. package/dist/elements/FetchMixin.js +14 -9
  25. package/dist/elements/TimegroupController.js +2 -1
  26. package/dist/elements/updateAnimations.browsertest.d.ts +0 -0
  27. package/dist/elements/updateAnimations.d.ts +19 -9
  28. package/dist/elements/updateAnimations.js +64 -25
  29. package/dist/gui/ContextMixin.js +34 -27
  30. package/dist/gui/EFConfiguration.d.ts +1 -1
  31. package/dist/gui/EFConfiguration.js +1 -0
  32. package/dist/gui/EFFilmstrip.d.ts +1 -0
  33. package/dist/gui/EFFilmstrip.js +12 -14
  34. package/dist/gui/TWMixin.js +1 -1
  35. package/dist/style.css +1 -1
  36. package/dist/transcoding/cache/URLTokenDeduplicator.d.ts +38 -0
  37. package/dist/transcoding/cache/URLTokenDeduplicator.js +66 -0
  38. package/dist/transcoding/cache/URLTokenDeduplicator.test.d.ts +1 -0
  39. package/dist/transcoding/types/index.d.ts +10 -0
  40. package/package.json +2 -2
  41. package/src/elements/EFMedia/AssetMediaEngine.ts +16 -2
  42. package/src/elements/EFMedia/JitMediaEngine.ts +14 -0
  43. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -1
  44. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +11 -4
  45. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -4
  46. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +4 -1
  47. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -5
  48. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +2 -2
  49. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +7 -3
  50. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +11 -4
  51. package/src/elements/EFMedia.browsertest.ts +13 -4
  52. package/src/elements/EFMedia.ts +6 -10
  53. package/src/elements/EFTemporal.ts +21 -26
  54. package/src/elements/EFTimegroup.browsertest.ts +186 -2
  55. package/src/elements/EFTimegroup.ts +205 -98
  56. package/src/elements/EFVideo.browsertest.ts +53 -132
  57. package/src/elements/EFVideo.ts +26 -13
  58. package/src/elements/EFWaveform.ts +2 -3
  59. package/src/elements/FetchContext.browsertest.ts +396 -0
  60. package/src/elements/FetchMixin.ts +25 -8
  61. package/src/elements/TimegroupController.ts +2 -1
  62. package/src/elements/updateAnimations.browsertest.ts +586 -0
  63. package/src/elements/updateAnimations.ts +113 -50
  64. package/src/gui/ContextMixin.browsertest.ts +4 -9
  65. package/src/gui/ContextMixin.ts +52 -33
  66. package/src/gui/EFConfiguration.ts +1 -1
  67. package/src/gui/EFFilmstrip.ts +15 -18
  68. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +182 -0
  69. package/src/transcoding/cache/URLTokenDeduplicator.ts +101 -0
  70. package/src/transcoding/types/index.ts +11 -0
  71. package/test/EFVideo.framegen.browsertest.ts +1 -1
  72. package/test/setup.ts +2 -0
  73. package/types.json +1 -1
@@ -0,0 +1,586 @@
1
+ import { assert, beforeEach, describe, test } from "vitest";
2
+ import type { EFTimegroup } from "./EFTimegroup.js";
3
+ import {
4
+ type AnimatableElement,
5
+ updateAnimations,
6
+ } from "./updateAnimations.js";
7
+
8
+ import "./EFTimegroup.js";
9
+
10
+ beforeEach(() => {
11
+ // Clean up DOM
12
+ while (document.body.children.length) {
13
+ document.body.children[0]?.remove();
14
+ }
15
+ // Clean up localStorage
16
+ for (let i = 0; i < localStorage.length; i++) {
17
+ const key = localStorage.key(i);
18
+ if (typeof key !== "string") continue;
19
+ localStorage.removeItem(key);
20
+ }
21
+ });
22
+
23
+ function createTestElement(
24
+ props: Partial<AnimatableElement> = {},
25
+ ): AnimatableElement {
26
+ const element = document.createElement("div") as unknown as AnimatableElement;
27
+ // Override readonly properties for testing
28
+ Object.defineProperty(element, "currentTimeMs", {
29
+ value: props.currentTimeMs ?? 0,
30
+ writable: true,
31
+ });
32
+ Object.defineProperty(element, "durationMs", {
33
+ value: props.durationMs ?? 1000,
34
+ writable: true,
35
+ });
36
+ Object.defineProperty(element, "startTimeMs", {
37
+ value: props.startTimeMs ?? 0,
38
+ writable: true,
39
+ });
40
+ Object.defineProperty(element, "endTimeMs", {
41
+ value: props.endTimeMs ?? 1000,
42
+ writable: true,
43
+ });
44
+ Object.defineProperty(element, "rootTimegroup", {
45
+ value: props.rootTimegroup,
46
+ writable: true,
47
+ });
48
+ Object.defineProperty(element, "parentTimegroup", {
49
+ value: props.parentTimegroup,
50
+ writable: true,
51
+ });
52
+ document.body.appendChild(element);
53
+ return element;
54
+ }
55
+
56
+ describe("Timeline Element Synchronizer", () => {
57
+ describe("CSS custom properties", () => {
58
+ test("sets --ef-progress based on currentTimeMs/durationMs ratio", () => {
59
+ const element = createTestElement({
60
+ currentTimeMs: 250,
61
+ durationMs: 1000,
62
+ });
63
+
64
+ updateAnimations(element);
65
+
66
+ assert.equal(element.style.getPropertyValue("--ef-progress"), "25%");
67
+ });
68
+
69
+ test("clamps --ef-progress to 0-100% range", () => {
70
+ const element1 = createTestElement({
71
+ currentTimeMs: -100,
72
+ durationMs: 1000,
73
+ });
74
+ const element2 = createTestElement({
75
+ currentTimeMs: 1500,
76
+ durationMs: 1000,
77
+ });
78
+
79
+ updateAnimations(element1);
80
+ updateAnimations(element2);
81
+
82
+ assert.equal(element1.style.getPropertyValue("--ef-progress"), "0%");
83
+ assert.equal(element2.style.getPropertyValue("--ef-progress"), "100%");
84
+ });
85
+
86
+ test("sets --ef-duration to element durationMs", () => {
87
+ const element = createTestElement({
88
+ durationMs: 2000,
89
+ });
90
+
91
+ updateAnimations(element);
92
+
93
+ assert.equal(element.style.getPropertyValue("--ef-duration"), "2000ms");
94
+ });
95
+
96
+ test("sets --ef-transition-duration based on parentTimegroup overlapMs", () => {
97
+ const parentTimegroup = document.createElement(
98
+ "ef-timegroup",
99
+ ) as EFTimegroup;
100
+ parentTimegroup.overlapMs = 500;
101
+
102
+ const element = createTestElement({
103
+ parentTimegroup,
104
+ });
105
+
106
+ updateAnimations(element);
107
+
108
+ assert.equal(
109
+ element.style.getPropertyValue("--ef-transition-duration"),
110
+ "500ms",
111
+ );
112
+ });
113
+
114
+ test("sets --ef-transition-duration to 0ms when no parentTimegroup", () => {
115
+ const element = createTestElement();
116
+
117
+ updateAnimations(element);
118
+
119
+ assert.equal(
120
+ element.style.getPropertyValue("--ef-transition-duration"),
121
+ "0ms",
122
+ );
123
+ });
124
+
125
+ test("sets --ef-transition-out-start correctly", () => {
126
+ const parentTimegroup = document.createElement(
127
+ "ef-timegroup",
128
+ ) as EFTimegroup;
129
+ parentTimegroup.overlapMs = 200;
130
+
131
+ const element = createTestElement({
132
+ durationMs: 1000,
133
+ parentTimegroup,
134
+ });
135
+
136
+ updateAnimations(element);
137
+
138
+ assert.equal(
139
+ element.style.getPropertyValue("--ef-transition-out-start"),
140
+ "800ms",
141
+ );
142
+ });
143
+
144
+ test("sets animation-related CSS properties when animations are present", () => {
145
+ const element = createTestElement({
146
+ durationMs: 2000,
147
+ });
148
+
149
+ // Create an animation to trigger CSS property setting
150
+ element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 1000 });
151
+
152
+ updateAnimations(element);
153
+
154
+ assert.equal(element.style.getPropertyValue("--ef-duration"), "2000ms");
155
+ assert.equal(
156
+ element.style.getPropertyValue("--ef-transition-duration"),
157
+ "0ms",
158
+ );
159
+ assert.equal(
160
+ element.style.getPropertyValue("--ef-transition-out-start"),
161
+ "2000ms",
162
+ );
163
+ });
164
+ });
165
+
166
+ describe("element visibility", () => {
167
+ test("hides element when timeline is before startTimeMs", () => {
168
+ const rootTimegroup = document.createElement(
169
+ "ef-timegroup",
170
+ ) as EFTimegroup;
171
+ rootTimegroup.currentTimeMs = 100;
172
+
173
+ const element = createTestElement({
174
+ startTimeMs: 200,
175
+ endTimeMs: 800,
176
+ rootTimegroup,
177
+ });
178
+
179
+ updateAnimations(element);
180
+
181
+ assert.equal(element.style.display, "none");
182
+ });
183
+
184
+ test("hides element when timeline is after endTimeMs", () => {
185
+ const rootTimegroup = document.createElement(
186
+ "ef-timegroup",
187
+ ) as EFTimegroup;
188
+ rootTimegroup.currentTimeMs = 900;
189
+
190
+ const element = createTestElement({
191
+ startTimeMs: 200,
192
+ endTimeMs: 800,
193
+ rootTimegroup,
194
+ });
195
+
196
+ updateAnimations(element);
197
+
198
+ assert.equal(element.style.display, "none");
199
+ });
200
+
201
+ test("shows element when timeline is within start/end range (using element currentTimeMs)", () => {
202
+ const element = createTestElement({
203
+ currentTimeMs: 500,
204
+ startTimeMs: 200,
205
+ endTimeMs: 800,
206
+ });
207
+ // Start with element hidden
208
+ element.style.display = "none";
209
+
210
+ updateAnimations(element);
211
+
212
+ assert.equal(element.style.display, "");
213
+ });
214
+
215
+ test("uses element currentTimeMs when no rootTimegroup", () => {
216
+ const element = createTestElement({
217
+ currentTimeMs: 500,
218
+ startTimeMs: 200,
219
+ endTimeMs: 800,
220
+ });
221
+ element.style.display = "none";
222
+
223
+ updateAnimations(element);
224
+
225
+ assert.equal(element.style.display, "");
226
+ });
227
+
228
+ test("element at exact start boundary is visible (using element currentTimeMs)", () => {
229
+ const element = createTestElement({
230
+ currentTimeMs: 200,
231
+ startTimeMs: 200,
232
+ endTimeMs: 800,
233
+ });
234
+ element.style.display = "none";
235
+
236
+ updateAnimations(element);
237
+
238
+ assert.equal(element.style.display, "");
239
+ });
240
+
241
+ test("element at exact end boundary is hidden (using element currentTimeMs)", () => {
242
+ const element = createTestElement({
243
+ currentTimeMs: 800,
244
+ startTimeMs: 200,
245
+ endTimeMs: 800,
246
+ });
247
+ element.style.display = "";
248
+
249
+ updateAnimations(element);
250
+
251
+ assert.equal(element.style.display, "none");
252
+ });
253
+
254
+ test("element just before start boundary is hidden", () => {
255
+ const element = createTestElement({
256
+ currentTimeMs: 199,
257
+ startTimeMs: 200,
258
+ endTimeMs: 800,
259
+ });
260
+
261
+ updateAnimations(element);
262
+
263
+ assert.equal(element.style.display, "none");
264
+ });
265
+
266
+ test("element just after end boundary is hidden", () => {
267
+ const element = createTestElement({
268
+ currentTimeMs: 801,
269
+ startTimeMs: 200,
270
+ endTimeMs: 800,
271
+ });
272
+
273
+ updateAnimations(element);
274
+
275
+ assert.equal(element.style.display, "none");
276
+ });
277
+ });
278
+
279
+ describe("Web Animations API integration", () => {
280
+ test("skips animation processing when getAnimations is not available", () => {
281
+ const element = createTestElement();
282
+ // Mock missing getAnimations
283
+ delete (element as any).getAnimations;
284
+
285
+ // Should not throw and should still set CSS properties
286
+ updateAnimations(element);
287
+
288
+ assert.equal(element.style.getPropertyValue("--ef-progress"), "0%");
289
+ });
290
+
291
+ test("pauses running animations", async () => {
292
+ const element = createTestElement();
293
+
294
+ // Create a test animation
295
+ const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
296
+ duration: 1000,
297
+ });
298
+ animation.play();
299
+
300
+ assert.equal(animation.playState, "running");
301
+
302
+ updateAnimations(element);
303
+
304
+ assert.equal(animation.playState, "paused");
305
+ });
306
+
307
+ test("ignores animations without KeyframeEffect", async () => {
308
+ const element = createTestElement();
309
+
310
+ // Create animation with non-KeyframeEffect (this is tricky to test, but we can verify no errors)
311
+ const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
312
+ duration: 1000,
313
+ });
314
+
315
+ // Mock the effect to not be a KeyframeEffect
316
+ Object.defineProperty(animation, "effect", {
317
+ value: {},
318
+ writable: false,
319
+ });
320
+
321
+ // Should not throw
322
+ updateAnimations(element);
323
+ });
324
+
325
+ test("ignores animations without target", async () => {
326
+ const element = createTestElement();
327
+
328
+ const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
329
+ duration: 1000,
330
+ });
331
+
332
+ // Mock the effect target to be null
333
+ if (animation.effect instanceof KeyframeEffect) {
334
+ Object.defineProperty(animation.effect, "target", {
335
+ value: null,
336
+ writable: false,
337
+ });
338
+ }
339
+
340
+ // Should not throw
341
+ updateAnimations(element);
342
+ });
343
+
344
+ test("handles missing timeTarget gracefully", async () => {
345
+ const element = createTestElement();
346
+
347
+ const target = document.createElement("div");
348
+ element.appendChild(target);
349
+
350
+ // Should not throw when target has no temporal parent
351
+ updateAnimations(element);
352
+ });
353
+
354
+ test("processes multiple animations on same element", async () => {
355
+ const element = createTestElement();
356
+
357
+ const animation1 = element.animate([{ opacity: 0 }, { opacity: 1 }], {
358
+ duration: 1000,
359
+ });
360
+ const animation2 = element.animate(
361
+ [{ transform: "scale(1)" }, { transform: "scale(2)" }],
362
+ { duration: 500 },
363
+ );
364
+
365
+ animation1.play();
366
+ animation2.play();
367
+
368
+ assert.equal(animation1.playState, "running");
369
+ assert.equal(animation2.playState, "running");
370
+
371
+ updateAnimations(element);
372
+
373
+ // Both animations should be paused
374
+ assert.equal(animation1.playState, "paused");
375
+ assert.equal(animation2.playState, "paused");
376
+ });
377
+
378
+ test("handles animations with zero duration", async () => {
379
+ const element = createTestElement();
380
+
381
+ // Should not throw
382
+ updateAnimations(element);
383
+ });
384
+
385
+ test("handles animations with zero iterations", async () => {
386
+ const element = createTestElement();
387
+
388
+ const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
389
+ duration: 1000,
390
+ iterations: 0,
391
+ });
392
+
393
+ updateAnimations(element);
394
+
395
+ // With 0 iterations and no timeTarget, currentIteration (0) >= iterations (0), so should be set to duration - epsilon
396
+ // But since there's no timeTarget, the code path continues and doesn't set currentTime
397
+ // The animation will continue with its default behavior
398
+ assert.equal(animation.currentTime, 0);
399
+ });
400
+
401
+ test("handles animations that are already paused", async () => {
402
+ const element = createTestElement();
403
+
404
+ const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
405
+ duration: 1000,
406
+ });
407
+ animation.pause();
408
+
409
+ assert.equal(animation.playState, "paused");
410
+
411
+ // Should not throw and should still set currentTime
412
+ updateAnimations(element);
413
+
414
+ assert.equal(animation.playState, "paused");
415
+ });
416
+
417
+ test("keeps completed animations available for scrubbing", async () => {
418
+ // Create a timegroup with 10s duration
419
+ const timegroup = document.createElement("ef-timegroup") as EFTimegroup;
420
+ timegroup.setAttribute("mode", "fixed");
421
+ timegroup.setAttribute("duration", "10000ms");
422
+ document.body.appendChild(timegroup);
423
+
424
+ // Create a child element with a 5s animation
425
+ const child = document.createElement("div");
426
+ timegroup.appendChild(child);
427
+
428
+ child.animate([{ opacity: 0 }, { opacity: 1 }], {
429
+ duration: 5000, // 5s animation
430
+ iterations: 1,
431
+ delay: 0,
432
+ });
433
+ timegroup.currentTime = 6;
434
+ await timegroup.seekTask.run();
435
+
436
+ // Animation should still be available even though timeline (6s) > animation duration (5s)
437
+ // This prevents animations from being removed, enabling scrubbing backwards
438
+ const animations = timegroup.getAnimations({ subtree: true });
439
+ assert.equal(
440
+ animations.length,
441
+ 1,
442
+ "REGRESSION TEST: Animation should remain available for scrubbing. This would fail with Number.EPSILON due to insufficient precision offset.",
443
+ );
444
+ });
445
+ });
446
+
447
+ describe("edge cases", () => {
448
+ test("handles zero duration gracefully", () => {
449
+ const element = createTestElement({
450
+ currentTimeMs: 100,
451
+ durationMs: 0,
452
+ });
453
+
454
+ updateAnimations(element);
455
+
456
+ // Should handle division by zero
457
+ assert.equal(element.style.getPropertyValue("--ef-progress"), "100%");
458
+ });
459
+
460
+ test("handles negative currentTimeMs", () => {
461
+ const element = createTestElement({
462
+ currentTimeMs: -100,
463
+ durationMs: 1000,
464
+ });
465
+
466
+ updateAnimations(element);
467
+
468
+ assert.equal(element.style.getPropertyValue("--ef-progress"), "0%");
469
+ });
470
+
471
+ test("handles missing parentTimegroup overlapMs", () => {
472
+ const parentTimegroup = {} as EFTimegroup; // Missing overlapMs property
473
+
474
+ const element = createTestElement({
475
+ parentTimegroup,
476
+ durationMs: 1000,
477
+ });
478
+
479
+ updateAnimations(element);
480
+
481
+ assert.equal(
482
+ element.style.getPropertyValue("--ef-transition-duration"),
483
+ "0ms",
484
+ );
485
+ assert.equal(
486
+ element.style.getPropertyValue("--ef-transition-out-start"),
487
+ "1000ms",
488
+ );
489
+ });
490
+
491
+ test("handles large duration values", () => {
492
+ const element = createTestElement({
493
+ currentTimeMs: 5000,
494
+ durationMs: 1000000, // 1000 seconds
495
+ });
496
+
497
+ updateAnimations(element);
498
+
499
+ assert.equal(element.style.getPropertyValue("--ef-progress"), "0.5%");
500
+ });
501
+
502
+ test("handles very small time values", () => {
503
+ const element = createTestElement({
504
+ currentTimeMs: 0.5,
505
+ durationMs: 10,
506
+ });
507
+
508
+ updateAnimations(element);
509
+
510
+ assert.equal(element.style.getPropertyValue("--ef-progress"), "5%");
511
+ });
512
+
513
+ test("does not modify display when element should remain visible", () => {
514
+ const element = createTestElement({
515
+ currentTimeMs: 500,
516
+ startTimeMs: 200,
517
+ endTimeMs: 800,
518
+ });
519
+ // Element starts visible (default state)
520
+ const initialDisplay = element.style.display;
521
+
522
+ updateAnimations(element);
523
+
524
+ // Should not have been modified
525
+ assert.equal(element.style.display, initialDisplay);
526
+ });
527
+
528
+ test("does not modify display when element should remain hidden", () => {
529
+ const element = createTestElement({
530
+ currentTimeMs: 100,
531
+ startTimeMs: 200,
532
+ endTimeMs: 800,
533
+ });
534
+ element.style.display = "none";
535
+
536
+ updateAnimations(element);
537
+
538
+ // Should still be hidden
539
+ assert.equal(element.style.display, "none");
540
+ });
541
+ });
542
+
543
+ describe("CSS variables with zero overlap", () => {
544
+ test("correctly sets transition duration to 0ms when overlap is 0ms", () => {
545
+ const element = createTestElement({
546
+ currentTimeMs: 500,
547
+ startTimeMs: 0,
548
+ endTimeMs: 1000,
549
+ durationMs: 1000,
550
+ parentTimegroup: {
551
+ overlapMs: 0, // Zero overlap case
552
+ } as any,
553
+ });
554
+
555
+ updateAnimations(element);
556
+
557
+ // When overlap is 0, transition duration should be 0ms (no overlap means no transition)
558
+ const transitionDuration = element.style.getPropertyValue(
559
+ "--ef-transition-duration",
560
+ );
561
+ assert.equal(
562
+ transitionDuration,
563
+ "0ms",
564
+ "Transition duration should be 0ms when no overlap",
565
+ );
566
+
567
+ // Transition out start should equal full duration (no transition period)
568
+ const transitionOutStart = element.style.getPropertyValue(
569
+ "--ef-transition-out-start",
570
+ );
571
+ assert.equal(
572
+ transitionOutStart,
573
+ "1000ms",
574
+ "Transition out start should equal full duration when no overlap",
575
+ );
576
+
577
+ // Duration variable should still be set correctly for within-clip animations
578
+ const duration = element.style.getPropertyValue("--ef-duration");
579
+ assert.equal(
580
+ duration,
581
+ "1000ms",
582
+ "Duration should be set for within-clip animation calculations",
583
+ );
584
+ });
585
+ });
586
+ });