@editframe/elements 0.16.8-beta.0 → 0.18.3-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 (267) hide show
  1. package/README.md +30 -0
  2. package/dist/DecoderResetFrequency.test.d.ts +1 -0
  3. package/dist/DecoderResetRecovery.test.d.ts +1 -0
  4. package/dist/DelayedLoadingState.d.ts +48 -0
  5. package/dist/DelayedLoadingState.integration.test.d.ts +1 -0
  6. package/dist/DelayedLoadingState.js +113 -0
  7. package/dist/DelayedLoadingState.test.d.ts +1 -0
  8. package/dist/EF_FRAMEGEN.d.ts +10 -1
  9. package/dist/EF_FRAMEGEN.js +199 -179
  10. package/dist/EF_INTERACTIVE.js +2 -6
  11. package/dist/EF_RENDERING.js +1 -3
  12. package/dist/LoadingDebounce.test.d.ts +1 -0
  13. package/dist/LoadingIndicator.browsertest.d.ts +0 -0
  14. package/dist/ManualScrubTest.test.d.ts +1 -0
  15. package/dist/ScrubResolvedFlashing.test.d.ts +1 -0
  16. package/dist/ScrubTrackManager.d.ts +96 -0
  17. package/dist/ScrubTrackManager.test.d.ts +1 -0
  18. package/dist/VideoSeekFlashing.browsertest.d.ts +0 -0
  19. package/dist/VideoStuckDiagnostic.test.d.ts +1 -0
  20. package/dist/elements/CrossUpdateController.js +13 -15
  21. package/dist/elements/EFAudio.browsertest.d.ts +0 -0
  22. package/dist/elements/EFAudio.d.ts +22 -3
  23. package/dist/elements/EFAudio.js +60 -43
  24. package/dist/elements/EFCaptions.js +337 -373
  25. package/dist/elements/EFImage.d.ts +1 -0
  26. package/dist/elements/EFImage.js +73 -91
  27. package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +18 -0
  28. package/dist/elements/EFMedia/AssetIdMediaEngine.js +41 -0
  29. package/dist/elements/EFMedia/AssetIdMediaEngine.test.d.ts +1 -0
  30. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +47 -0
  31. package/dist/elements/EFMedia/AssetMediaEngine.js +116 -0
  32. package/dist/elements/EFMedia/BaseMediaEngine.d.ts +55 -0
  33. package/dist/elements/EFMedia/BaseMediaEngine.js +96 -0
  34. package/dist/elements/EFMedia/BaseMediaEngine.test.d.ts +1 -0
  35. package/dist/elements/EFMedia/BufferedSeekingInput.browsertest.d.ts +1 -0
  36. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +43 -0
  37. package/dist/elements/EFMedia/BufferedSeekingInput.js +159 -0
  38. package/dist/elements/EFMedia/JitMediaEngine.browsertest.d.ts +0 -0
  39. package/dist/elements/EFMedia/JitMediaEngine.d.ts +31 -0
  40. package/dist/elements/EFMedia/JitMediaEngine.js +62 -0
  41. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.d.ts +9 -0
  42. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.d.ts +16 -0
  43. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +48 -0
  44. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +3 -0
  45. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +138 -0
  46. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.d.ts +9 -0
  47. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.d.ts +4 -0
  48. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +16 -0
  49. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.d.ts +9 -0
  50. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.d.ts +3 -0
  51. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +22 -0
  52. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.d.ts +7 -0
  53. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +24 -0
  54. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +4 -0
  55. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +18 -0
  56. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.d.ts +4 -0
  57. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +16 -0
  58. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +3 -0
  59. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +104 -0
  60. package/dist/elements/EFMedia/services/AudioElementFactory.browsertest.d.ts +1 -0
  61. package/dist/elements/EFMedia/services/AudioElementFactory.d.ts +22 -0
  62. package/dist/elements/EFMedia/services/AudioElementFactory.js +72 -0
  63. package/dist/elements/EFMedia/services/MediaSourceService.browsertest.d.ts +1 -0
  64. package/dist/elements/EFMedia/services/MediaSourceService.d.ts +47 -0
  65. package/dist/elements/EFMedia/services/MediaSourceService.js +73 -0
  66. package/dist/elements/EFMedia/shared/AudioSpanUtils.d.ts +7 -0
  67. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +54 -0
  68. package/dist/elements/EFMedia/shared/BufferUtils.d.ts +70 -0
  69. package/dist/elements/EFMedia/shared/BufferUtils.js +89 -0
  70. package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +23 -0
  71. package/dist/elements/EFMedia/shared/RenditionHelpers.browsertest.d.ts +1 -0
  72. package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +19 -0
  73. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +1 -0
  74. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +18 -0
  75. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +60 -0
  76. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts +1 -0
  77. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.d.ts +9 -0
  78. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts +16 -0
  79. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +46 -0
  80. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.d.ts +9 -0
  81. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.d.ts +4 -0
  82. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.js +16 -0
  83. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.d.ts +9 -0
  84. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.d.ts +3 -0
  85. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.js +27 -0
  86. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.d.ts +7 -0
  87. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +25 -0
  88. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.d.ts +9 -0
  89. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.d.ts +4 -0
  90. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +18 -0
  91. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.d.ts +9 -0
  92. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.d.ts +4 -0
  93. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.js +16 -0
  94. package/dist/elements/EFMedia.browsertest.d.ts +1 -0
  95. package/dist/elements/EFMedia.d.ts +95 -66
  96. package/dist/elements/EFMedia.js +204 -683
  97. package/dist/elements/EFSourceMixin.js +31 -48
  98. package/dist/elements/EFTemporal.d.ts +2 -1
  99. package/dist/elements/EFTemporal.js +266 -360
  100. package/dist/elements/EFTimegroup.d.ts +14 -1
  101. package/dist/elements/EFTimegroup.js +337 -323
  102. package/dist/elements/EFVideo.browsertest.d.ts +0 -0
  103. package/dist/elements/EFVideo.d.ts +123 -4
  104. package/dist/elements/EFVideo.js +308 -111
  105. package/dist/elements/EFWaveform.js +375 -411
  106. package/dist/elements/FetchMixin.js +14 -24
  107. package/dist/elements/MediaController.d.ts +30 -0
  108. package/dist/elements/SampleBuffer.d.ts +14 -0
  109. package/dist/elements/SampleBuffer.js +52 -0
  110. package/dist/elements/TargetController.js +130 -156
  111. package/dist/elements/TimegroupController.js +17 -19
  112. package/dist/elements/durationConverter.js +15 -4
  113. package/dist/elements/parseTimeToMs.js +4 -10
  114. package/dist/elements/printTaskStatus.d.ts +2 -0
  115. package/dist/elements/updateAnimations.js +39 -59
  116. package/dist/getRenderInfo.d.ts +2 -2
  117. package/dist/getRenderInfo.js +59 -67
  118. package/dist/gui/ContextMixin.js +150 -288
  119. package/dist/gui/EFConfiguration.js +27 -43
  120. package/dist/gui/EFFilmstrip.d.ts +3 -3
  121. package/dist/gui/EFFilmstrip.js +440 -620
  122. package/dist/gui/EFFitScale.d.ts +2 -2
  123. package/dist/gui/EFFitScale.js +112 -135
  124. package/dist/gui/EFFocusOverlay.js +45 -61
  125. package/dist/gui/EFPreview.js +30 -49
  126. package/dist/gui/EFScrubber.js +78 -99
  127. package/dist/gui/EFTimeDisplay.js +49 -70
  128. package/dist/gui/EFToggleLoop.js +17 -34
  129. package/dist/gui/EFTogglePlay.js +37 -58
  130. package/dist/gui/EFWorkbench.js +66 -88
  131. package/dist/gui/TWMixin.js +2 -48
  132. package/dist/gui/TWMixin2.js +31 -0
  133. package/dist/gui/efContext.js +2 -6
  134. package/dist/gui/fetchContext.js +1 -3
  135. package/dist/gui/focusContext.js +1 -3
  136. package/dist/gui/focusedElementContext.js +2 -6
  137. package/dist/gui/playingContext.js +1 -4
  138. package/dist/gui/services/ElementConnectionManager.browsertest.d.ts +1 -0
  139. package/dist/gui/services/ElementConnectionManager.d.ts +59 -0
  140. package/dist/gui/services/ElementConnectionManager.js +128 -0
  141. package/dist/gui/services/PlaybackController.browsertest.d.ts +1 -0
  142. package/dist/gui/services/PlaybackController.d.ts +103 -0
  143. package/dist/gui/services/PlaybackController.js +290 -0
  144. package/dist/index.js +5 -30
  145. package/dist/msToTimeCode.js +11 -13
  146. package/dist/services/MediaSourceManager.d.ts +62 -0
  147. package/dist/services/MediaSourceManager.js +211 -0
  148. package/dist/style.css +2 -1
  149. package/dist/transcoding/cache/CacheManager.d.ts +73 -0
  150. package/dist/transcoding/cache/RequestDeduplicator.d.ts +29 -0
  151. package/dist/transcoding/cache/RequestDeduplicator.js +53 -0
  152. package/dist/transcoding/cache/RequestDeduplicator.test.d.ts +1 -0
  153. package/dist/transcoding/types/index.d.ts +242 -0
  154. package/dist/transcoding/utils/MediaUtils.d.ts +9 -0
  155. package/dist/transcoding/utils/UrlGenerator.d.ts +26 -0
  156. package/dist/transcoding/utils/UrlGenerator.js +45 -0
  157. package/dist/transcoding/utils/constants.d.ts +27 -0
  158. package/dist/utils/LRUCache.d.ts +34 -0
  159. package/dist/utils/LRUCache.js +115 -0
  160. package/package.json +4 -3
  161. package/src/elements/EFAudio.browsertest.ts +709 -0
  162. package/src/elements/EFAudio.ts +59 -15
  163. package/src/elements/EFCaptions.browsertest.ts +0 -1
  164. package/src/elements/EFImage.browsertest.ts +42 -1
  165. package/src/elements/EFImage.ts +23 -3
  166. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +222 -0
  167. package/src/elements/EFMedia/AssetIdMediaEngine.ts +70 -0
  168. package/src/elements/EFMedia/AssetMediaEngine.ts +210 -0
  169. package/src/elements/EFMedia/BaseMediaEngine.test.ts +164 -0
  170. package/src/elements/EFMedia/BaseMediaEngine.ts +170 -0
  171. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +400 -0
  172. package/src/elements/EFMedia/BufferedSeekingInput.ts +267 -0
  173. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +165 -0
  174. package/src/elements/EFMedia/JitMediaEngine.ts +110 -0
  175. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +554 -0
  176. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +81 -0
  177. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +241 -0
  178. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +59 -0
  179. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +23 -0
  180. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +55 -0
  181. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +35 -0
  182. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +42 -0
  183. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +34 -0
  184. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +23 -0
  185. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +174 -0
  186. package/src/elements/EFMedia/services/AudioElementFactory.browsertest.ts +325 -0
  187. package/src/elements/EFMedia/services/AudioElementFactory.ts +119 -0
  188. package/src/elements/EFMedia/services/MediaSourceService.browsertest.ts +257 -0
  189. package/src/elements/EFMedia/services/MediaSourceService.ts +102 -0
  190. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +128 -0
  191. package/src/elements/EFMedia/shared/BufferUtils.ts +310 -0
  192. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +44 -0
  193. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +247 -0
  194. package/src/elements/EFMedia/shared/RenditionHelpers.ts +79 -0
  195. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +128 -0
  196. package/src/elements/EFMedia/tasks/makeMediaEngineTask.test.ts +233 -0
  197. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +89 -0
  198. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.ts +555 -0
  199. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +79 -0
  200. package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.ts +59 -0
  201. package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.ts +23 -0
  202. package/src/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.ts +55 -0
  203. package/src/elements/EFMedia/videoTasks/makeVideoInputTask.ts +45 -0
  204. package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +44 -0
  205. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +57 -0
  206. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +32 -0
  207. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +56 -0
  208. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +23 -0
  209. package/src/elements/EFMedia.browsertest.ts +696 -271
  210. package/src/elements/EFMedia.ts +218 -776
  211. package/src/elements/EFTemporal.browsertest.ts +0 -1
  212. package/src/elements/EFTemporal.ts +13 -3
  213. package/src/elements/EFTimegroup.browsertest.ts +6 -3
  214. package/src/elements/EFTimegroup.ts +221 -27
  215. package/src/elements/EFVideo.browsertest.ts +758 -0
  216. package/src/elements/EFVideo.ts +418 -68
  217. package/src/elements/EFWaveform.ts +5 -5
  218. package/src/elements/MediaController.ts +98 -0
  219. package/src/elements/SampleBuffer.ts +97 -0
  220. package/src/elements/printTaskStatus.ts +16 -0
  221. package/src/elements/updateAnimations.ts +6 -0
  222. package/src/gui/ContextMixin.ts +23 -104
  223. package/src/gui/TWMixin.ts +10 -3
  224. package/src/gui/services/ElementConnectionManager.browsertest.ts +263 -0
  225. package/src/gui/services/ElementConnectionManager.ts +224 -0
  226. package/src/gui/services/PlaybackController.browsertest.ts +437 -0
  227. package/src/gui/services/PlaybackController.ts +521 -0
  228. package/src/services/MediaSourceManager.ts +333 -0
  229. package/src/transcoding/cache/CacheManager.ts +208 -0
  230. package/src/transcoding/cache/RequestDeduplicator.test.ts +170 -0
  231. package/src/transcoding/cache/RequestDeduplicator.ts +65 -0
  232. package/src/transcoding/types/index.ts +265 -0
  233. package/src/transcoding/utils/MediaUtils.ts +63 -0
  234. package/src/transcoding/utils/UrlGenerator.ts +68 -0
  235. package/src/transcoding/utils/constants.ts +36 -0
  236. package/src/utils/LRUCache.ts +153 -0
  237. package/test/EFVideo.framegen.browsertest.ts +127 -0
  238. package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/data.bin +0 -0
  239. package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/metadata.json +21 -0
  240. package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/data.bin +0 -0
  241. package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/metadata.json +21 -0
  242. package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/data.bin +0 -0
  243. package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/metadata.json +21 -0
  244. package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/data.bin +0 -0
  245. package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/metadata.json +21 -0
  246. package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/data.bin +0 -0
  247. package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/metadata.json +21 -0
  248. package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/data.bin +0 -0
  249. package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/metadata.json +21 -0
  250. package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/data.bin +0 -0
  251. package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/metadata.json +21 -0
  252. package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/data.bin +0 -0
  253. package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/metadata.json +21 -0
  254. package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/data.bin +0 -0
  255. package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/metadata.json +21 -0
  256. package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/data.bin +0 -0
  257. package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/metadata.json +21 -0
  258. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -0
  259. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +19 -0
  260. package/test/createJitTestClips.ts +425 -0
  261. package/test/recordReplayProxyPlugin.js +302 -0
  262. package/test/useAssetMSW.ts +49 -0
  263. package/test/useMSW.ts +44 -0
  264. package/types.json +1 -1
  265. package/dist/gui/TWMixin.css.js +0 -4
  266. /package/dist/elements/{TargetController.test.d.ts → TargetController.browsertest.d.ts} +0 -0
  267. /package/src/elements/{TargetController.test.ts → TargetController.browsertest.ts} +0 -0
