1+ /** 
2+  * Copyright 2025, Optimizely 
3+  * 
4+  * Licensed under the Apache License, Version 2.0 (the "License"); 
5+  * you may not use this file except in compliance with the License. 
6+  * You may obtain a copy of the License at 
7+  * 
8+  * https://www.apache.org/licenses/LICENSE-2.0 
9+  * 
10+  * Unless required by applicable law or agreed to in writing, software 
11+  * distributed under the License is distributed on an "AS IS" BASIS, 
12+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13+  * See the License for the specific language governing permissions and 
14+  * limitations under the License. 
15+  */ 
16+ import  {  describe ,  it ,  expect ,  beforeEach  }  from  'vitest' ; 
17+ 
18+ import  {  SerialRunner  }  from  './serial_runner' ; 
19+ import  {  resolvablePromise  }  from  '../promise/resolvablePromise' ; 
20+ import  {  exhaustMicrotasks  }  from  '../../tests/testUtils' ; 
21+ 
22+ describe ( 'SerialRunner' ,  ( )  =>  { 
23+   let  serialRunner : SerialRunner ; 
24+ 
25+   beforeEach ( ( )  =>  { 
26+     serialRunner  =  new  SerialRunner ( ) ; 
27+   } ) ; 
28+ 
29+   it ( 'should return result from a single async function' ,  async  ( )  =>  { 
30+     const  fn  =  ( )  =>  Promise . resolve ( 'result' ) ; 
31+     
32+     const  result  =  await  serialRunner . run ( fn ) ; 
33+     
34+     expect ( result ) . toBe ( 'result' ) ; 
35+   } ) ; 
36+ 
37+   it ( 'should reject with same error when the passed function rejects' ,  async  ( )  =>  { 
38+     const  error  =  new  Error ( 'test error' ) ; 
39+     const  fn  =  ( )  =>  Promise . reject ( error ) ; 
40+     
41+     await  expect ( serialRunner . run ( fn ) ) . rejects . toThrow ( error ) ; 
42+   } ) ; 
43+ 
44+  it ( 'should execute multiple async functions in order' ,  async  ( )  =>  { 
45+     const  executionOrder : number [ ]  =  [ ] ; 
46+     const  promises  =  [ resolvablePromise ( ) ,  resolvablePromise ( ) ,  resolvablePromise ( ) ] ; 
47+ 
48+     const  createTask  =  ( id : number )  =>  async  ( )  =>  { 
49+       executionOrder . push ( id ) ; 
50+       await  promises [ id ] ; 
51+       return  id ; 
52+     } ; 
53+ 
54+     const  results  =  [ serialRunner . run ( createTask ( 0 ) ) ,  serialRunner . run ( createTask ( 1 ) ) ,  serialRunner . run ( createTask ( 2 ) ) ] ; 
55+ 
56+     // only first task should have started 
57+     await  exhaustMicrotasks ( ) ; 
58+     expect ( executionOrder ) . toEqual ( [ 0 ] ) ; 
59+ 
60+     // Resolve first task - second should start 
61+     promises [ 0 ] . resolve ( '' ) ; 
62+     await  exhaustMicrotasks ( ) ; 
63+     expect ( executionOrder ) . toEqual ( [ 0 ,  1 ] ) ; 
64+ 
65+     // Resolve second task - third should start 
66+     promises [ 1 ] . resolve ( '' ) ; 
67+     await  exhaustMicrotasks ( ) ; 
68+     expect ( executionOrder ) . toEqual ( [ 0 ,  1 ,  2 ] ) ; 
69+ 
70+     // Resolve third task - all done 
71+     promises [ 2 ] . resolve ( '' ) ; 
72+ 
73+     // Verify all results are correct 
74+     expect ( await  results [ 0 ] ) . toBe ( 0 ) ; 
75+     expect ( await  results [ 1 ] ) . toBe ( 1 ) ; 
76+     expect ( await  results [ 2 ] ) . toBe ( 2 ) ; 
77+   } ) ; 
78+ 
79+   it ( 'should continue execution even if one function throws an error' ,  async  ( )  =>  { 
80+     const  executionOrder : number [ ]  =  [ ] ; 
81+     const  promises  =  [ resolvablePromise ( ) ,  resolvablePromise ( ) ,  resolvablePromise ( ) ] ; 
82+ 
83+     const  createTask  =  ( id : number )  =>  async  ( )  =>  { 
84+       executionOrder . push ( id ) ; 
85+       await  promises [ id ] ; 
86+       return  id ; 
87+     } ; 
88+ 
89+     const  results  =  [ serialRunner . run ( createTask ( 0 ) ) ,  serialRunner . run ( createTask ( 1 ) ) ,  serialRunner . run ( createTask ( 2 ) ) ] ; 
90+ 
91+     // only first task should have started 
92+     await  exhaustMicrotasks ( ) ; 
93+     expect ( executionOrder ) . toEqual ( [ 0 ] ) ; 
94+ 
95+     // reject first task - second should still start 
96+     promises [ 0 ] . reject ( new  Error ( 'first error' ) ) ; 
97+     await  exhaustMicrotasks ( ) ; 
98+     expect ( executionOrder ) . toEqual ( [ 0 ,  1 ] ) ; 
99+ 
100+     // reject second task - third should still start 
101+     promises [ 1 ] . reject ( new  Error ( 'second error' ) ) ; 
102+     await  exhaustMicrotasks ( ) ; 
103+     expect ( executionOrder ) . toEqual ( [ 0 ,  1 ,  2 ] ) ; 
104+ 
105+     // Resolve third task - all done 
106+     promises [ 2 ] . resolve ( '' ) ; 
107+ 
108+     // Verify results - first and third succeed, second fails 
109+     await  expect ( results [ 0 ] ) . rejects . toThrow ( 'first error' ) ; 
110+     await  expect ( results [ 1 ] ) . rejects . toThrow ( 'second error' ) ; 
111+     await  expect ( results [ 2 ] ) . resolves . toBe ( 2 ) ; 
112+   } ) ; 
113+ 
114+   it ( 'should handle functions that return different types' ,  async  ( )  =>  { 
115+     const  numberFn  =  ( )  =>  Promise . resolve ( 42 ) ; 
116+     const  stringFn  =  ( )  =>  Promise . resolve ( 'hello' ) ; 
117+     const  objectFn  =  ( )  =>  Promise . resolve ( {  key : 'value'  } ) ; 
118+     const  arrayFn  =  ( )  =>  Promise . resolve ( [ 1 ,  2 ,  3 ] ) ; 
119+     const  booleanFn  =  ( )  =>  Promise . resolve ( true ) ; 
120+     const  nullFn  =  ( )  =>  Promise . resolve ( null ) ; 
121+     const  undefinedFn  =  ( )  =>  Promise . resolve ( undefined ) ; 
122+ 
123+     const  results  =  await  Promise . all ( [ 
124+       serialRunner . run ( numberFn ) , 
125+       serialRunner . run ( stringFn ) , 
126+       serialRunner . run ( objectFn ) , 
127+       serialRunner . run ( arrayFn ) , 
128+       serialRunner . run ( booleanFn ) , 
129+       serialRunner . run ( nullFn ) , 
130+       serialRunner . run ( undefinedFn ) , 
131+     ] ) ; 
132+ 
133+     expect ( results ) . toEqual ( [ 42 ,  'hello' ,  {  key : 'value'  } ,  [ 1 ,  2 ,  3 ] ,  true ,  null ,  undefined ] ) ; 
134+   } ) ; 
135+ 
136+   it ( 'should handle empty function that returns undefined' ,  async  ( )  =>  { 
137+     const  emptyFn  =  ( )  =>  Promise . resolve ( undefined ) ; 
138+     
139+     const  result  =  await  serialRunner . run ( emptyFn ) ; 
140+     
141+     expect ( result ) . toBeUndefined ( ) ; 
142+   } ) ; 
143+ } ) ; 
0 commit comments