UNPKG

webdriver

Version:

A Node.js bindings implementation for the W3C WebDriver and Mobile JSONWire Protocol

152 lines (151 loc) 7.06 kB
import logger from '@wdio/logger'; import { webdriverMonad, sessionEnvironmentDetector, startWebDriver } from '@wdio/utils'; import { validateConfig } from '@wdio/config'; import { deepmerge } from 'deepmerge-ts'; import command from './command.js'; import { DEFAULTS } from './constants.js'; import { startWebDriverSession, getPrototype, getEnvironmentVars, setupDirectConnect, initiateBidi, parseBidiMessage } from './utils.js'; const log = logger('webdriver'); export default class WebDriver { static async newSession(options, modifier, userPrototype = {}, customCommandWrapper) { const envLogLevel = process.env.WDIO_LOG_LEVEL; options.logLevel = envLogLevel ?? options.logLevel; const params = validateConfig(DEFAULTS, options); if (params.logLevel && (!options.logLevels || !options.logLevels.webdriver)) { logger.setLevel('webdriver', params.logLevel); } log.info('Initiate new session using the WebDriver protocol'); const driverProcess = await startWebDriver(params); const requestedCapabilities = { ...params.capabilities }; const { sessionId, capabilities } = await startWebDriverSession(params); const environment = sessionEnvironmentDetector({ capabilities, requestedCapabilities }); const environmentPrototype = getEnvironmentVars(environment); const protocolCommands = getPrototype(environment); /** * attach driver process to instance capabilities so we can kill the driver process * even after attaching to this session */ if (driverProcess?.pid) { capabilities['wdio:driverPID'] = driverProcess.pid; } /** * initiate WebDriver Bidi */ const bidiPrototype = {}; if (capabilities.webSocketUrl) { log.info(`Register BiDi handler for session with id ${sessionId}`); Object.assign(bidiPrototype, initiateBidi(capabilities.webSocketUrl, options.strictSSL)); } const monad = webdriverMonad({ ...params, requestedCapabilities }, modifier, { ...protocolCommands, ...environmentPrototype, ...userPrototype, ...bidiPrototype }); const client = monad(sessionId, customCommandWrapper); /** * parse and propagate all Bidi events to the browser instance */ if (capabilities.webSocketUrl) { // make sure the Bidi connection is established before returning await client._bidiHandler.connect(); client._bidiHandler?.socket.on('message', parseBidiMessage.bind(client)); } /** * if the server responded with direct connect information, update the * client options to speak directly to the appium host instead of a load * balancer (see https://github.com/appium/python-client#direct-connect-urls * for example). But only do this if the user has enabled this * behavior in the first place. */ if (params.enableDirectConnect) { setupDirectConnect(client); } return client; } /** * allows user to attach to existing sessions */ static attachToSession(options, modifier, userPrototype = {}, commandWrapper) { if (!options || typeof options.sessionId !== 'string') { throw new Error('sessionId is required to attach to existing session'); } // logLevel can be undefined in watch mode when SIGINT is called if (options.logLevel) { logger.setLevel('webdriver', options.logLevel); } options.capabilities = options.capabilities || {}; options.isW3C = options.isW3C === false ? false : true; options.protocol = options.protocol || DEFAULTS.protocol.default; options.hostname = options.hostname || DEFAULTS.hostname.default; options.port = options.port || DEFAULTS.port.default; options.path = options.path || DEFAULTS.path.default; const environment = sessionEnvironmentDetector({ capabilities: options.capabilities, requestedCapabilities: options.capabilities }); options = Object.assign(environment, options); const environmentPrototype = getEnvironmentVars(options); const protocolCommands = getPrototype(options); /** * initiate WebDriver Bidi */ const bidiPrototype = {}; const webSocketUrl = 'alwaysMatch' in options.capabilities ? options.capabilities.alwaysMatch?.webSocketUrl : options.capabilities.webSocketUrl; if (webSocketUrl) { log.info(`Register BiDi handler for session with id ${options.sessionId}`); Object.assign(bidiPrototype, initiateBidi(webSocketUrl, options.strictSSL)); } const prototype = { ...protocolCommands, ...environmentPrototype, ...userPrototype, ...bidiPrototype }; const monad = webdriverMonad(options, modifier, prototype); const client = monad(options.sessionId, commandWrapper); /** * parse and propagate all Bidi events to the browser instance */ if (webSocketUrl) { client._bidiSocket?.on('message', parseBidiMessage.bind(client)); } return client; } /** * Changes The instance session id and browser capabilities for the new session * directly into the passed in browser object * * @param {object} instance the object we get from a new browser session. * @returns {string} the new session id of the browser */ static async reloadSession(instance, newCapabilities) { const capabilities = deepmerge(instance.requestedCapabilities, newCapabilities || {}); const params = { ...instance.options, capabilities }; let driverProcess; if (newCapabilities?.browserName) { delete params.port; delete params.hostname; driverProcess = await startWebDriver(params); } const { sessionId, capabilities: newSessionCapabilities } = await startWebDriverSession(params); /** * attach driver process to instance capabilities so we can kill the driver process * even after attaching to this session */ if (driverProcess?.pid) { newSessionCapabilities['wdio:driverPID'] = driverProcess.pid; } instance.options.hostname = params.hostname; instance.options.port = params.port; instance.sessionId = sessionId; instance.capabilities = newSessionCapabilities; return sessionId; } static get WebDriver() { return WebDriver; } } /** * Helper methods consumed by webdriverio package */ export { getPrototype, DEFAULTS, command, getEnvironmentVars }; export * from './types.js'; export * from './constants.js'; export * from './bidi/handler.js'; export * as local from './bidi/localTypes.js'; export * as remote from './bidi/remoteTypes.js';