UNPKG

17.7 kBJavaScriptView Raw
1"use strict";
2
3const {CLI_MODE, mobileWeb} = require('./constants');
4const _ = require('lodash');
5const fs = require('fs');
6const path = require('path');
7const config = require('./config');
8const utils = require('../utils');
9const {getAppCaps} = require('./apkUploader/apkUploaderFactory');
10
11
12const LOG_LEVEL = config.WEBDRIVER_DEBUG ? 'verbose' : 'silent';
13const CONTENT_SETTING = {
14 CONTENT_SETTING_DEFAULT: 0,
15 CONTENT_SETTING_ALLOW: 1,
16 CONTENT_SETTING_BLOCK: 2,
17 CONTENT_SETTING_ASK: 3,
18};
19
20function buildEdgeOptions(opts) {
21 Object.assign(opts.desiredCapabilities, {
22 browserName: 'MicrosoftEdge',
23 _isOldEdge: true
24 });
25
26 return opts;
27}
28
29function buildSafariOptions(opts, browserName) {
30 const safariOptions = { browserName: 'safari' }
31
32 if(browserName === 'safari technology preview') {
33 safariOptions['safari.options'] = { technologyPreview: true };
34 }
35
36 Object.assign(opts.desiredCapabilities, safariOptions);
37 return opts;
38}
39
40function buildIEOptions(opts, browserOptions, version) {
41 const ieOptions = {
42 ignoreProtectedModeSettings: true,
43 'ie.ensureCleanSession': true,
44 'ie.enableFullPageScreenshot': false,
45 'ie.fileUploadDialogTimeout': 3000
46 };
47
48 Object.assign(opts.desiredCapabilities, {
49 browserName: 'internet explorer',
50 version: version
51 });
52
53 if (browserOptions.oldCapabilities) {
54 Object.assign(opts.desiredCapabilities, ieOptions);
55 }
56
57 if (browserOptions.w3cCapabilities) {
58 opts.desiredCapabilities['se:ieOptions'] = ieOptions;
59 }
60
61 return opts;
62}
63
64function readFileToBase64(fileLocation) {
65 return fs.readFileSync(fileLocation, {encoding: 'base64'});
66}
67
68function setTestimExtension(browserOptions, extensions, args) {
69 if (browserOptions.ext) {
70 const ext = typeof(browserOptions.ext) === 'string' ? browserOptions.ext : (__dirname + '/..');
71 const loadExt = '--load-extension=' + ext;
72 args.push(loadExt);
73 return;
74 }
75
76 const zipFileSuffix = browserOptions.canary ? "-master.zip" : ".zip";
77 const filePath = path.join(process.cwd(), "testim-headless" + zipFileSuffix);
78 extensions.push(readFileToBase64(filePath));
79}
80
81function buildChromiumOptions(opts, browserOptions, testRunConfig, customExtensionLocalLocation, gridInfo) {
82 const extensions = [];
83 const args = [
84 '--disable-popup-blocking',
85 '--ignore-gpu-blacklist',
86 '--auto-select-desktop-capture-source=Entire screen',
87 '--ignore-certificate-errors',
88 ];
89 if (browserOptions.headless) {
90 args.push('--headless');
91 }
92 //sauce labs issues - if you set w3c = true sauce search the data in capabilities instead of desiredCapabilities
93 const isW3CMode = () => {
94 return browserOptions.mode !== CLI_MODE.EXTENSION;
95 };
96 const chromiumOptions = {
97 prefs: {
98 'profile.default_content_setting_values.popups': CONTENT_SETTING.CONTENT_SETTING_ALLOW,
99 // allow multiple download files
100 'profile.default_content_setting_values.automatic_downloads': CONTENT_SETTING.CONTENT_SETTING_ALLOW,
101 // disable pdf viewer
102 'plugins.always_open_pdf_externally': true
103 },
104 w3c: isW3CMode()
105 };
106
107 if(browserOptions.chromeExtraPrefs) {
108 Object.assign(chromiumOptions.prefs, browserOptions.chromeExtraPrefs);
109 }
110
111 if(browserOptions.chromeBlockLocation) {
112 chromiumOptions.prefs["profile.default_content_setting_values.geolocation"] = CONTENT_SETTING.CONTENT_SETTING_BLOCK;
113 }
114
115 if(browserOptions.chromeUserDataDir) {
116 args.push(`--user-data-dir=${browserOptions.chromeUserDataDir}`);
117 }
118
119 if(browserOptions.projectData && browserOptions.projectData.defaults && browserOptions.projectData.defaults.disableChromiumGpu) {
120 args.push('--disable-gpu');
121 }
122
123 const browserName = testRunConfig.seleniumName || testRunConfig.browserValue;
124 Object.assign(opts.desiredCapabilities, { browserName });
125
126 function setMobileEmulationSettings() {
127 if (testRunConfig.mobileEmulation) {
128 chromiumOptions.mobileEmulation = {
129 deviceMetrics: {
130 width: testRunConfig.mobileEmulation.device.width,
131 height: testRunConfig.mobileEmulation.device.height + mobileWeb.MOBILE_WEB_REMOTE_RUN_HEADER_SPACING,
132 pixelRatio: testRunConfig.mobileEmulation.device.deviceScaleFactor
133 },
134 userAgent: testRunConfig.mobileEmulation.userAgent
135 };
136 }
137 }
138
139 setMobileEmulationSettings();
140
141 if (browserOptions.mode === CLI_MODE.EXTENSION) {
142 setTestimExtension(browserOptions, extensions, args);
143 }
144
145 if (customExtensionLocalLocation) {
146 extensions.push(readFileToBase64(customExtensionLocalLocation));
147 }
148
149 if (extensions.length > 0) {
150 chromiumOptions.extensions = extensions;
151 }
152
153 chromiumOptions.args = args;
154 const optionsKey = {'MicrosoftEdge': 'edgeOptions', 'chrome': 'chromeOptions'}[browserName];
155 const vendor = {'MicrosoftEdge': 'ms', 'chrome': 'goog'}[browserName];
156
157 if (browserOptions.oldCapabilities && gridInfo.type !== 'testimEnterprise') {
158 opts.desiredCapabilities[optionsKey] = chromiumOptions;
159 }
160
161 if (browserOptions.w3cCapabilities || gridInfo.type === 'testimEnterprise') {
162 opts.desiredCapabilities[`${vendor}:${optionsKey}`] = chromiumOptions;
163 }
164
165 return opts;
166}
167
168function buildFirefoxOptions(opts, browserOptions) {
169 Object.assign(opts.desiredCapabilities, {
170 acceptInsecureCerts: true,
171 browserName: 'firefox',
172 marionette: true,
173 //disable pdf viewer. Otherwise, the pdf viewer takes over when we download a pdf.
174 'moz:firefoxOptions': {
175 prefs: {
176 'pdfjs.disabled': true
177 }
178 }
179 });
180
181 if (browserOptions.mode === CLI_MODE.EXTENSION) {
182 if (browserOptions.ext) {
183 opts.desiredCapabilities.testim_firefox_profile = browserOptions.ext;
184 } else {
185 const zipFileSuffix = browserOptions.canary ? '-master.zip' : '.zip';
186 const filePath = path.join(process.cwd(), 'testim-firefox-profile' + zipFileSuffix);
187 opts.desiredCapabilities.firefox_profile = readFileToBase64(filePath);
188 }
189 }
190
191
192 // more interesting options
193 // https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode#Debugging_headless_Firefox
194 if (browserOptions.headless) {
195 if (!opts.desiredCapabilities['moz:firefoxOptions'].args) {
196 opts.desiredCapabilities['moz:firefoxOptions'].args = [];
197 }
198 opts.desiredCapabilities['moz:firefoxOptions'].args.push('-headless');
199 };
200
201 return opts;
202}
203
204function buildSaucelabs(browserOptions, testName, testRunConfig) {
205 if (browserOptions.saucelabs && browserOptions.saucelabs.username && browserOptions.saucelabs.accessKey) {
206 if (testRunConfig) {
207 testRunConfig.sl.version = testRunConfig.browserValue === 'safari' ? testRunConfig.sl.safari_version : testRunConfig.sl.version;
208 testRunConfig.sl.appiumVersion = browserOptions.saucelabs.appiumVersion || testRunConfig.sl.appiumVersion;
209 return Object.assign({}, testRunConfig.sl, browserOptions.saucelabs, {name: testName});
210 }
211 return Object.assign({}, browserOptions.saucelabs, {name: testName});
212 }
213 return {};
214}
215
216function buildBrowserstack(browserOptions, testName, testRunConfig) {
217 if (!_.isEmpty(browserOptions.browserstack)) {
218 if (testRunConfig) {
219 testRunConfig.bs.browser_version = testRunConfig.browserValue === 'safari' ? testRunConfig.bs.safari_version : testRunConfig.bs.browser_version;
220 if (testRunConfig.browserValue === 'safari' && testRunConfig.bs.safari_version === '10') {
221 Object.assign(testRunConfig.bs, {'safari.options': {'technologyPreview': true}});
222 }
223 return Object.assign({}, testRunConfig.bs, browserOptions.browserstack, {name: testName});
224 }
225 return Object.assign({}, browserOptions.browserstack, {name: testName});
226 }
227 return {};
228}
229
230function buildPerfecto(browserOptions) {
231 if (browserOptions.perfecto) {
232 return browserOptions.perfecto;
233 }
234 return {};
235}
236
237function buildExperitest(browserOptions, browser, sessionTimeoutSec) {
238
239 if (browserOptions.experitestToken) {
240 const isSafari = browser === 'safari';
241 return {
242 accessKey: browserOptions.experitestToken,
243 browserVersion: 'latest',
244 platformName: isSafari ? 'MAC' : 'WIN10',
245 //Workaround to Experitest bug in take screenshot in Safari browser
246 seleniumScreenshot: isSafari,
247 newSessionWaitTimeout: sessionTimeoutSec
248 };
249 }
250
251 return {};
252}
253
254function getConnectionRetryTimeout(browserOptions) {
255 const browserTimeout = browserOptions.browserTimeout;
256 const MIN_TIMEOUT = 90000;
257 return browserTimeout < MIN_TIMEOUT ? MIN_TIMEOUT : browserTimeout;
258}
259
260function buildHeaders(browserOptions) {
261 const headers = {};
262 if (typeof browserOptions.gridUsername !== "undefined" && typeof browserOptions.gridPassword !== "undefined") {
263 headers.Authorization = utils.buildBasicHeader(browserOptions.gridUsername, browserOptions.gridPassword);
264 }
265 return headers;
266}
267
268function buildSeleniumOptions(browserOptions, testName, testRunConfig, gridInfo, customExtensionLocalLocation) {
269 if (gridInfo.mode === 'local') {
270 const extensions = [];
271
272 const args = [];
273
274 if (browserOptions.headless) {
275 args.push("--headless");
276 }
277
278 if (browserOptions.silentDebuggerExtensionApi) {
279 args.push("--silent-debugger-extension-api");
280 }
281
282 if (browserOptions.remoteDebuggingPort !== undefined) {
283 args.push(`--remote-debugging-port=${browserOptions.remoteDebuggingPort}`);
284 }
285
286 if (browserOptions.mode !== 'selenium') {
287 setTestimExtension(browserOptions, extensions, args);
288 }
289
290 return {
291 logLevel: LOG_LEVEL,
292 desiredCapabilities: {
293 chromeOptions: {
294 args,
295 extensions
296 },
297 browserName: 'chrome'
298 },
299 host: 'localhost',
300 port: 9515 // chromedriver port
301 };
302 }
303
304 const headers = buildHeaders(browserOptions);
305 let opts = {
306 host: gridInfo.host,
307 port: gridInfo.port || 4444,
308 path: gridInfo.path || '/wd/hub',
309 protocol: gridInfo.protocol || 'http',
310 logLevel: LOG_LEVEL,
311 connectionRetryTimeout: getConnectionRetryTimeout(browserOptions),
312 desiredCapabilities: {
313 acceptSslCerts: true,
314 unexpectedAlertBehaviour: "accept", // What the browser should do with an unhandled alert before throwing out the UnhandledAlertException - automatically click on accept
315 nativeEvents: true,
316 testName
317 },
318 deprecationWarnings: false
319 };
320
321 if (!_.isEmpty(headers)) {
322 opts.headers = headers;
323 }
324
325 if (browserOptions.proxyForGrid) {
326 opts.agent = new global.ProxyAgent(global.proxyUri);
327 }
328
329 if (browserOptions.disableNativeEvents) {
330 opts.desiredCapabilities.nativeEvents = false;
331 }
332
333 if (gridInfo.user && gridInfo.key) {
334 if (gridInfo.type === "saucelabs") {
335 browserOptions.saucelabs = browserOptions.saucelabs || {};
336 browserOptions.saucelabs.username = browserOptions.saucelabs.username || gridInfo.user;
337 browserOptions.saucelabs.accessKey = browserOptions.saucelabs.accessKey || gridInfo.key;
338 }
339
340 if (gridInfo.type === "browserstack") {
341 browserOptions.browserstack = browserOptions.browserstack || {};
342 browserOptions.browserstack['browserstack.user'] = browserOptions.browserstack['browserstack.user'] || gridInfo.user;
343 browserOptions.browserstack['browserstack.key'] = browserOptions.browserstack['browserstack.key'] || gridInfo.key;
344 }
345 }
346
347 if (gridInfo.key && gridInfo.type === "perfecto") {
348 browserOptions.perfecto.securityToken = gridInfo.key;
349 }
350
351 const browserTimeoutSec = parseInt(browserOptions.browserTimeout / 1000);
352 const browser = browserOptions.browser || (testRunConfig && testRunConfig.browserValue);
353
354 Object.assign(
355 opts.desiredCapabilities,
356 buildSaucelabs(browserOptions, testName, testRunConfig),
357 buildBrowserstack(browserOptions, testName, testRunConfig),
358 buildPerfecto(browserOptions, testName, testRunConfig),
359 buildExperitest(browserOptions, browser, browserTimeoutSec));
360
361 if (!browserOptions.ext && _.endsWith(gridInfo.host, '.testim.io') && !browserOptions.canary && browserOptions.mode === CLI_MODE.EXTENSION) {
362 if (browser === 'chrome') {
363 browserOptions.ext = '/opt/testim-headless';
364 } else if (browser === 'edge-chromium') {
365 browserOptions.ext = 'C:/selenium/testim-headless';
366 } else if (browser === 'firefox') {
367 browserOptions.ext = '/opt/testim-firefox-profile';
368 }
369 }
370
371
372
373 if (_.endsWith(gridInfo.host, '.testim.io') && browser === 'edge-chromium') {
374 opts.desiredCapabilities.version = '83'; // Need to match GGR filter
375 }
376
377 switch (browser) {
378 case 'chrome':
379 case 'edge-chromium':
380 opts = buildChromiumOptions(opts, browserOptions, testRunConfig, customExtensionLocalLocation, gridInfo);
381 break;
382 case 'firefox':
383 opts = buildFirefoxOptions(opts, browserOptions);
384 break;
385 case 'edge':
386 opts = buildEdgeOptions(opts);
387 break;
388 case 'safari':
389 case 'safari technology preview':
390 opts = buildSafariOptions(opts, browser);
391 break;
392 case 'ie11':
393 opts = buildIEOptions(opts, browserOptions, "11");
394 break;
395 }
396
397 _.merge(opts.desiredCapabilities, browserOptions.seleniumCapsFileContent);
398
399 return opts;
400}
401
402function buildTestObject(browserOptions, testName, appCaps) {
403 if (!_.isEmpty(browserOptions.testobjectSauce)) {
404 return Object.assign({
405 "idleTimeout": 30,
406 "testobject_test_name": testName,
407 "testobject_session_creation_timeout": String(browserOptions.browserTimeout),
408 deviceName: null
409 }, appCaps, browserOptions.testobjectSauce);
410 }
411 return {};
412}
413
414function buildSaucelabsMobile(browserOptions, testName, appCaps) {
415 if (!_.isEmpty(browserOptions.saucelabs)) {
416 return Object.assign({
417 deviceName: null,
418 name: testName,
419 "idleTimeout": 30,
420 browserName: ""
421 }, appCaps, browserOptions.saucelabs);
422 }
423 return {};
424}
425
426function buildAppiumOptions(browserOptions, gridInfo, nativeAppData, testName) {
427 let opts = {
428 host: gridInfo.host,
429 port: gridInfo.port || 4444,
430 path: gridInfo.path || '/wd/hub',
431 protocol: gridInfo.protocol || 'http',
432 logLevel: LOG_LEVEL,
433 connectionRetryTimeout: getConnectionRetryTimeout(browserOptions),
434 desiredCapabilities: {
435 browserName: "",
436 "noReset": false,
437 "fullReset": false,
438 },
439 };
440
441 if(browserOptions.projectData.type === "android") {
442 Object.assign(
443 opts.desiredCapabilities, {
444 appPackage: nativeAppData.packageName,
445 appActivity: nativeAppData.activity,
446 "platformName": "Android",
447 "automationName": 'uiautomator2',
448 "autoLaunch": false,
449 "ignoreUnimportantViews": false,
450 "disableWindowAnimation": browserOptions.disableWindowAnimation
451 }
452 );
453 } else if (browserOptions.projectData.type === "ios") {
454 Object.assign(
455 opts.desiredCapabilities, {
456 bundleId: nativeAppData.packageName,
457 "platformName": "iOS",
458 "automationName": 'XCUITest',
459 }
460 );
461 }
462
463 if(browserOptions.deviceUdid) {
464 opts.desiredCapabilities.udid = browserOptions.deviceUdid;
465 }
466
467 if(browserOptions.deviceName) {
468 opts.desiredCapabilities.deviceName = browserOptions.deviceName;
469 }
470
471 if (gridInfo.user && gridInfo.key && gridInfo.type === "saucelabs") {
472 browserOptions.saucelabs = browserOptions.saucelabs || {};
473 browserOptions.saucelabs.username = browserOptions.saucelabs.username || gridInfo.user;
474 browserOptions.saucelabs.accessKey = browserOptions.saucelabs.accessKey || gridInfo.key;
475 } else if (gridInfo.key && gridInfo.type === "testobject") {
476 browserOptions.testobjectSauce = browserOptions.testobjectSauce || {};
477 browserOptions.testobjectSauce.testobjectApiKey = browserOptions.testobjectSauce.testobjectApiKey || gridInfo.key;
478 } else if (gridInfo.key && gridInfo.type === "perfecto") {
479 browserOptions.perfecto = browserOptions.perfecto || {};
480 browserOptions.perfecto.securityToken = gridInfo.key;
481 }
482
483 const appCaps = getAppCaps(gridInfo);
484
485 Object.assign(
486 opts.desiredCapabilities,
487 buildTestObject(browserOptions, testName, appCaps),
488 buildSaucelabsMobile(browserOptions, testName, appCaps)
489 );
490
491 return opts;
492}
493
494module.exports = {
495 buildSeleniumOptions: buildSeleniumOptions,
496 buildAppiumOptions: buildAppiumOptions
497};