@@ -7,19 +7,41 @@ import { join } from 'path';
7
7
import { inspect } from 'util' ;
8
8
import { expect } from 'vitest' ;
9
9
10
- /** Promise only resolves when fn returns true */
11
- async function waitFor ( fn : ( ) => boolean , timeout = 10_000 , message = 'Timed out waiting' ) : Promise < void > {
12
- let remaining = timeout ;
13
- while ( fn ( ) === false ) {
14
- await new Promise < void > ( resolve => setTimeout ( resolve , 100 ) ) ;
15
- remaining -= 100 ;
16
- if ( remaining < 0 ) {
17
- throw new Error ( message ) ;
18
- }
10
+ const CLEANUP_STEPS = new Set < ( ) => void > ( ) ;
11
+
12
+ export function cleanupChildProcesses ( ) : void {
13
+ for ( const step of CLEANUP_STEPS ) {
14
+ step ( ) ;
19
15
}
16
+ CLEANUP_STEPS . clear ( ) ;
20
17
}
21
18
22
- type VoidFunction = ( ) => void ;
19
+ process . on ( 'exit' , cleanupChildProcesses ) ;
20
+
21
+ function deferredPromise < T = void > (
22
+ done ?: ( ) => void ,
23
+ ) : { resolve : ( val : T ) => void ; reject : ( reason ?: unknown ) => void ; promise : Promise < T > } {
24
+ let resolve ;
25
+ let reject ;
26
+ const promise = new Promise < T > ( ( res , rej ) => {
27
+ resolve = ( val : T ) => {
28
+ done ?.( ) ;
29
+ res ( val ) ;
30
+ } ;
31
+ reject = ( reason : Error ) => {
32
+ done ?.( ) ;
33
+ rej ( reason ) ;
34
+ } ;
35
+ } ) ;
36
+ if ( ! resolve || ! reject ) {
37
+ throw new Error ( 'Failed to create deferred promise' ) ;
38
+ }
39
+ return {
40
+ resolve,
41
+ reject,
42
+ promise,
43
+ } ;
44
+ }
23
45
24
46
type Expected = Envelope | ( ( envelope : Envelope ) => void ) ;
25
47
@@ -41,7 +63,6 @@ export function createRunner(...paths: string[]) {
41
63
throw new Error ( `Test scenario not found: ${ testPath } ` ) ;
42
64
}
43
65
44
- const cleanupSteps = new Set < VoidFunction > ( ) ;
45
66
const expectedEnvelopes : Expected [ ] = [ ] ;
46
67
// By default, we ignore session & sessions
47
68
const ignored : Set < EnvelopeItemType > = new Set ( [ 'session' , 'sessions' , 'client_report' ] ) ;
@@ -68,32 +89,18 @@ export function createRunner(...paths: string[]) {
68
89
return this ;
69
90
} ,
70
91
start : function ( ) : StartResult {
71
- let isComplete = false ;
72
- let completeError : Error | undefined ;
73
-
92
+ const { resolve, reject, promise : isComplete } = deferredPromise ( cleanupChildProcesses ) ;
74
93
const expectedEnvelopeCount = expectedEnvelopes . length ;
75
94
76
95
let envelopeCount = 0 ;
77
- let scenarioServerPort : number | undefined ;
96
+ const { resolve : setWorkerPort , promise : workerPortPromise } = deferredPromise < number > ( ) ;
78
97
let child : ReturnType < typeof spawn > | undefined ;
79
98
80
- function complete ( error ?: Error ) : void {
81
- if ( isComplete ) {
82
- return ;
83
- }
84
-
85
- isComplete = true ;
86
- completeError = error || undefined ;
87
- for ( const step of cleanupSteps ) {
88
- step ( ) ;
89
- }
90
- }
91
-
92
99
/** Called after each expect callback to check if we're complete */
93
100
function expectCallbackCalled ( ) : void {
94
101
envelopeCount ++ ;
95
102
if ( envelopeCount === expectedEnvelopeCount ) {
96
- complete ( ) ;
103
+ resolve ( ) ;
97
104
}
98
105
}
99
106
@@ -121,87 +128,68 @@ export function createRunner(...paths: string[]) {
121
128
}
122
129
expectCallbackCalled ( ) ;
123
130
} catch ( e ) {
124
- complete ( e as Error ) ;
131
+ reject ( e ) ;
125
132
}
126
133
}
127
134
128
135
createBasicSentryServer ( newEnvelope )
129
136
. then ( ( [ mockServerPort , mockServerClose ] ) => {
130
137
if ( mockServerClose ) {
131
- cleanupSteps . add ( ( ) => {
138
+ CLEANUP_STEPS . add ( ( ) => {
132
139
mockServerClose ( ) ;
133
140
} ) ;
134
141
}
135
142
136
- // const env = { ...process.env, ...withEnv, SENTRY_DSN: `http://public@localhost :${mockServerPort}/1337` };
137
-
138
- const SENTRY_DSN = `http://public@localhost:${ mockServerPort } /1337` ;
139
-
140
- if ( process . env . DEBUG ) log ( 'starting scenario' , { testPath, SENTRY_DSN } ) ;
141
-
142
- const wranglerConfigPath = join ( testPath , 'wrangler.jsonc' ) ;
143
-
144
- child = spawn ( 'wrangler' , [ 'dev' , '--config' , wranglerConfigPath , '--var' , `SENTRY_DSN:${ SENTRY_DSN } ` ] ) ;
143
+ if ( process . env . DEBUG ) log ( 'Starting scenario' , testPath ) ;
144
+
145
+ const stdio : ( 'inherit' | 'ipc' | 'ignore' ) [ ] = process . env . DEBUG
146
+ ? [ 'inherit' , 'inherit' , 'inherit' , 'ipc' ]
147
+ : [ 'ignore' , 'ignore' , 'ignore' , 'ipc' ] ;
148
+
149
+ child = spawn (
150
+ 'wrangler' ,
151
+ [
152
+ 'dev' ,
153
+ '--config' ,
154
+ join ( testPath , 'wrangler.jsonc' ) ,
155
+ '--show-interactive-dev-session' ,
156
+ 'false' ,
157
+ '--var' ,
158
+ `SENTRY_DSN:http://public@localhost:${ mockServerPort } /1337` ,
159
+ ] ,
160
+ { stdio } ,
161
+ ) ;
162
+
163
+ CLEANUP_STEPS . add ( ( ) => {
164
+ child ?. kill ( ) ;
165
+ } ) ;
145
166
146
167
child . on ( 'error' , e => {
147
168
// eslint-disable-next-line no-console
148
169
console . error ( 'Error starting child process:' , e ) ;
149
- complete ( e ) ;
170
+ reject ( e ) ;
150
171
} ) ;
151
172
152
- cleanupSteps . add ( ( ) => {
153
- child ?. kill ( ) ;
154
- } ) ;
155
-
156
- if ( process . env . DEBUG ) {
157
- child . stderr ?. on ( 'data' , ( data : Buffer ) => {
158
- log ( 'stderr line' , data . toString ( ) ) ;
159
- } ) ;
160
- }
161
-
162
- child . stdout ?. on ( 'data' , ( data : Buffer ) => {
163
- if ( scenarioServerPort === undefined ) {
164
- const line = data . toString ( ) ;
165
- const result = line . match ( / R e a d y o n h t t p : \/ \/ l o c a l h o s t : ( \d + ) / ) ;
166
- if ( result ?. [ 1 ] ) {
167
- scenarioServerPort = parseInt ( result [ 1 ] , 10 ) ;
168
- }
169
- }
170
-
171
- if ( process . env . DEBUG ) {
172
- log ( 'stdout line' , data . toString ( ) ) ;
173
+ child . on ( 'message' , ( message : string ) => {
174
+ const msg = JSON . parse ( message ) as { event : string ; port ?: number } ;
175
+ if ( msg . event === 'DEV_SERVER_READY' && typeof msg . port === 'number' ) {
176
+ setWorkerPort ( msg . port ) ;
177
+ if ( process . env . DEBUG ) log ( 'worker ready on port' , msg . port ) ;
173
178
}
174
179
} ) ;
175
-
176
- // Pass error to done to end the test quickly
177
- child . on ( 'error' , e => {
178
- if ( process . env . DEBUG ) log ( 'scenario error' , e ) ;
179
- complete ( e ) ;
180
- } ) ;
181
180
} )
182
- . catch ( e => complete ( e ) ) ;
181
+ . catch ( e => reject ( e ) ) ;
183
182
184
183
return {
185
184
completed : async function ( ) : Promise < void > {
186
- await waitFor ( ( ) => isComplete , 120_000 , 'Timed out waiting for test to complete' ) ;
187
-
188
- if ( completeError ) {
189
- throw completeError ;
190
- }
185
+ return isComplete ;
191
186
} ,
192
187
makeRequest : async function < T > (
193
188
method : 'get' | 'post' ,
194
189
path : string ,
195
190
options : { headers ?: Record < string , string > ; data ?: BodyInit ; expectError ?: boolean } = { } ,
196
191
) : Promise < T | undefined > {
197
- try {
198
- await waitFor ( ( ) => scenarioServerPort !== undefined , 10_000 , 'Timed out waiting for server port' ) ;
199
- } catch ( e ) {
200
- complete ( e as Error ) ;
201
- return ;
202
- }
203
-
204
- const url = `http://localhost:${ scenarioServerPort } ${ path } ` ;
192
+ const url = `http://localhost:${ await workerPortPromise } ${ path } ` ;
205
193
const body = options . data ;
206
194
const headers = options . headers || { } ;
207
195
const expectError = options . expectError || false ;
@@ -213,14 +201,14 @@ export function createRunner(...paths: string[]) {
213
201
214
202
if ( ! res . ok ) {
215
203
if ( ! expectError ) {
216
- complete ( new Error ( `Expected request to "${ path } " to succeed, but got a ${ res . status } response` ) ) ;
204
+ reject ( new Error ( `Expected request to "${ path } " to succeed, but got a ${ res . status } response` ) ) ;
217
205
}
218
206
219
207
return ;
220
208
}
221
209
222
210
if ( expectError ) {
223
- complete ( new Error ( `Expected request to "${ path } " to fail, but got a ${ res . status } response` ) ) ;
211
+ reject ( new Error ( `Expected request to "${ path } " to fail, but got a ${ res . status } response` ) ) ;
224
212
return ;
225
213
}
226
214
@@ -234,7 +222,7 @@ export function createRunner(...paths: string[]) {
234
222
return ;
235
223
}
236
224
237
- complete ( e as Error ) ;
225
+ reject ( e ) ;
238
226
return ;
239
227
}
240
228
} ,
0 commit comments