88import { describe , expect , it , vi , beforeEach , afterEach } from 'vitest' ;
99import { Composition } from '../../composition' ;
1010import { AudioClip } from './audio' ;
11-
12- import type { MockInstance } from 'vitest' ;
13- import type { frame } from '../../types' ;
1411import { Timestamp } from '../../models' ;
1512import { AudioSource } from '../../sources' ;
1613
14+ import type { MockInstance } from 'vitest' ;
15+
1716const file = new File ( [ ] , 'audio.mp3' , { type : 'audio/mp3' } ) ;
1817
1918describe ( 'The Audio Clip' , ( ) => {
@@ -26,23 +25,40 @@ describe('The Audio Clip', () => {
2625 let clip : AudioClip ;
2726
2827 beforeEach ( async ( ) => {
29- clip = await new AudioClip ( ) . load ( file ) ;
28+ clip = new AudioClip ( file ) ;
3029 clip . on ( 'update' , updateFn ) ;
3130 clip . on ( 'error' , errorFn ) ;
3231 clip . on ( 'load' , loadFn ) ;
3332
34- playFn = vi . spyOn ( clip . element , 'play' ) . mockImplementation ( async ( ) => {
35- clip . element . dispatchEvent ( new Event ( 'play' ) ) ;
36- } ) ;
37- pauseFn = vi . spyOn ( clip . element , 'pause' ) . mockImplementation ( async ( ) => {
38- clip . element . dispatchEvent ( new Event ( 'pause' ) ) ;
39- } ) ;
33+ playFn = mockPlayValid ( clip ) ;
34+ pauseFn = mockPauseValid ( clip ) ;
35+
36+ mockAudioValid ( clip ) ;
37+ mockDurationValid ( clip ) ;
38+
39+ await clip . init ( ) ;
4040 } ) ;
4141
42- it ( 'should have an initial state' , async ( ) => {
43- clip . element . dispatchEvent ( new Event ( 'canplay' ) ) ;
44- expect ( clip . element ) . toBeDefined ( ) ;
45- expect ( loadFn ) . toBeCalledTimes ( 1 ) ;
42+ it ( 'should initialize' , async ( ) => {
43+ const clip = new AudioClip ( file ) ;
44+
45+ expect ( clip . state ) . toBe ( 'IDLE' ) ;
46+ expect ( clip . duration . seconds ) . not . toBe ( 20 ) ;
47+ expect ( clip . source . file ) . toBeInstanceOf ( File ) ;
48+ expect ( clip . element . src ) . toBeFalsy ( ) ;
49+
50+ const evtSpy = mockAudioValid ( clip ) ;
51+ const timeSpy = mockDurationValid ( clip ) ;
52+
53+ await clip . init ( ) ;
54+
55+ expect ( clip . duration . seconds ) . toBe ( 20 ) ;
56+ expect ( clip . range [ 0 ] . seconds ) . toBe ( 0 ) ;
57+ expect ( clip . range [ 1 ] . seconds ) . toBe ( 20 ) ;
58+
59+ expect ( evtSpy ) . toHaveBeenCalledOnce ( ) ;
60+ expect ( timeSpy ) . toHaveBeenCalledOnce ( ) ;
61+
4662 expect ( clip . type ) . toBe ( 'audio' ) ;
4763 expect ( clip . state ) . toBe ( 'READY' ) ;
4864 expect ( clip . source . name ) . toBe ( 'audio.mp3' ) ;
@@ -51,83 +67,112 @@ describe('The Audio Clip', () => {
5167 ) ;
5268 } ) ;
5369
54- it ( 'should update its state when canplay is called' , ( ) => {
55- vi . spyOn ( clip . element , 'duration' , 'get' ) . mockReturnValue ( 30 ) ;
56- expect ( clip . element . duration ) . toBe ( 30 ) ;
57- expect ( clip . duration . seconds ) . toBe ( 0 ) ;
58- clip . element . dispatchEvent ( new Event ( 'canplay' ) ) ;
59- expect ( clip . duration . seconds ) . toBe ( 30 ) ;
60- expect ( clip . range [ 0 ] . seconds ) . toBe ( 0 ) ;
61- expect ( clip . range [ 1 ] . seconds ) . toBe ( 30 ) ;
62- expect ( clip . state ) . toBe ( 'READY' ) ;
63- expect ( loadFn ) . toBeCalledTimes ( 1 ) ;
64- } ) ;
70+ it ( "should throw an error if the media can't be loaded" , async ( ) => {
71+ const clip = new AudioClip ( file ) ;
6572
66- it ( 'should go to idle state if the media gets emptied' , ( ) => {
67- vi . spyOn ( clip . element , 'duration' , 'get' ) . mockReturnValue ( 30 ) ;
68- clip . element . dispatchEvent ( new Event ( 'canplay' ) ) ;
73+ mockDurationValid ( clip ) ;
74+ const evtSpy = mockAudioInvalid ( clip ) ;
75+ mockDurationValid ( clip ) ;
6976
70- clip . playing = true ;
71- clip . element . dispatchEvent ( new Event ( 'emptied' ) ) ;
72- expect ( clip . playing ) . toBe ( false ) ;
73- expect ( clip . state ) . toBe ( 'IDLE' ) ;
74- expect ( clip . track ) . toBeUndefined ( ) ;
75- } ) ;
77+ await expect ( ( ) => clip . init ( ) ) . rejects . toThrowError ( ) ;
78+
79+ expect ( clip . duration . seconds ) . not . toBe ( 20 ) ;
80+ expect ( evtSpy ) . toHaveBeenCalledOnce ( ) ;
7681
77- it ( "should throw an error if the media can't be loaded" , ( ) => {
78- vi . spyOn ( clip . element , 'duration' , 'get' ) . mockReturnValue ( 30 ) ;
79- clip . element . dispatchEvent ( new Event ( 'canplay' ) ) ;
80- clip . element . dispatchEvent ( new Event ( 'error' ) ) ;
8182 expect ( clip . state ) . toBe ( 'ERROR' ) ;
82- expect ( clip . track ) . toBeUndefined ( ) ;
83- expect ( errorFn ) . toBeCalledTimes ( 1 ) ;
8483 } ) ;
8584
86- it ( 'should play and pause the audio with the render method' , ( ) => {
87- vi . spyOn ( clip . element , 'duration' , 'get' ) . mockReturnValue ( 5 ) ;
88-
85+ it ( 'should play and pause the audio with the render method' , async ( ) => {
8986 const composition = new Composition ( ) ;
90- Object . assign ( clip , { track : { composition } } ) ;
87+ await composition . add ( clip . offsetBy ( 30 ) ) ;
9188
92- clip . element . dispatchEvent ( new Event ( 'canplay' ) ) ;
93- expect ( clip . duration . seconds ) . toBe ( 5 ) ;
89+ const enterSpy = vi . spyOn ( clip , 'enter' ) ;
90+ const updateSpy = vi . spyOn ( clip , 'update' ) ;
91+ const exitSpy = vi . spyOn ( clip , 'exit' ) ;
9492
9593 expect ( clip . playing ) . toBe ( false ) ;
94+
9695 composition . state = 'PLAY' ;
97- clip . render ( ) ;
96+ composition . computeFrame ( ) ;
97+
98+ expect ( playFn ) . toBeCalledTimes ( 0 ) ;
99+ expect ( clip . playing ) . toBe ( false ) ;
100+ expect ( enterSpy ) . toHaveBeenCalledTimes ( 0 ) ;
101+ expect ( updateSpy ) . toHaveBeenCalledTimes ( 0 ) ;
102+ expect ( exitSpy ) . toHaveBeenCalledTimes ( 0 ) ;
103+
104+ composition . frame = 30 ;
105+ composition . computeFrame ( ) ;
98106
99- expect ( clip . playing ) . toBe ( true ) ;
100107 expect ( playFn ) . toBeCalledTimes ( 1 ) ;
108+ expect ( clip . playing ) . toBe ( true ) ;
109+ expect ( enterSpy ) . toHaveBeenCalledTimes ( 1 ) ;
110+ expect ( updateSpy ) . toHaveBeenCalledTimes ( 1 ) ;
111+ expect ( exitSpy ) . toHaveBeenCalledTimes ( 0 ) ;
101112
102113 composition . state = 'IDLE' ;
103- clip . render ( ) ;
114+ composition . frame = 60 ;
115+ composition . computeFrame ( ) ;
116+
104117 expect ( clip . playing ) . toBe ( false ) ;
105118 expect ( pauseFn ) . toBeCalledTimes ( 1 ) ;
119+ expect ( enterSpy ) . toHaveBeenCalledTimes ( 1 ) ;
120+ expect ( updateSpy ) . toHaveBeenCalledTimes ( 2 ) ;
121+ expect ( exitSpy ) . toHaveBeenCalledTimes ( 0 ) ;
106122
107- pauseFn . mockClear ( ) ;
123+ composition . state = 'PLAY' ;
124+ composition . frame = 90 ;
125+ composition . computeFrame ( ) ;
108126
109- clip . playing = true ;
110- clip . unrender ( ) ;
127+ expect ( playFn ) . toBeCalledTimes ( 2 ) ;
128+ expect ( clip . playing ) . toBe ( true ) ;
129+ expect ( enterSpy ) . toHaveBeenCalledTimes ( 1 ) ;
130+ expect ( updateSpy ) . toHaveBeenCalledTimes ( 3 ) ;
131+ expect ( exitSpy ) . toHaveBeenCalledTimes ( 0 ) ;
132+
133+ composition . frame = clip . stop . frames + 1 ;
134+ composition . computeFrame ( ) ;
111135
136+ expect ( pauseFn ) . toBeCalledTimes ( 2 ) ;
112137 expect ( clip . playing ) . toBe ( false ) ;
113- expect ( pauseFn ) . toBeCalledTimes ( 1 ) ;
138+ expect ( enterSpy ) . toHaveBeenCalledTimes ( 1 ) ;
139+ expect ( updateSpy ) . toHaveBeenCalledTimes ( 3 ) ;
140+ expect ( exitSpy ) . toHaveBeenCalledTimes ( 1 ) ;
114141 } ) ;
115142
116143 it ( 'slice should be persistant after adding clip' , async ( ) => {
117- vi . spyOn ( clip . element , 'duration' , 'get' ) . mockReturnValue ( 30 ) ;
118- vi . spyOn ( clip , 'seek' ) . mockImplementation ( async ( ) => { } ) ;
119-
120- clip . subclip ( < frame > 20 , < frame > 60 ) ;
144+ clip . offsetBy ( 30 ) . subclip ( 20 , 60 ) . set ( { muted : true , volume : 0.2 } ) ;
121145
122146 const composition = new Composition ( ) ;
123- const promise = composition . add ( clip ) ;
124- clip . element . dispatchEvent ( new Event ( 'canplay' ) ) ;
125- await promise ;
147+ await composition . add ( clip )
126148
127149 expect ( clip . state ) . toBe ( 'ATTACHED' ) ;
128- expect ( clip . duration . seconds ) . toBe ( 30 ) ;
150+ expect ( clip . duration . seconds ) . toBe ( 20 ) ;
129151 expect ( clip . range [ 0 ] . frames ) . toBe ( 20 ) ;
130152 expect ( clip . range [ 1 ] . frames ) . toBe ( 60 ) ;
153+ expect ( clip . offset . frames ) . toBe ( 30 ) ;
154+ expect ( clip . muted ) . toBe ( true ) ;
155+ expect ( clip . volume ) . toBe ( 0.2 ) ;
156+ } ) ;
157+
158+ it ( "should not render the clip if it's disabled" , async ( ) => {
159+ const composition = new Composition ( ) ;
160+ await composition . add ( clip ) ;
161+
162+ const exitSpy = vi . spyOn ( clip , 'exit' ) ;
163+ const updateSpy = vi . spyOn ( clip , 'update' ) ;
164+
165+ composition . state = 'PLAY' ;
166+ composition . computeFrame ( ) ;
167+
168+ expect ( updateSpy ) . toHaveBeenCalledTimes ( 1 ) ;
169+ expect ( exitSpy ) . not . toHaveBeenCalled ( ) ;
170+ expect ( clip . playing ) . toBe ( true ) ;
171+
172+ clip . set ( { disabled : true } ) ;
173+
174+ expect ( exitSpy ) . toHaveBeenCalledTimes ( 1 ) ;
175+ expect ( clip . playing ) . toBe ( false ) ;
131176 } ) ;
132177
133178 afterEach ( ( ) => {
@@ -143,8 +188,8 @@ describe('The Audio Clip', () => {
143188describe ( 'Copying the AudioClip' , ( ) => {
144189 let clip : AudioClip ;
145190
146- beforeEach ( async ( ) => {
147- clip = await new AudioClip ( ) . load ( file ) ;
191+ beforeEach ( ( ) => {
192+ clip = new AudioClip ( ) ;
148193 } ) ;
149194
150195 it ( 'should transfer audio properties' , async ( ) => {
@@ -180,22 +225,34 @@ describe('Copying the AudioClip', () => {
180225 expect ( copy . id ) . not . toBe ( clip . id ) ;
181226 expect ( copy . track ) . not . toBeDefined ( ) ;
182227 } ) ;
183- } )
184-
185- // copied from src/clips/clip/clip.decorator.spec.ts
186- describe ( 'The render decorator' , ( ) => {
187- it ( 'should not render the compostition if the clip is disabled' , ( ) => {
188- const clip = new AudioClip ( ) ;
228+ } ) ;
189229
190- const unrenderSpy = vi . spyOn ( clip , 'unrender' ) ;
230+ function mockAudioValid ( clip : AudioClip ) {
231+ return vi . spyOn ( clip . element , 'oncanplay' , 'set' )
232+ . mockImplementation ( function ( this : HTMLMediaElement , fn ) {
233+ fn ?. call ( this , new Event ( 'canplay' ) ) ;
234+ } ) ;
235+ }
191236
192- clip . render ( ) ;
237+ function mockDurationValid ( clip : AudioClip ) {
238+ return vi . spyOn ( clip . element , 'duration' , 'get' ) . mockReturnValue ( 20 ) ;
239+ }
193240
194- expect ( unrenderSpy ) . not . toHaveBeenCalled ( ) ;
241+ function mockAudioInvalid ( clip : AudioClip ) {
242+ return vi . spyOn ( clip . element , 'onerror' , 'set' )
243+ . mockImplementation ( function ( this : HTMLMediaElement , fn ) {
244+ fn ?. call ( this , new Event ( 'error' ) ) ;
245+ } ) ;
246+ }
195247
196- clip . set ( { disabled : true } ) ;
197- clip . render ( ) ;
248+ function mockPlayValid ( clip : AudioClip ) {
249+ return vi . spyOn ( clip . element , 'play' ) . mockImplementation ( async ( ) => {
250+ clip . element . dispatchEvent ( new Event ( 'play' ) ) ;
251+ } ) ;
252+ }
198253
199- expect ( unrenderSpy ) . toHaveBeenCalledOnce ( )
254+ function mockPauseValid ( clip : AudioClip ) {
255+ return vi . spyOn ( clip . element , 'pause' ) . mockImplementation ( async ( ) => {
256+ clip . element . dispatchEvent ( new Event ( 'pause' ) ) ;
200257 } ) ;
201- } ) ;
258+ }
0 commit comments