1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | const fs = require("fs");
|
8 | const q = require("q");
|
9 | const configParser_1 = require("./configParser");
|
10 | const exitCodes_1 = require("./exitCodes");
|
11 | const logger_1 = require("./logger");
|
12 | const runner_1 = require("./runner");
|
13 | const taskRunner_1 = require("./taskRunner");
|
14 | const taskScheduler_1 = require("./taskScheduler");
|
15 | const helper = require("./util");
|
16 | let logger = new logger_1.Logger('launcher');
|
17 | let RUNNERS_FAILED_EXIT_CODE = 100;
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | class TaskResults {
|
24 | constructor() {
|
25 |
|
26 | this.results_ = [];
|
27 | }
|
28 | add(result) {
|
29 | this.results_.push(result);
|
30 | }
|
31 | totalSpecFailures() {
|
32 | return this.results_.reduce((specFailures, result) => {
|
33 | return specFailures + result.failedCount;
|
34 | }, 0);
|
35 | }
|
36 | totalProcessFailures() {
|
37 | return this.results_.reduce((processFailures, result) => {
|
38 | return !result.failedCount && result.exitCode !== 0 ? processFailures + 1 : processFailures;
|
39 | }, 0);
|
40 | }
|
41 | saveResults(filepath) {
|
42 | let jsonOutput = this.results_.reduce((jsonOutput, result) => {
|
43 | return jsonOutput.concat(result.specResults);
|
44 | }, []);
|
45 | let json = JSON.stringify(jsonOutput, null, ' ');
|
46 | fs.writeFileSync(filepath, json);
|
47 | }
|
48 | reportSummary() {
|
49 | let specFailures = this.totalSpecFailures();
|
50 | let processFailures = this.totalProcessFailures();
|
51 | this.results_.forEach((result) => {
|
52 | let capabilities = result.capabilities;
|
53 | let shortName = (capabilities.browserName) ? capabilities.browserName : '';
|
54 | shortName = (capabilities.logName) ?
|
55 | capabilities.logName :
|
56 | (capabilities.browserName) ? capabilities.browserName : '';
|
57 | shortName += (capabilities.version) ? capabilities.version : '';
|
58 | shortName += (capabilities.logName && capabilities.count < 2) ? '' : ' #' + result.taskId;
|
59 | if (result.failedCount) {
|
60 | logger.info(shortName + ' failed ' + result.failedCount + ' test(s)');
|
61 | }
|
62 | else if (result.exitCode !== 0) {
|
63 | logger.info(shortName + ' failed with exit code: ' + result.exitCode);
|
64 | }
|
65 | else {
|
66 | logger.info(shortName + ' passed');
|
67 | }
|
68 | });
|
69 | if (specFailures && processFailures) {
|
70 | logger.info('overall: ' + specFailures + ' failed spec(s) and ' + processFailures +
|
71 | ' process(es) failed to complete');
|
72 | }
|
73 | else if (specFailures) {
|
74 | logger.info('overall: ' + specFailures + ' failed spec(s)');
|
75 | }
|
76 | else if (processFailures) {
|
77 | logger.info('overall: ' + processFailures + ' process(es) failed to complete');
|
78 | }
|
79 | }
|
80 | }
|
81 | let taskResults_ = new TaskResults();
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | let initFn = function (configFile, additionalConfig) {
|
91 | let configParser = new configParser_1.ConfigParser();
|
92 | if (configFile) {
|
93 | configParser.addFileConfig(configFile);
|
94 | }
|
95 | if (additionalConfig) {
|
96 | configParser.addConfig(additionalConfig);
|
97 | }
|
98 | let config = configParser.getConfig();
|
99 | logger_1.Logger.set(config);
|
100 | logger.debug('Running with --troubleshoot');
|
101 | logger.debug('Protractor version: ' + require('../package.json').version);
|
102 | logger.debug('Your base url for tests is ' + config.baseUrl);
|
103 |
|
104 | helper.runFilenameOrFn_(config.configDir, config.beforeLaunch)
|
105 | .then(() => {
|
106 | return q
|
107 | .Promise((resolve, reject) => {
|
108 |
|
109 |
|
110 | if (config.getMultiCapabilities &&
|
111 | typeof config.getMultiCapabilities === 'function') {
|
112 | if (config.multiCapabilities.length || config.capabilities) {
|
113 | logger.warn('getMultiCapabilities() will override both capabilities ' +
|
114 | 'and multiCapabilities');
|
115 | }
|
116 |
|
117 | q(config.getMultiCapabilities())
|
118 | .then((multiCapabilities) => {
|
119 | config.multiCapabilities = multiCapabilities;
|
120 | config.capabilities = null;
|
121 | })
|
122 | .then(() => {
|
123 | resolve();
|
124 | })
|
125 | .catch(err => {
|
126 | reject(err);
|
127 | });
|
128 | }
|
129 | else {
|
130 | resolve();
|
131 | }
|
132 | })
|
133 | .then(() => {
|
134 |
|
135 |
|
136 |
|
137 | if (config.capabilities) {
|
138 | if (config.multiCapabilities.length) {
|
139 | logger.warn('You have specified both capabilities and ' +
|
140 | 'multiCapabilities. This will result in capabilities being ' +
|
141 | 'ignored');
|
142 | }
|
143 | else {
|
144 |
|
145 | config.multiCapabilities = [config.capabilities];
|
146 | }
|
147 | }
|
148 | else if (!config.multiCapabilities.length) {
|
149 |
|
150 | config.multiCapabilities = [{ browserName: 'chrome' }];
|
151 | }
|
152 | });
|
153 | })
|
154 | .then(() => {
|
155 |
|
156 | if (config.elementExplorer || config.framework === 'explorer') {
|
157 | if (config.multiCapabilities.length != 1) {
|
158 | throw new Error('Must specify only 1 browser while using elementExplorer');
|
159 | }
|
160 | else {
|
161 | config.capabilities = config.multiCapabilities[0];
|
162 | }
|
163 | config.framework = 'explorer';
|
164 | let runner = new runner_1.Runner(config);
|
165 | return runner.run().then((exitCode) => {
|
166 | process.exit(exitCode);
|
167 | }, (err) => {
|
168 | logger.error(err);
|
169 | process.exit(1);
|
170 | });
|
171 | }
|
172 | })
|
173 | .then(() => {
|
174 |
|
175 | let scheduler = new taskScheduler_1.TaskScheduler(config);
|
176 | process.on('uncaughtException', (exc) => {
|
177 | let e = (exc instanceof Error) ? exc : new Error(exc);
|
178 | if (config.ignoreUncaughtExceptions) {
|
179 |
|
180 |
|
181 |
|
182 | logger.warn('Ignoring uncaught error ' + exc);
|
183 | return;
|
184 | }
|
185 | let errorCode = exitCodes_1.ErrorHandler.parseError(e);
|
186 | if (errorCode) {
|
187 | let protractorError = e;
|
188 | exitCodes_1.ProtractorError.log(logger, errorCode, protractorError.message, protractorError.stack);
|
189 | process.exit(errorCode);
|
190 | }
|
191 | else {
|
192 | logger.error(e.message);
|
193 | logger.error(e.stack);
|
194 | process.exit(exitCodes_1.ProtractorError.CODE);
|
195 | }
|
196 | });
|
197 | process.on('exit', (code) => {
|
198 | if (code) {
|
199 | logger.error('Process exited with error code ' + code);
|
200 | }
|
201 | else if (scheduler.numTasksOutstanding() > 0) {
|
202 | logger.error('BUG: launcher exited with ' + scheduler.numTasksOutstanding() +
|
203 | ' tasks remaining');
|
204 | process.exit(RUNNERS_FAILED_EXIT_CODE);
|
205 | }
|
206 | });
|
207 |
|
208 | let cleanUpAndExit = (exitCode) => {
|
209 | return helper.runFilenameOrFn_(config.configDir, config.afterLaunch, [exitCode])
|
210 | .then((returned) => {
|
211 | if (typeof returned === 'number') {
|
212 | process.exit(returned);
|
213 | }
|
214 | else {
|
215 | process.exit(exitCode);
|
216 | }
|
217 | }, (err) => {
|
218 | logger.error('Error:', err);
|
219 | process.exit(1);
|
220 | });
|
221 | };
|
222 | let totalTasks = scheduler.numTasksOutstanding();
|
223 | let forkProcess = false;
|
224 | if (totalTasks > 1) {
|
225 | forkProcess = true;
|
226 | if (config.debug) {
|
227 | throw new exitCodes_1.ConfigError(logger, 'Cannot run in debug mode with multiCapabilities, count > 1, or sharding');
|
228 | }
|
229 | }
|
230 | let deferred = q.defer();
|
231 | let createNextTaskRunner = () => {
|
232 | let task = scheduler.nextTask();
|
233 | if (task) {
|
234 | let taskRunner = new taskRunner_1.TaskRunner(configFile, additionalConfig, task, forkProcess);
|
235 | taskRunner.run()
|
236 | .then((result) => {
|
237 | if (result.exitCode && !result.failedCount) {
|
238 | logger.error('Runner process exited unexpectedly with error code: ' + result.exitCode);
|
239 | }
|
240 | taskResults_.add(result);
|
241 | task.done();
|
242 | createNextTaskRunner();
|
243 |
|
244 | if (scheduler.numTasksOutstanding() === 0) {
|
245 | deferred.resolve();
|
246 | }
|
247 | logger.info(scheduler.countActiveTasks() + ' instance(s) of WebDriver still running');
|
248 | })
|
249 | .catch((err) => {
|
250 | logger.error('Error:', err.stack || err.message || err);
|
251 | cleanUpAndExit(RUNNERS_FAILED_EXIT_CODE);
|
252 | });
|
253 | }
|
254 | };
|
255 |
|
256 |
|
257 |
|
258 |
|
259 | for (let i = 0; i < scheduler.maxConcurrentTasks(); ++i) {
|
260 | createNextTaskRunner();
|
261 | }
|
262 | logger.info('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver');
|
263 |
|
264 | deferred.promise
|
265 | .then(function () {
|
266 |
|
267 | if (config.resultJsonOutputFile) {
|
268 | taskResults_.saveResults(config.resultJsonOutputFile);
|
269 | }
|
270 | taskResults_.reportSummary();
|
271 | let exitCode = 0;
|
272 | if (taskResults_.totalProcessFailures() > 0) {
|
273 | exitCode = RUNNERS_FAILED_EXIT_CODE;
|
274 | }
|
275 | else if (taskResults_.totalSpecFailures() > 0) {
|
276 | exitCode = 1;
|
277 | }
|
278 | return cleanUpAndExit(exitCode);
|
279 | })
|
280 | .done();
|
281 | })
|
282 | .done();
|
283 | };
|
284 | exports.init = initFn;
|
285 |
|
\ | No newline at end of file |