3
3
* input configuration and launching test runners.
4
4
*/
5
5
import * as fs from 'fs' ;
6
- import * as q from 'q' ;
7
6
8
7
import { Config } from './config' ;
9
8
import { ConfigParser } from './configParser' ;
10
9
import { ConfigError , ErrorHandler , ProtractorError } from './exitCodes' ;
11
10
import { Logger } from './logger' ;
12
- import { Runner } from './runner' ;
13
11
import { TaskRunner } from './taskRunner' ;
14
12
import { TaskScheduler } from './taskScheduler' ;
15
- import * as helper from './util' ;
13
+ import { runFilenameOrFn_ } from './util' ;
14
+
16
15
17
16
let logger = new Logger ( 'launcher' ) ;
18
17
let RUNNERS_FAILED_EXIT_CODE = 100 ;
@@ -93,7 +92,7 @@ let taskResults_ = new TaskResults();
93
92
* @param {string= } configFile
94
93
* @param {Object= } additionalConfig
95
94
*/
96
- let initFn = function ( configFile : string , additionalConfig : Config ) {
95
+ let initFn = async function ( configFile : string , additionalConfig : Config ) {
97
96
let configParser = new ConfigParser ( ) ;
98
97
if ( configFile ) {
99
98
configParser . addFileConfig ( configFile ) ;
@@ -108,197 +107,159 @@ let initFn = function(configFile: string, additionalConfig: Config) {
108
107
logger . debug ( 'Your base url for tests is ' + config . baseUrl ) ;
109
108
110
109
// Run beforeLaunch
111
- helper . runFilenameOrFn_ ( config . configDir , config . beforeLaunch )
112
- . then ( ( ) => {
110
+ await runFilenameOrFn_ ( config . configDir , config . beforeLaunch ) ;
111
+ // 1) If getMultiCapabilities is set, resolve that as
112
+ // `multiCapabilities`.
113
+ if ( config . getMultiCapabilities && typeof config . getMultiCapabilities === 'function' ) {
114
+ if ( config . multiCapabilities . length || config . capabilities ) {
115
+ logger . warn (
116
+ 'getMultiCapabilities() will override both capabilities ' +
117
+ 'and multiCapabilities' ) ;
118
+ }
119
+ // If getMultiCapabilities is defined and a function, use this.
120
+ const waitMultiConfig = await config . getMultiCapabilities ( ) ;
121
+ config . multiCapabilities = waitMultiConfig ;
122
+ config . capabilities = null ;
123
+ }
113
124
114
- return q
115
- . Promise < any > ( ( resolve : Function , reject : Function ) => {
116
- // 1) If getMultiCapabilities is set, resolve that as
117
- // `multiCapabilities`.
118
- if ( config . getMultiCapabilities &&
119
- typeof config . getMultiCapabilities === 'function' ) {
120
- if ( config . multiCapabilities . length || config . capabilities ) {
121
- logger . warn (
122
- 'getMultiCapabilities() will override both capabilities ' +
123
- 'and multiCapabilities' ) ;
124
- }
125
- // If getMultiCapabilities is defined and a function, use this.
126
- q ( config . getMultiCapabilities ( ) )
127
- . then ( ( multiCapabilities ) => {
128
- config . multiCapabilities = multiCapabilities ;
129
- config . capabilities = null ;
130
- } )
131
- . then ( ( ) => {
132
- resolve ( ) ;
133
- } )
134
- . catch ( err => {
135
- reject ( err ) ;
136
- } ) ;
137
- } else {
138
- resolve ( ) ;
139
- }
140
- } )
141
- . then ( ( ) => {
142
- // 2) Set `multicapabilities` using `capabilities`,
143
- // `multicapabilities`,
144
- // or default
145
- if ( config . capabilities ) {
146
- if ( config . multiCapabilities . length ) {
147
- logger . warn (
148
- 'You have specified both capabilities and ' +
149
- 'multiCapabilities. This will result in capabilities being ' +
150
- 'ignored' ) ;
151
- } else {
152
- // Use capabilities if multiCapabilities is empty.
153
- config . multiCapabilities = [ config . capabilities ] ;
154
- }
155
- } else if ( ! config . multiCapabilities . length ) {
156
- // Default to chrome if no capabilities given
157
- config . multiCapabilities = [ { browserName : 'chrome' } ] ;
158
- }
159
- } ) ;
160
- } )
161
- . then ( ( ) => {
162
- // 3) If we're in `elementExplorer` mode, run only that.
163
- if ( config . elementExplorer || config . framework === 'explorer' ) {
164
- if ( config . multiCapabilities . length != 1 ) {
165
- throw new Error ( 'Must specify only 1 browser while using elementExplorer' ) ;
166
- } else {
167
- config . capabilities = config . multiCapabilities [ 0 ] ;
168
- }
169
- config . framework = 'explorer' ;
125
+ // 2) Set `multicapabilities` using `capabilities`,
126
+ // `multicapabilities`, or default
127
+ if ( config . capabilities ) {
128
+ if ( config . multiCapabilities . length ) {
129
+ logger . warn (
130
+ 'You have specified both capabilities and ' +
131
+ 'multiCapabilities. This will result in capabilities being ' +
132
+ 'ignored' ) ;
133
+ } else {
134
+ // Use capabilities if multiCapabilities is empty.
135
+ config . multiCapabilities = [ config . capabilities ] ;
136
+ }
137
+ } else if ( ! config . multiCapabilities . length ) {
138
+ // Default to chrome if no capabilities given
139
+ config . multiCapabilities = [ { browserName : 'chrome' } ] ;
140
+ }
170
141
171
- let runner = new Runner ( config ) ;
172
- return runner . run ( ) . then (
173
- ( exitCode : number ) => {
174
- process . exit ( exitCode ) ;
175
- } ,
176
- ( err : Error ) => {
177
- logger . error ( err ) ;
178
- process . exit ( 1 ) ;
179
- } ) ;
180
- }
181
- } )
182
- . then ( ( ) => {
183
- // 4) Run tests.
184
- let scheduler = new TaskScheduler ( config ) ;
142
+ // 3) If we're in `elementExplorer` mode, throw an error and exit.
143
+ if ( config . elementExplorer || config . framework === 'explorer' ) {
144
+ const err = new Error (
145
+ 'Deprecated: Element explorer depends on the ' +
146
+ 'WebDriver control flow, and thus is no longer supported.' ) ;
147
+ logger . error ( err ) ;
148
+ process . exit ( 1 ) ;
149
+ }
185
150
186
- process . on ( 'uncaughtException' , ( exc : ( Error | string ) ) => {
187
- let e = ( exc instanceof Error ) ? exc : new Error ( exc ) ;
188
- if ( config . ignoreUncaughtExceptions ) {
189
- // This can be a sign of a bug in the test framework, that it may
190
- // not be handling WebDriver errors properly. However, we don't
191
- // want these errors to prevent running the tests.
192
- logger . warn ( 'Ignoring uncaught error ' + exc ) ;
193
- return ;
194
- }
151
+ // 4) Run tests.
152
+ let scheduler = new TaskScheduler ( config ) ;
195
153
196
- let errorCode = ErrorHandler . parseError ( e ) ;
197
- if ( errorCode ) {
198
- let protractorError = e as ProtractorError ;
199
- ProtractorError . log ( logger , errorCode , protractorError . message , protractorError . stack ) ;
200
- process . exit ( errorCode ) ;
201
- } else {
202
- logger . error ( e . message ) ;
203
- logger . error ( e . stack ) ;
204
- process . exit ( ProtractorError . CODE ) ;
205
- }
206
- } ) ;
154
+ process . on ( 'uncaughtException' , ( exc : ( Error | string ) ) => {
155
+ let e = ( exc instanceof Error ) ? exc : new Error ( exc ) ;
156
+ if ( config . ignoreUncaughtExceptions ) {
157
+ // This can be a sign of a bug in the test framework, that it may
158
+ // not be handling WebDriver errors properly. However, we don't
159
+ // want these errors to prevent running the tests.
160
+ logger . warn ( 'Ignoring uncaught error ' + exc ) ;
161
+ return ;
162
+ }
163
+ logger . error ( e . message ) ;
164
+ logger . error ( e . stack ) ;
165
+ if ( e instanceof ProtractorError ) {
166
+ let protractorError = e as ProtractorError ;
167
+ process . exit ( protractorError . code ) ;
168
+ } else {
169
+ process . exit ( 1 ) ;
170
+ }
171
+ } ) ;
207
172
208
- process . on ( 'exit' , ( code : number ) => {
209
- if ( code ) {
210
- logger . error ( 'Process exited with error code ' + code ) ;
211
- } else if ( scheduler . numTasksOutstanding ( ) > 0 ) {
212
- logger . error (
213
- 'BUG: launcher exited with ' + scheduler . numTasksOutstanding ( ) +
214
- ' tasks remaining' ) ;
215
- process . exit ( RUNNERS_FAILED_EXIT_CODE ) ;
216
- }
217
- } ) ;
173
+ process . on ( 'unhandledRejection' , ( reason : Error | any , p : Promise < any > ) => {
174
+ logger . warn ( 'Unhandled rejection at:' , p , 'reason:' , reason ) ;
175
+ } ) ;
218
176
219
- // Run afterlaunch and exit
220
- let cleanUpAndExit = ( exitCode : number ) => {
221
- return helper . runFilenameOrFn_ ( config . configDir , config . afterLaunch , [ exitCode ] )
222
- . then (
223
- ( returned ) => {
224
- if ( typeof returned === 'number' ) {
225
- process . exit ( returned ) ;
226
- } else {
227
- process . exit ( exitCode ) ;
228
- }
229
- } ,
230
- ( err : Error ) => {
231
- logger . error ( 'Error:' , err ) ;
232
- process . exit ( 1 ) ;
233
- } ) ;
234
- } ;
177
+ process . on ( 'exit' , ( code : number ) => {
178
+ if ( code ) {
179
+ logger . error ( 'Process exited with error code ' + code ) ;
180
+ } else if ( scheduler . numTasksOutstanding ( ) > 0 ) {
181
+ logger . error (
182
+ 'BUG: launcher exited with ' + scheduler . numTasksOutstanding ( ) + ' tasks remaining' ) ;
183
+ process . exit ( RUNNERS_FAILED_EXIT_CODE ) ;
184
+ }
185
+ } ) ;
235
186
236
- let totalTasks = scheduler . numTasksOutstanding ( ) ;
237
- let forkProcess = false ;
238
- if ( totalTasks > 1 ) { // Start new processes only if there are >1 tasks.
239
- forkProcess = true ;
240
- if ( config . debug ) {
241
- throw new ConfigError (
242
- logger , 'Cannot run in debug mode with multiCapabilities, count > 1, or sharding' ) ;
243
- }
244
- }
187
+ // Run afterlaunch and exit
188
+ const cleanUpAndExit = async ( exitCode : number ) => {
189
+ try {
190
+ const returned = await runFilenameOrFn_ ( config . configDir , config . afterLaunch , [ exitCode ] ) ;
191
+ if ( typeof returned === 'number' ) {
192
+ process . exit ( returned ) ;
193
+ } else {
194
+ process . exit ( exitCode ) ;
195
+ }
196
+ } catch ( err ) {
197
+ logger . error ( 'Error:' , err ) ;
198
+ process . exit ( 1 ) ;
199
+ }
200
+ } ;
245
201
246
- let deferred = q . defer < any > ( ) ; // Resolved when all tasks are completed
247
- let createNextTaskRunner = ( ) => {
248
- let task = scheduler . nextTask ( ) ;
249
- if ( task ) {
250
- let taskRunner = new TaskRunner ( configFile , additionalConfig , task , forkProcess ) ;
251
- taskRunner . run ( )
252
- . then ( ( result ) => {
253
- if ( result . exitCode && ! result . failedCount ) {
254
- logger . error (
255
- 'Runner process exited unexpectedly with error code: ' + result . exitCode ) ;
256
- }
257
- taskResults_ . add ( result ) ;
258
- task . done ( ) ;
259
- createNextTaskRunner ( ) ;
260
- // If all tasks are finished
261
- if ( scheduler . numTasksOutstanding ( ) === 0 ) {
262
- deferred . resolve ( ) ;
263
- }
264
- logger . info (
265
- scheduler . countActiveTasks ( ) + ' instance(s) of WebDriver still running' ) ;
266
- } )
267
- . catch ( ( err : Error ) => {
268
- logger . error ( 'Error:' , ( err as any ) . stack || err . message || err ) ;
269
- cleanUpAndExit ( RUNNERS_FAILED_EXIT_CODE ) ;
270
- } ) ;
202
+ const totalTasks = scheduler . numTasksOutstanding ( ) ;
203
+ let forkProcess = false ;
204
+ if ( totalTasks > 1 ) { // Start new processes only if there are >1 tasks.
205
+ forkProcess = true ;
206
+ if ( config . debug ) {
207
+ throw new ConfigError (
208
+ logger , 'Cannot run in debug mode with multiCapabilities, count > 1, or sharding' ) ;
209
+ }
210
+ }
211
+
212
+ const createNextTaskRunner = async ( ) => {
213
+ return new Promise ( async ( resolve ) => {
214
+ const task = scheduler . nextTask ( ) ;
215
+ if ( task ) {
216
+ const taskRunner = new TaskRunner ( configFile , additionalConfig , task , forkProcess ) ;
217
+ try {
218
+ const result = await taskRunner . run ( ) ;
219
+ if ( result . exitCode && ! result . failedCount ) {
220
+ logger . error ( 'Runner process exited unexpectedly with error code: ' + result . exitCode ) ;
271
221
}
272
- } ;
273
- // Start `scheduler.maxConcurrentTasks()` workers for handling tasks in
274
- // the beginning. As a worker finishes a task, it will pick up the next
275
- // task
276
- // from the scheduler's queue until all tasks are gone.
277
- for ( let i = 0 ; i < scheduler . maxConcurrentTasks ( ) ; ++ i ) {
222
+ taskResults_ . add ( result ) ;
223
+ task . done ( ) ;
278
224
createNextTaskRunner ( ) ;
225
+ // If all tasks are finished
226
+ if ( scheduler . numTasksOutstanding ( ) === 0 ) {
227
+ resolve ( ) ;
228
+ }
229
+ logger . info ( scheduler . countActiveTasks ( ) + ' instance(s) of WebDriver still running' ) ;
230
+ } catch ( err ) {
231
+ const errorCode = ErrorHandler . parseError ( err ) ;
232
+ logger . error ( 'Error:' , ( err as any ) . stack || err . message || err ) ;
233
+ await cleanUpAndExit ( errorCode ? errorCode : RUNNERS_FAILED_EXIT_CODE ) ;
279
234
}
280
- logger . info ( 'Running ' + scheduler . countActiveTasks ( ) + ' instances of WebDriver' ) ;
235
+ }
236
+ } ) ;
237
+ } ;
238
+
239
+ const maxConcurrentTasks = scheduler . maxConcurrentTasks ( ) ;
240
+ for ( let i = 0 ; i < maxConcurrentTasks ; ++ i ) {
241
+ await createNextTaskRunner ( ) ;
242
+ }
243
+ logger . info ( 'Running ' + scheduler . countActiveTasks ( ) + ' instances of WebDriver' ) ;
281
244
282
- // By now all runners have completed.
283
- deferred . promise
284
- . then ( function ( ) {
285
- // Save results if desired
286
- if ( config . resultJsonOutputFile ) {
287
- taskResults_ . saveResults ( config . resultJsonOutputFile ) ;
288
- }
245
+ // By now all runners have completed.
246
+ // Save results if desired
247
+ if ( config . resultJsonOutputFile ) {
248
+ taskResults_ . saveResults ( config . resultJsonOutputFile ) ;
249
+ }
250
+
251
+ taskResults_ . reportSummary ( ) ;
252
+ let exitCode = 0 ;
253
+ if ( taskResults_ . totalProcessFailures ( ) > 0 ) {
254
+ exitCode = RUNNERS_FAILED_EXIT_CODE ;
255
+ } else if ( taskResults_ . totalSpecFailures ( ) > 0 ) {
256
+ exitCode = 1 ;
257
+ }
258
+ await cleanUpAndExit ( exitCode ) ;
259
+ // Start `const maxConcurrentTasks` workers for handling tasks in
260
+ // the beginning. As a worker finishes a task, it will pick up the next
261
+ // task from the scheduler's queue until all tasks are gone.
289
262
290
- taskResults_ . reportSummary ( ) ;
291
- let exitCode = 0 ;
292
- if ( taskResults_ . totalProcessFailures ( ) > 0 ) {
293
- exitCode = RUNNERS_FAILED_EXIT_CODE ;
294
- } else if ( taskResults_ . totalSpecFailures ( ) > 0 ) {
295
- exitCode = 1 ;
296
- }
297
- return cleanUpAndExit ( exitCode ) ;
298
- } )
299
- . done ( ) ;
300
- } )
301
- . done ( ) ;
302
263
} ;
303
264
304
265
export let init = initFn ;
0 commit comments