@@ -0,0 +1,758 @@
1
+ import { html, render } from "lit";
2
+ import { afterEach, beforeEach, describe, vi } from "vitest";
3
+ import { assetMSWHandlers } from "../../test/useAssetMSW.js";
4
+ import { test as baseTest } from "../../test/useMSW.js";
5
+ import type { EFVideo } from "./EFVideo.js";
6
+ import "./EFVideo.js";
7
+ import "../gui/EFWorkbench.js";
8
+ import "../gui/EFPreview.js";
9
+ import "./EFTimegroup.js";
10
+
11
+ // Extend the base test with no additional fixtures for EFVideo tests
12
+ const test = baseTest.extend({});
13
+
14
+ describe("EFVideo", () => {
15
+ beforeEach(() => {
16
+ // Clean up DOM and localStorage
17
+ while (document.body.children.length) {
18
+ document.body.children[0]?.remove();
19
+ }
20
+ localStorage.clear();
21
+ });
22
+
23
+ afterEach(async () => {
24
+ // Clean up any remaining elements
25
+ const videos = document.querySelectorAll("ef-video");
26
+ for (const video of videos) {
27
+ video.remove();
28
+ }
29
+ });
30
+
31
+ describe("basic rendering", () => {
32
+ test("should be defined and render canvas", async ({ expect }) => {
33
+ const element = document.createElement("ef-video");
34
+ document.body.appendChild(element);
35
+
36
+ // Wait for element to render
37
+ await element.updateComplete;
38
+
39
+ expect(element.tagName).toBe("EF-VIDEO");
40
+ expect(element.canvasElement).toBeDefined();
41
+ expect(element.canvasElement?.tagName).toBe("CANVAS");
42
+ });
43
+
44
+ test("canvas has correct default properties", async ({ expect }) => {
45
+ const container = document.createElement("div");
46
+ render(html`<ef-video></ef-video>`, container);
47
+ document.body.appendChild(container);
48
+
49
+ const video = container.querySelector("ef-video") as EFVideo;
50
+
51
+ // Wait for element to render
52
+ await video.updateComplete;
53
+
54
+ const canvas = video.canvasElement;
55
+
56
+ expect(canvas).toBeDefined();
57
+ expect(canvas?.width).toBeGreaterThan(0);
58
+ expect(canvas?.height).toBeGreaterThan(0);
59
+ });
60
+
61
+ test("canvas inherits styling correctly", async ({ expect }) => {
62
+ const container = document.createElement("div");
63
+ render(
64
+ html`
65
+ <ef-video style="width: 640px; height: 360px;"></ef-video>
66
+ `,
67
+ container,
68
+ );
69
+ document.body.appendChild(container);
70
+
71
+ const video = container.querySelector("ef-video") as EFVideo;
72
+
73
+ // Wait for element to render
74
+ await video.updateComplete;
75
+
76
+ const canvas = video.canvasElement;
77
+
78
+ expect(canvas).toBeDefined();
79
+ // Canvas should inherit the styling
80
+ const computedStyle = window.getComputedStyle(canvas!);
81
+ expect(computedStyle.width).toBe("640px");
82
+ expect(computedStyle.height).toBe("360px");
83
+ });
84
+ });
85
+
86
+ describe("video asset integration", () => {
87
+ test("integrates with video asset loading", async ({ expect, worker }) => {
88
+ // Set up MSW handlers for asset loading
89
+ worker.use(...assetMSWHandlers);
90
+ const container = document.createElement("div");
91
+ render(
92
+ html`
93
+ <ef-preview>
94
+ <ef-video src="/test-assets/media/bars-n-tone2.mp4" mode="asset"></ef-video>
95
+ </ef-preview>
96
+ `,
97
+ container,
98
+ );
99
+ document.body.appendChild(container);
100
+
101
+ const video = container.querySelector("ef-video") as EFVideo;
102
+ await video.updateComplete;
103
+
104
+ // Wait for fragment index to load
105
+ await new Promise((resolve) => setTimeout(resolve, 300));
106
+
107
+ expect(video.src).toBe("/test-assets/media/bars-n-tone2.mp4");
108
+
109
+ // The video should have loaded successfully and have a duration > 0
110
+ // We don't test for specific duration since real assets may vary
111
+ expect(video.intrinsicDurationMs).toBeGreaterThan(0);
112
+ });
113
+
114
+ test("handles missing video asset gracefully", async ({
115
+ expect,
116
+ worker,
117
+ }) => {
118
+ // Set up MSW handlers for asset loading
119
+ worker.use(...assetMSWHandlers);
120
+ const container = document.createElement("div");
121
+ render(
122
+ html`
123
+ <ef-preview>
124
+ <ef-video src="/nonexistent.mp4"></ef-video>
125
+ </ef-preview>
126
+ `,
127
+ container,
128
+ );
129
+ document.body.appendChild(container);
130
+
131
+ const video = container.querySelector("ef-video") as EFVideo;
132
+
133
+ // Should not throw when video asset is missing
134
+ expect(() => {
135
+ video.paintTask.run();
136
+ }).not.toThrow();
137
+ });
138
+ });
139
+
140
+ describe("frame painting and canvas updates", () => {
141
+ test("canvas dimensions update when frame dimensions change", async ({
142
+ expect,
143
+ }) => {
144
+ const container = document.createElement("div");
145
+ render(html`<ef-video></ef-video>`, container);
146
+ document.body.appendChild(container);
147
+
148
+ const video = container.querySelector("ef-video") as EFVideo;
149
+
150
+ // Wait for element to render
151
+ await video.updateComplete;
152
+
153
+ const canvas = video.canvasElement!;
154
+
155
+ // Mock a video frame with specific dimensions
156
+ const mockFrame = {
157
+ codedWidth: 1920,
158
+ codedHeight: 1080,
159
+ format: "RGBA",
160
+ timestamp: 0,
161
+ close: vi.fn(),
162
+ } as unknown as VideoFrame;
163
+
164
+ // Simulate frame painting (this would normally happen through paintTask)
165
+ const ctx = canvas.getContext("2d");
166
+ if (ctx && mockFrame.codedWidth && mockFrame.codedHeight) {
167
+ canvas.width = mockFrame.codedWidth;
168
+ canvas.height = mockFrame.codedHeight;
169
+ // Mock drawing the frame
170
+ ctx.fillStyle = "red";
171
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
172
+ }
173
+
174
+ expect(canvas.width).toBe(1920);
175
+ expect(canvas.height).toBe(1080);
176
+ });
177
+
178
+ test("handles frame painting with null format gracefully", async ({
179
+ expect,
180
+ }) => {
181
+ const container = document.createElement("div");
182
+ render(html`<ef-video></ef-video>`, container);
183
+ document.body.appendChild(container);
184
+
185
+ const video = container.querySelector("ef-video") as EFVideo;
186
+
187
+ // Wait for element to render
188
+ await video.updateComplete;
189
+
190
+ const canvas = video.canvasElement!;
191
+
192
+ // Mock a frame with null format (edge case)
193
+ const mockFrame = {
194
+ codedWidth: 640,
195
+ codedHeight: 480,
196
+ format: null,
197
+ timestamp: 0,
198
+ close: vi.fn(),
199
+ } as unknown as VideoFrame;
200
+
201
+ const ctx = canvas.getContext("2d");
202
+
203
+ // Should handle null format gracefully
204
+ expect(() => {
205
+ if (ctx && mockFrame.format === null) {
206
+ console.warn("Frame format is null", mockFrame);
207
+ return;
208
+ }
209
+ }).not.toThrow();
210
+ });
211
+
212
+ test("canvas context is available for drawing", async ({ expect }) => {
213
+ const container = document.createElement("div");
214
+ render(html`<ef-video></ef-video>`, container);
215
+ document.body.appendChild(container);
216
+
217
+ const video = container.querySelector("ef-video") as EFVideo;
218
+
219
+ // Wait for element to render
220
+ await video.updateComplete;
221
+
222
+ const canvas = video.canvasElement!;
223
+ const ctx = canvas.getContext("2d");
224
+
225
+ expect(ctx).toBeDefined();
226
+ expect(ctx).toBeInstanceOf(CanvasRenderingContext2D);
227
+
228
+ // Test that we can draw on the canvas
229
+ expect(() => {
230
+ ctx!.fillStyle = "blue";
231
+ ctx!.fillRect(0, 0, 100, 100);
232
+ }).not.toThrow();
233
+ });
234
+ });
235
+
236
+ describe("decoder lock scenarios", () => {
237
+ test("handles concurrent paint attempts safely", async ({ expect }) => {
238
+ const container = document.createElement("div");
239
+ render(html`<ef-video></ef-video>`, container);
240
+ document.body.appendChild(container);
241
+
242
+ const video = container.querySelector("ef-video") as EFVideo;
243
+
244
+ // Access the private decoder lock through reflection for testing
245
+ const decoderLockDescriptor = Object.getOwnPropertyDescriptor(
246
+ Object.getPrototypeOf(video),
247
+ "#decoderLock",
248
+ );
249
+
250
+ // Simulate the decoder being in use
251
+ if (decoderLockDescriptor) {
252
+ // We can't directly access private fields in tests, but we can test
253
+ // that multiple paint calls don't cause issues
254
+ const paintPromise1 = video.paintTask.run();
255
+ const paintPromise2 = video.paintTask.run();
256
+ const paintPromise3 = video.paintTask.run();
257
+
258
+ // All should complete without throwing
259
+ await expect(
260
+ Promise.allSettled([paintPromise1, paintPromise2, paintPromise3]),
261
+ ).resolves.toBeDefined();
262
+ }
263
+ });
264
+
265
+ test("paintTask handles missing canvas gracefully", ({ expect }) => {
266
+ const container = document.createElement("div");
267
+ render(html`<ef-video></ef-video>`, container);
268
+ document.body.appendChild(container);
269
+
270
+ const video = container.querySelector("ef-video") as EFVideo;
271
+
272
+ // Remove canvas to test edge case
273
+ const canvas = video.canvasElement;
274
+ canvas?.remove();
275
+
276
+ // Paint task should handle missing canvas
277
+ expect(() => {
278
+ video.paintTask.run();
279
+ }).not.toThrow();
280
+ });
281
+
282
+ test("handles paint task with no video asset", ({ expect }) => {
283
+ const container = document.createElement("div");
284
+ render(html`<ef-video></ef-video>`, container);
285
+ document.body.appendChild(container);
286
+
287
+ const video = container.querySelector("ef-video") as EFVideo;
288
+
289
+ // Paint task should handle missing video asset gracefully
290
+ expect(() => {
291
+ video.paintTask.run();
292
+ }).not.toThrow();
293
+ });
294
+ });
295
+
296
+ describe("frame task integration", () => {
297
+ test("frameTask coordinates all required tasks", async ({ expect }) => {
298
+ const container = document.createElement("div");
299
+ render(
300
+ html`
301
+ <ef-preview>
302
+ <ef-video src="/test-video.mp4"></ef-video>
303
+ </ef-preview>
304
+ `,
305
+ container,
306
+ );
307
+ document.body.appendChild(container);
308
+
309
+ const video = container.querySelector("ef-video") as EFVideo;
310
+
311
+ // frameTask should complete without errors even when other tasks fail
312
+ expect(() => {
313
+ video.frameTask.run();
314
+ }).not.toThrow();
315
+ });
316
+
317
+ test("frameTask handles missing dependencies", ({ expect }) => {
318
+ const container = document.createElement("div");
319
+ render(html`<ef-video></ef-video>`, container);
320
+ document.body.appendChild(container);
321
+
322
+ const video = container.querySelector("ef-video") as EFVideo;
323
+
324
+ // Should handle missing dependencies gracefully
325
+ expect(() => {
326
+ video.frameTask.run();
327
+ }).not.toThrow();
328
+ });
329
+ });
330
+
331
+ describe("error handling and edge cases", () => {
332
+ test("handles seek to invalid time", ({ expect }) => {
333
+ const container = document.createElement("div");
334
+ render(html`<ef-video></ef-video>`, container);
335
+ document.body.appendChild(container);
336
+
337
+ const video = container.querySelector("ef-video") as EFVideo;
338
+
339
+ // Should handle invalid seek times gracefully
340
+ expect(() => {
341
+ video.desiredSeekTimeMs = -1000; // Invalid negative time
342
+ video.paintTask.run();
343
+ }).not.toThrow();
344
+
345
+ expect(() => {
346
+ video.desiredSeekTimeMs = Number.POSITIVE_INFINITY;
347
+ video.paintTask.run();
348
+ }).not.toThrow();
349
+ });
350
+
351
+ test("handles video element removal during playback", ({ expect }) => {
352
+ const container = document.createElement("div");
353
+ render(html`<ef-video></ef-video>`, container);
354
+ document.body.appendChild(container);
355
+
356
+ const video = container.querySelector("ef-video") as EFVideo;
357
+
358
+ // Start some operations
359
+ video.paintTask.run();
360
+
361
+ // Remove element
362
+ video.remove();
363
+
364
+ // Should not cause errors
365
+ expect(() => {
366
+ video.paintTask.run();
367
+ }).not.toThrow();
368
+ });
369
+
370
+ test("handles canvas context loss gracefully", async ({ expect }) => {
371
+ const container = document.createElement("div");
372
+ render(html`<ef-video></ef-video>`, container);
373
+ document.body.appendChild(container);
374
+
375
+ const video = container.querySelector("ef-video") as EFVideo;
376
+
377
+ // Wait for element to render
378
+ await video.updateComplete;
379
+
380
+ const canvas = video.canvasElement!;
381
+
382
+ // Simulate context loss by making getContext return null
383
+ const originalGetContext = canvas.getContext;
384
+ canvas.getContext = vi.fn().mockReturnValue(null);
385
+
386
+ // Should handle context loss gracefully
387
+ expect(() => {
388
+ video.paintTask.run();
389
+ }).not.toThrow();
390
+
391
+ // Restore original method
392
+ canvas.getContext = originalGetContext;
393
+ });
394
+ });
395
+
396
+ describe("assetId property", () => {
397
+ test("reads assetId from html source", async ({ expect }) => {
398
+ const container = document.createElement("div");
399
+ container.innerHTML = `<ef-video asset-id="test-video-asset-123"></ef-video>`;
400
+ document.body.appendChild(container);
401
+
402
+ const video = container.querySelector("ef-video") as EFVideo;
403
+ await video.updateComplete;
404
+
405
+ expect(video).toBeDefined();
406
+ expect(video.assetId).toBe("test-video-asset-123");
407
+
408
+ container.remove();
409
+ });
410
+
411
+ test("reads from js property", ({ expect }) => {
412
+ const container = document.createElement("div");
413
+ render(html`<ef-video></ef-video>`, container);
414
+ const video = container.querySelector("ef-video") as EFVideo;
415
+
416
+ video.assetId = "test-video-456";
417
+ expect(video.assetId).toBe("test-video-456");
418
+ });
419
+
420
+ test("reflects property changes to attribute", async ({ expect }) => {
421
+ const container = document.createElement("div");
422
+ render(html`<ef-video></ef-video>`, container);
423
+ document.body.appendChild(container);
424
+
425
+ const video = container.querySelector("ef-video") as EFVideo;
426
+ await video.updateComplete;
427
+
428
+ video.assetId = "test-video-789";
429
+ await video.updateComplete;
430
+ expect(video.getAttribute("asset-id")).toBe("test-video-789");
431
+
432
+ video.assetId = null;
433
+ await video.updateComplete;
434
+ expect(video.hasAttribute("asset-id")).toBe(false);
435
+
436
+ container.remove();
437
+ });
438
+ });
439
+
440
+ describe("integration with timegroups", () => {
441
+ test("integrates correctly within timegroup structure", async ({
442
+ expect,
443
+ worker,
444
+ }) => {
445
+ // Set up MSW handlers for asset loading
446
+ worker.use(...assetMSWHandlers);
447
+ const container = document.createElement("div");
448
+ render(
449
+ html`
450
+ <ef-preview>
451
+ <ef-timegroup mode="sequence">
452
+ <ef-video src="/test-assets/media/bars-n-tone2.mp4" mode="asset"></ef-video>
453
+ </ef-timegroup>
454
+ </ef-preview>
455
+ `,
456
+ container,
457
+ );
458
+ document.body.appendChild(container);
459
+
460
+ const video = container.querySelector("ef-video") as EFVideo;
461
+ const timegroup = container.querySelector("ef-timegroup");
462
+ await video.updateComplete;
463
+
464
+ // Wait for fragment index to load with longer timeout
465
+ await new Promise((resolve) => setTimeout(resolve, 500));
466
+
467
+ expect(timegroup).toBeDefined();
468
+
469
+ // The video should have loaded successfully within the timegroup
470
+ // We test that it has a valid duration instead of a specific value
471
+ // Allow for race conditions in test environment
472
+ if (video.intrinsicDurationMs === 0) {
473
+ // If not loaded yet, wait a bit more
474
+ await new Promise((resolve) => setTimeout(resolve, 300));
475
+ }
476
+ expect(video.intrinsicDurationMs).toBeGreaterThan(0);
477
+ });
478
+ });
479
+
480
+ describe.skip("scrub track integration", () => {
481
+ test("should initialize scrub track manager for JIT transcode mode", async ({
482
+ expect,
483
+ }) => {
484
+ const container = document.createElement("div");
485
+ render(
486
+ html`
487
+ <ef-preview>
488
+ <ef-video src="http://example.com/video.mp4"></ef-video>
489
+ </ef-preview>
490
+ `,
491
+ container,
492
+ );
493
+ document.body.appendChild(container);
494
+
495
+ const video = container.querySelector("ef-video") as EFVideo;
496
+ await video.updateComplete;
497
+
498
+ // Give the async initialization time to complete
499
+ await new Promise((resolve) => setTimeout(resolve, 100));
500
+
501
+ // For JIT transcode mode, scrub track manager should be initialized
502
+ expect(video.scrubTrackManager).toBeDefined();
503
+ });
504
+
505
+ test("should not initialize scrub track manager for asset mode", async ({
506
+ expect,
507
+ }) => {
508
+ const container = document.createElement("div");
509
+ render(
510
+ html`
511
+ <ef-preview>
512
+ <ef-video src="/@ef-abc123/video.mp4" mode="asset"></ef-video>
513
+ </ef-preview>
514
+ `,
515
+ container,
516
+ );
517
+ document.body.appendChild(container);
518
+
519
+ const video = container.querySelector("ef-video") as EFVideo;
520
+ await video.updateComplete;
521
+ await new Promise((resolve) => setTimeout(resolve, 100));
522
+
523
+ // For asset mode, scrub track manager should not be initialized
524
+ expect(video.scrubTrackManager).toBeUndefined();
525
+ });
526
+
527
+ test("should expose scrub track performance metrics", async ({
528
+ expect,
529
+ }) => {
530
+ const container = document.createElement("div");
531
+ render(
532
+ html`
533
+ <ef-preview>
534
+ <ef-video src="http://example.com/video.mp4"></ef-video>
535
+ </ef-preview>
536
+ `,
537
+ container,
538
+ );
539
+ document.body.appendChild(container);
540
+
541
+ const video = container.querySelector("ef-video") as EFVideo;
542
+ await video.updateComplete;
543
+ await new Promise((resolve) => setTimeout(resolve, 100));
544
+
545
+ const stats = video.getScrubTrackStats();
546
+
547
+ if (video.scrubTrackManager) {
548
+ expect(stats).not.toBeNull();
549
+ expect(typeof stats?.hits).toBe("number");
550
+ expect(typeof stats?.misses).toBe("number");
551
+ expect(typeof stats?.hitRate).toBe("number");
552
+ } else {
553
+ expect(stats).toBeNull();
554
+ }
555
+ });
556
+
557
+ test("should return null stats when no scrub track manager exists", async ({
558
+ expect,
559
+ }) => {
560
+ const container = document.createElement("div");
561
+ render(
562
+ html`
563
+ <ef-preview>
564
+ <ef-video src="/@ef-abc123/video.mp4" mode="asset"></ef-video>
565
+ </ef-preview>
566
+ `,
567
+ container,
568
+ );
569
+ document.body.appendChild(container);
570
+
571
+ const video = container.querySelector("ef-video") as EFVideo;
572
+ await video.updateComplete;
573
+
574
+ const stats = video.getScrubTrackStats();
575
+ expect(stats).toBeNull();
576
+ });
577
+
578
+ test("should have canvas element available", async ({ expect }) => {
579
+ const container = document.createElement("div");
580
+ render(html`<ef-video></ef-video>`, container);
581
+ document.body.appendChild(container);
582
+
583
+ const video = container.querySelector("ef-video") as EFVideo;
584
+ await video.updateComplete;
585
+
586
+ const canvas = video.canvasElement;
587
+ expect(canvas).toBeDefined();
588
+ expect(canvas?.tagName).toBe("CANVAS");
589
+ });
590
+
591
+ test("should clean up scrub track manager on disconnect", async ({
592
+ expect,
593
+ }) => {
594
+ const container = document.createElement("div");
595
+ render(
596
+ html`
597
+ <ef-preview>
598
+ <ef-video src="http://example.com/video.mp4"></ef-video>
599
+ </ef-preview>
600
+ `,
601
+ container,
602
+ );
603
+ document.body.appendChild(container);
604
+
605
+ const video = container.querySelector("ef-video") as EFVideo;
606
+ await video.updateComplete;
607
+ await new Promise((resolve) => setTimeout(resolve, 100));
608
+
609
+ const hadScrubManager = !!video.scrubTrackManager;
610
+
611
+ // Simulate disconnect
612
+ video.remove();
613
+
614
+ // If there was a scrub manager, it should have been cleaned up
615
+ // We can't directly test the cleanup call, but we can verify the element is disconnected
616
+ expect(video.isConnected).toBe(false);
617
+
618
+ // The scrub manager should still exist but be cleaned up internally
619
+ if (hadScrubManager) {
620
+ expect(video.scrubTrackManager).toBeDefined();
621
+ }
622
+ });
623
+ });
624
+
625
+ describe("loading indicator", () => {
626
+ test("should not show loading indicator for operations completing under 250ms", async ({
627
+ expect,
628
+ }) => {
629
+ const container = document.createElement("div");
630
+ render(html`<ef-video></ef-video>`, container);
631
+ document.body.appendChild(container);
632
+
633
+ const video = container.querySelector("ef-video") as EFVideo;
634
+ await video.updateComplete;
635
+
636
+ // Start a fast operation
637
+ video.startDelayedLoading("test-fast", "Fast operation");
638
+
639
+ // Clear it quickly (under 250ms)
640
+ setTimeout(() => {
641
+ video.clearDelayedLoading("test-fast");
642
+ }, 100);
643
+
644
+ // Wait past the delay threshold
645
+ await new Promise((resolve) => setTimeout(resolve, 300));
646
+
647
+ expect(video.loadingState.isLoading).toBe(false);
648
+ });
649
+
650
+ test("should show loading indicator only after 250ms for slow operations", async ({
651
+ expect,
652
+ }) => {
653
+ const container = document.createElement("div");
654
+ render(html`<ef-video></ef-video>`, container);
655
+ document.body.appendChild(container);
656
+
657
+ const video = container.querySelector("ef-video") as EFVideo;
658
+ await video.updateComplete;
659
+
660
+ // Start a slow operation
661
+ video.startDelayedLoading("test-slow", "Slow operation");
662
+
663
+ // Should not be loading immediately
664
+ expect(video.loadingState.isLoading).toBe(false);
665
+
666
+ // Wait past the delay threshold
667
+ await new Promise((resolve) => setTimeout(resolve, 300));
668
+
669
+ // Should now be loading
670
+ expect(video.loadingState.isLoading).toBe(true);
671
+ expect(video.loadingState.message).toBe("Slow operation");
672
+
673
+ // Clear the loading
674
+ video.clearDelayedLoading("test-slow");
675
+
676
+ // Should stop loading
677
+ expect(video.loadingState.isLoading).toBe(false);
678
+ });
679
+
680
+ test("should handle multiple concurrent loading operations", async ({
681
+ expect,
682
+ }) => {
683
+ const container = document.createElement("div");
684
+ render(html`<ef-video></ef-video>`, container);
685
+ document.body.appendChild(container);
686
+
687
+ const video = container.querySelector("ef-video") as EFVideo;
688
+ await video.updateComplete;
689
+
690
+ // Start multiple operations
691
+ video.startDelayedLoading("op1", "Operation 1");
692
+ video.startDelayedLoading("op2", "Operation 2");
693
+
694
+ // Wait past delay threshold
695
+ await new Promise((resolve) => setTimeout(resolve, 300));
696
+
697
+ // Should be loading
698
+ expect(video.loadingState.isLoading).toBe(true);
699
+
700
+ // Clear one operation
701
+ video.clearDelayedLoading("op1");
702
+
703
+ // Should still be loading (op2 still active)
704
+ expect(video.loadingState.isLoading).toBe(true);
705
+
706
+ // Clear second operation
707
+ video.clearDelayedLoading("op2");
708
+
709
+ // Should stop loading
710
+ expect(video.loadingState.isLoading).toBe(false);
711
+ });
712
+
713
+ test("should not show loading for background operations", async ({
714
+ expect,
715
+ }) => {
716
+ const container = document.createElement("div");
717
+ render(html`<ef-video></ef-video>`, container);
718
+ document.body.appendChild(container);
719
+
720
+ const video = container.querySelector("ef-video") as EFVideo;
721
+ await video.updateComplete;
722
+
723
+ // Start a background operation
724
+ video.startDelayedLoading("bg-op", "Background operation", {
725
+ background: true,
726
+ });
727
+
728
+ // Wait past delay threshold
729
+ await new Promise((resolve) => setTimeout(resolve, 300));
730
+
731
+ // Should not show loading UI for background operations
732
+ expect(video.loadingState.isLoading).toBe(false);
733
+
734
+ // Clear the operation
735
+ video.clearDelayedLoading("bg-op");
736
+ });
737
+
738
+ test("should properly clean up loading state on disconnect", async ({
739
+ expect,
740
+ }) => {
741
+ const container = document.createElement("div");
742
+ render(html`<ef-video></ef-video>`, container);
743
+ document.body.appendChild(container);
744
+
745
+ const video = container.querySelector("ef-video") as EFVideo;
746
+ await video.updateComplete;
747
+
748
+ // Start an operation
749
+ video.startDelayedLoading("cleanup-test", "Test operation");
750
+
751
+ // Disconnect the element
752
+ video.remove();
753
+
754
+ // Loading should be cleared
755
+ expect(video.loadingState.isLoading).toBe(false);
756
+ });
757
+ });
758
+ });