UNPKG

5.68 kBJavaScriptView Raw
1/*
2 * Copyright (c) 2015, Groupon, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of GROUPON nor the names of its contributors may be
17 * used to endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33'use strict';
34
35const fs = require('fs');
36
37const Bluebird = require('bluebird');
38const findOpenPort = require('find-open-port');
39const _ = require('lodash');
40const debug = require('debug')('testium-core:processes');
41
42const spawnServers = require('./spawn-server');
43
44const Phantom = require('./processes/phantom');
45const Proxy = require('./processes/proxy');
46const App = require('./processes/application');
47const Selenium = require('./processes/selenium');
48const ChromeDriver = require('./processes/chromedriver');
49
50const CHROME = 'chrome';
51
52function isChromeInDocker(browserName) {
53 if (browserName !== CHROME) {
54 return Bluebird.reject();
55 }
56
57 const fileName = '/.dockerenv';
58 return new Bluebird((resolve, reject) => {
59 fs.stat(fileName, (err, stats) => {
60 return err ? reject(err) : resolve(stats);
61 });
62 });
63}
64
65function mergeChromeOptions(baseOpts, configOpts) {
66 const reg = /^([-\w]+)=?([\.,\/\w]*)/;
67
68 const objOpts = baseOpts.concat(configOpts).reduce((acc, val) => {
69 const match = reg.exec(val);
70 acc[match[1]] = match[2];
71 return acc;
72 }, {});
73
74 return Object.keys(objOpts).reduce((acc, key) => {
75 const option = objOpts[key] !== '' ? `${key}=${objOpts[key]}` : key;
76 return acc.concat([option]);
77 }, []);
78}
79
80function unrefAll(procs) {
81 // Make sure these processes don't keep the parent alive
82 _.each(procs, proc => {
83 proc.rawProcess.unref();
84 });
85 return procs;
86}
87
88function launchAllProcesses(config) {
89 const launchApp = config.getBool('launch', false);
90
91 function launchWithAppPort(appPort) {
92 config.set('app.port', appPort);
93
94 const browserName = config.get('browser');
95 config.set('desiredCapabilities.browserName', browserName);
96
97 const servers = [Proxy];
98
99 if (launchApp) {
100 debug('Launching app');
101 servers.push(App);
102 } else {
103 debug('Using already running application on port %d', appPort);
104 }
105
106 const seleniumUrl = config.get('selenium.serverUrl', false);
107 if (!seleniumUrl) {
108 debug('Will launch webdriver server for %j', browserName);
109 servers.push(
110 {
111 phantomjs: Phantom,
112 chrome: ChromeDriver,
113 }[browserName] || Selenium
114 );
115 } else {
116 debug('Using existing selenium server', seleniumUrl);
117 }
118
119 if (browserName === CHROME) {
120 const chromePath = config.get('chrome.command', null);
121 if (chromePath) {
122 config.set('desiredCapabilities.chromeOptions.binary', chromePath);
123 }
124
125 let args = [
126 '--disable-application-cache',
127 '--media-cache-size=1',
128 '--disk-cache-size=1',
129 '--disk-cache-dir=/dev/null',
130 '--disable-cache',
131 '--disable-desktop-notifications',
132 ];
133
134 const headlessCfg = config.get('chrome.headless', !process.env.DISPLAY);
135 if (headlessCfg && headlessCfg !== 'false') args.push('--headless');
136
137 if (config.has('desiredCapabilities.chromeOptions.args')) {
138 if (
139 config.get('desiredCapabilities.chromeOptions.mergeArgs', false) ===
140 true
141 ) {
142 args = mergeChromeOptions(
143 args,
144 config.get('desiredCapabilities.chromeOptions.args')
145 );
146 } else {
147 args = config.get('desiredCapabilities.chromeOptions.args');
148 }
149 }
150
151 if (process.getuid() === 0) args.push('--no-sandbox');
152
153 return isChromeInDocker(browserName)
154 .then(() => {
155 debug('Running in docker env');
156 args.push('--disable-dev-shm-usage');
157 })
158 .catch(() => {
159 debug('Not running in docker env');
160 })
161 .then(() => {
162 config.set('desiredCapabilities.chromeOptions.args', args);
163 return spawnServers(config, servers);
164 })
165 .then(unrefAll);
166 }
167
168 return spawnServers(config, servers).then(unrefAll);
169 }
170
171 // app.port is special because the proxy needs to know it
172 const appPort = config.get('app.port', launchApp ? 0 : -1);
173 if (appPort === 0) {
174 return findOpenPort().then(launchWithAppPort);
175 }
176 return launchWithAppPort(appPort);
177}
178module.exports = launchAllProcesses;