UNPKG

6.5 kBJavaScriptView Raw
1import logger from '@wdio/logger';
2import WebDriver, { DEFAULTS } from 'webdriver';
3import { validateConfig } from '@wdio/config';
4import { wrapCommand } from '@wdio/utils';
5import MultiRemote from './multiremote.js';
6import SevereServiceErrorImport from './utils/SevereServiceError.js';
7import detectBackend from './utils/detectBackend.js';
8import { getProtocolDriver } from './utils/driver.js';
9import { WDIO_DEFAULTS, SupportedAutomationProtocols, Key as KeyConstant } from './constants.js';
10import { getPrototype, addLocatorStrategyHandler, isStub } from './utils/index.js';
11export * from './types.js';
12export const Key = KeyConstant;
13export const SevereServiceError = SevereServiceErrorImport;
14/**
15 * A method to create a new session with WebdriverIO.
16 *
17 * <b>
18 * NOTE: If you hit "error TS2694: Namespace 'global.WebdriverIO' has no exported member 'Browser'" when using typescript,
19 * add "@wdio/globals/types" into tsconfig.json's "types" array will solve it: <code> { "compilerOptions": { "types": ["@wdio/globals/types"] } } </code>
20 * </b>
21 *
22 * @param params Options to create the session with
23 * @param remoteModifier Modifier function to change the monad object
24 * @return browser object with sessionId
25 * @see <a href="https://webdriver.io/docs/typescript">Typescript setup</a>
26 */
27export const remote = async function (params, remoteModifier) {
28 logger.setLogLevelsConfig(params.logLevels, params.logLevel);
29 const keysToKeep = Object.keys(process.env.WDIO_WORKER_ID ? params : DEFAULTS);
30 const config = validateConfig(WDIO_DEFAULTS, params, keysToKeep);
31 const modifier = (client, options) => {
32 /**
33 * overwrite instance options with default values of the protocol
34 * package (without undefined properties)
35 */
36 Object.assign(options, Object.entries(config)
37 .reduce((a, [k, v]) => (typeof v === 'undefined' ? a : { ...a, [k]: v }), {}));
38 if (typeof remoteModifier === 'function') {
39 client = remoteModifier(client, options);
40 }
41 return client;
42 };
43 const { Driver, options } = await getProtocolDriver({ ...params, ...config });
44 const prototype = getPrototype('browser');
45 const instance = await Driver.newSession(options, modifier, prototype, wrapCommand);
46 /**
47 * we need to overwrite the original addCommand and overwriteCommand
48 */
49 if (params.framework && !isStub(params.automationProtocol)) {
50 const origAddCommand = instance.addCommand.bind(instance);
51 instance.addCommand = (name, fn, attachToElement) => (origAddCommand(name, fn, attachToElement));
52 const origOverwriteCommand = instance.overwriteCommand.bind(instance);
53 instance.overwriteCommand = (name, fn, attachToElement) => (origOverwriteCommand(name, fn, attachToElement));
54 }
55 instance.addLocatorStrategy = addLocatorStrategyHandler(instance);
56 return instance;
57};
58export const attach = async function (attachOptions) {
59 /**
60 * copy instances properties into new object
61 */
62 const params = {
63 automationProtocol: SupportedAutomationProtocols.webdriver,
64 ...attachOptions,
65 ...detectBackend(attachOptions.options),
66 requestedCapabilities: attachOptions.requestedCapabilities
67 };
68 const prototype = getPrototype('browser');
69 const { Driver } = await getProtocolDriver(params);
70 const driver = Driver.attachToSession(params, undefined, prototype, wrapCommand);
71 driver.addLocatorStrategy = addLocatorStrategyHandler(driver);
72 return driver;
73};
74/**
75 * WebdriverIO allows you to run multiple automated sessions in a single test.
76 * This is handy when you're testing features that require multiple users (for example, chat or WebRTC applications).
77 *
78 * Instead of creating a couple of remote instances where you need to execute common commands like newSession() or url() on each instance,
79 * you can simply create a multiremote instance and control all browsers at the same time.
80 *
81 * <b>
82 * NOTE: Multiremote is not meant to execute all your tests in parallel.
83 * It is intended to help coordinate multiple browsers and/or mobile devices for special integration tests (e.g. chat applications).
84 * </b>
85 *
86 * @param params capabilities to choose desired devices.
87 * @param automationProtocol
88 * @return All remote instances, the first result represents the capability defined first in the capability object,
89 * the second result the second capability and so on.
90 *
91 * @see <a href="https://webdriver.io/docs/multiremote">External document and example usage</a>.
92 */
93export const multiremote = async function (params, { automationProtocol } = {}) {
94 const multibrowser = new MultiRemote();
95 const browserNames = Object.keys(params);
96 /**
97 * create all instance sessions
98 */
99 await Promise.all(browserNames.map(async (browserName) => {
100 const instance = await remote(params[browserName]);
101 return multibrowser.addInstance(browserName, instance);
102 }));
103 /**
104 * use attachToSession capability to wrap instances around blank pod
105 */
106 const prototype = getPrototype('browser');
107 const sessionParams = isStub(automationProtocol) ? undefined : {
108 sessionId: '',
109 isW3C: multibrowser.instances[browserNames[0]].isW3C,
110 logLevel: multibrowser.instances[browserNames[0]].options.logLevel
111 };
112 const ProtocolDriver = automationProtocol && isStub(automationProtocol)
113 ? (await import(automationProtocol)).default
114 : WebDriver;
115 const driver = ProtocolDriver.attachToSession(sessionParams, multibrowser.modifier.bind(multibrowser), prototype, wrapCommand);
116 /**
117 * in order to get custom command overwritten or added to multiremote instance
118 * we need to pass in the prototype of the multibrowser
119 */
120 if (!isStub(automationProtocol)) {
121 const origAddCommand = driver.addCommand.bind(driver);
122 driver.addCommand = (name, fn, attachToElement) => {
123 return origAddCommand(name, fn, attachToElement, Object.getPrototypeOf(multibrowser.baseInstance), multibrowser.instances);
124 };
125 const origOverwriteCommand = driver.overwriteCommand.bind(driver);
126 driver.overwriteCommand = (name, fn, attachToElement) => {
127 return origOverwriteCommand(name, fn, attachToElement, Object.getPrototypeOf(multibrowser.baseInstance), multibrowser.instances);
128 };
129 }
130 driver.addLocatorStrategy = addLocatorStrategyHandler(driver);
131 return driver;
132};