webdriver 8.13.13 → 8.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/command.d.ts +3 -0
- package/build/command.d.ts.map +1 -1
- package/build/command.js +29 -2
- package/build/constants.d.ts +6 -0
- package/build/constants.d.ts.map +1 -1
- package/build/constants.js +17 -3
- package/build/driver/index.d.ts +19 -0
- package/build/driver/index.d.ts.map +1 -0
- package/build/driver/index.js +138 -0
- package/build/driver/utils.d.ts +18 -0
- package/build/driver/utils.d.ts.map +1 -0
- package/build/driver/utils.js +108 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +6 -1
- package/build/utils.js +1 -1
- package/package.json +14 -6
package/build/command.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { ChildProcess } from 'node:child_process';
|
|
1
3
|
import type { CommandEndpoint, BidiResponse } from '@wdio/protocols';
|
|
2
4
|
import type { BidiHandler } from './bidi/handler.js';
|
|
3
5
|
import type { WebDriverResponse } from './request/index.js';
|
|
4
6
|
import type { BaseClient } from './types.js';
|
|
5
7
|
interface BaseClientWithEventHandler extends BaseClient {
|
|
6
8
|
eventMiddleware: BidiHandler;
|
|
9
|
+
_driverProcess?: ChildProcess;
|
|
7
10
|
}
|
|
8
11
|
export default function (method: string, endpointUri: string, commandInfo: CommandEndpoint, doubleEncodeVariables?: boolean): (this: BaseClientWithEventHandler, ...args: any[]) => Promise<WebDriverResponse | BidiResponse | void>;
|
|
9
12
|
export {};
|
package/build/command.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGtD,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAGpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAK5C,UAAU,0BAA2B,SAAQ,UAAU;IACnD,eAAe,EAAE,WAAW,CAAA;IAC5B,cAAc,CAAC,EAAE,YAAY,CAAA;CAChC;AAED,MAAM,CAAC,OAAO,WACV,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,eAAe,EAC5B,qBAAqB,UAAQ,UAIgB,0BAA0B,WAAW,GAAG,EAAE,KAAG,QAAQ,iBAAiB,GAAG,YAAY,GAAG,IAAI,CAAC,CA0I7I"}
|
package/build/command.js
CHANGED
|
@@ -91,8 +91,35 @@ export default function (method, endpointUri, commandInfo, doubleEncodeVariables
|
|
|
91
91
|
log.info('RESULT', resultLog);
|
|
92
92
|
}
|
|
93
93
|
this.emit('result', { method, endpoint, body, result });
|
|
94
|
-
if (command === 'deleteSession'
|
|
95
|
-
|
|
94
|
+
if (command === 'deleteSession') {
|
|
95
|
+
/**
|
|
96
|
+
* kill driver process if there is one
|
|
97
|
+
*/
|
|
98
|
+
if (this._driverProcess) {
|
|
99
|
+
log.info(`Kill ${this._driverProcess.spawnfile} driver process with command line: ${this._driverProcess.spawnargs.slice(1).join(' ')}`);
|
|
100
|
+
const killedSuccessfully = this._driverProcess.kill('SIGKILL');
|
|
101
|
+
if (!killedSuccessfully) {
|
|
102
|
+
log.warn('Failed to kill driver process, manully clean-up might be required');
|
|
103
|
+
}
|
|
104
|
+
this._driverProcess = undefined;
|
|
105
|
+
setTimeout(() => {
|
|
106
|
+
/**
|
|
107
|
+
* clear up potential leaked TLS Socket handles
|
|
108
|
+
* see https://github.com/puppeteer/puppeteer/pull/10667
|
|
109
|
+
*/
|
|
110
|
+
for (const handle of process._getActiveHandles()) {
|
|
111
|
+
if (handle.servername && handle.servername.includes('edgedl.me')) {
|
|
112
|
+
handle.destroy();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}, 10);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* clear logger stream if session has been terminated
|
|
119
|
+
*/
|
|
120
|
+
if (!process.env.WDIO_WORKER_ID) {
|
|
121
|
+
logger.clearLogger();
|
|
122
|
+
}
|
|
96
123
|
}
|
|
97
124
|
return result.value;
|
|
98
125
|
});
|
package/build/constants.d.ts
CHANGED
package/build/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAO,MAAM,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAiJpE,CAAA;AAED,eAAO,MAAM,QAAQ;;;CAGpB,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;CAKlC,CAAA"}
|
package/build/constants.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
1
2
|
export const DEFAULTS = {
|
|
2
3
|
/**
|
|
3
4
|
* protocol of automation driver
|
|
@@ -12,14 +13,13 @@ export const DEFAULTS = {
|
|
|
12
13
|
*/
|
|
13
14
|
hostname: {
|
|
14
15
|
type: 'string',
|
|
15
|
-
default: '
|
|
16
|
+
default: '0.0.0.0'
|
|
16
17
|
},
|
|
17
18
|
/**
|
|
18
19
|
* port of automation driver
|
|
19
20
|
*/
|
|
20
21
|
port: {
|
|
21
|
-
type: 'number'
|
|
22
|
-
default: 4444
|
|
22
|
+
type: 'number'
|
|
23
23
|
},
|
|
24
24
|
/**
|
|
25
25
|
* path to WebDriver endpoints
|
|
@@ -134,9 +134,23 @@ export const DEFAULTS = {
|
|
|
134
134
|
strictSSL: {
|
|
135
135
|
type: 'boolean',
|
|
136
136
|
default: true
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* The path to the root of the cache directory. This directory is used to store all drivers that are downloaded
|
|
140
|
+
* when attempting to start a session.
|
|
141
|
+
*/
|
|
142
|
+
cacheDir: {
|
|
143
|
+
type: 'string',
|
|
144
|
+
default: process.env.WEBDRIVER_CACHE_DIR || os.tmpdir()
|
|
137
145
|
}
|
|
138
146
|
};
|
|
139
147
|
export const REG_EXPS = {
|
|
140
148
|
commandName: /.*\/session\/[0-9a-f-]+\/(.*)/,
|
|
141
149
|
execFn: /return \(([\s\S]*)\)\.apply\(null, arguments\)/
|
|
142
150
|
};
|
|
151
|
+
export const SUPPORTED_BROWSERNAMES = {
|
|
152
|
+
chrome: ['chrome', 'googlechrome', 'chromium', 'chromium-browser'],
|
|
153
|
+
firefox: ['firefox', 'ff', 'mozilla', 'mozilla firefox'],
|
|
154
|
+
edge: ['edge', 'microsoftedge', 'msedge'],
|
|
155
|
+
safari: ['safari', 'safari technology preview']
|
|
156
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import cp from 'node:child_process';
|
|
3
|
+
import type { Options } from '@wdio/types';
|
|
4
|
+
import { type SafaridriverOptions } from 'safaridriver';
|
|
5
|
+
import { type GeckodriverParameters } from 'geckodriver';
|
|
6
|
+
import { type EdgedriverParameters } from 'edgedriver';
|
|
7
|
+
import { type InstallOptions } from '@puppeteer/browsers';
|
|
8
|
+
import type { Capabilities } from '@wdio/types';
|
|
9
|
+
export interface ExtendedCapabilities extends Capabilities.Capabilities, WDIODriverOptions {
|
|
10
|
+
}
|
|
11
|
+
export type ChromedriverOptions = InstallOptions & Omit<EdgedriverParameters, 'port' | 'edgeDriverVersion' | 'customEdgeDriverPath'>;
|
|
12
|
+
export interface WDIODriverOptions {
|
|
13
|
+
'wdio:chromedriverOptions'?: ChromedriverOptions;
|
|
14
|
+
'wdio:safaridriverOptions'?: Omit<SafaridriverOptions, 'port'>;
|
|
15
|
+
'wdio:geckodriverOptions'?: Omit<GeckodriverParameters, 'port'>;
|
|
16
|
+
'wdio:edgedriverOptions'?: Omit<EdgedriverParameters, 'port'>;
|
|
17
|
+
}
|
|
18
|
+
export declare function startWebDriver(options: Options.WebDriver): Promise<cp.ChildProcess | undefined>;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/driver/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAyB,MAAM,oBAAoB,CAAA;AAM1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,EAA8B,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACnF,OAAO,EAA6B,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,EAA4B,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAChF,OAAO,EAA2C,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAElG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAO/C,MAAM,WAAW,oBAAqB,SAAQ,YAAY,CAAC,YAAY,EAAE,iBAAiB;CAAG;AAC7F,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,IAAI,CAAC,oBAAoB,EAAE,MAAM,GAAG,mBAAmB,GAAG,sBAAsB,CAAC,CAAA;AAEpI,MAAM,WAAW,iBAAiB;IAC9B,0BAA0B,CAAC,EAAE,mBAAmB,CAAA;IAChD,0BAA0B,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAA;IAC9D,yBAAyB,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;IAC/D,wBAAwB,CAAC,EAAE,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAA;CAChE;AAED,wBAAsB,cAAc,CAAE,OAAO,EAAE,OAAO,CAAC,SAAS,wCAqI/D"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import fsp from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import cp from 'node:child_process';
|
|
6
|
+
import getPort from 'get-port';
|
|
7
|
+
import waitPort from 'wait-port';
|
|
8
|
+
import logger from '@wdio/logger';
|
|
9
|
+
import { deepmerge } from 'deepmerge-ts';
|
|
10
|
+
import { start as startSafaridriver } from 'safaridriver';
|
|
11
|
+
import { start as startGeckodriver } from 'geckodriver';
|
|
12
|
+
import { start as startEdgedriver } from 'edgedriver';
|
|
13
|
+
import { install, computeExecutablePath, Browser } from '@puppeteer/browsers';
|
|
14
|
+
import { parseParams, setupChrome, definesRemoteDriver, downloadProgressCallback } from './utils.js';
|
|
15
|
+
import { SUPPORTED_BROWSERNAMES } from '../constants.js';
|
|
16
|
+
const log = logger('webdriver');
|
|
17
|
+
export async function startWebDriver(options) {
|
|
18
|
+
/**
|
|
19
|
+
* in case we are running unit tests, just return
|
|
20
|
+
*/
|
|
21
|
+
if (process.env.WDIO_SKIP_DRIVER_SETUP) {
|
|
22
|
+
options.hostname = '0.0.0.0';
|
|
23
|
+
options.port = 4321;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* if any of the connection parameter are set, don't start any driver
|
|
28
|
+
*/
|
|
29
|
+
if (definesRemoteDriver(options)) {
|
|
30
|
+
log.info(`Connecting to existing driver at ${options.protocol}://${options.hostname}:${options.port}${options.path}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
let driverProcess;
|
|
34
|
+
let driver = '';
|
|
35
|
+
const start = Date.now();
|
|
36
|
+
const caps = options.capabilities.alwaysMatch || options.capabilities;
|
|
37
|
+
/**
|
|
38
|
+
* session might be a mobile session so don't do anything
|
|
39
|
+
*/
|
|
40
|
+
if (!caps.browserName) {
|
|
41
|
+
throw new Error('No "browserName" defined in capabilities nor hostname or port found!\n' +
|
|
42
|
+
'If you like to run a mobile session with Appium, make sure to set "hostname" and "port" in your ' +
|
|
43
|
+
'WebdriverIO options. If you like to run a local browser session make sure to pick from one of ' +
|
|
44
|
+
`the following browser names: ${Object.values(SUPPORTED_BROWSERNAMES).flat(Infinity)}`);
|
|
45
|
+
}
|
|
46
|
+
const port = await getPort();
|
|
47
|
+
if (SUPPORTED_BROWSERNAMES.chrome.includes(caps.browserName.toLowerCase())) {
|
|
48
|
+
/**
|
|
49
|
+
* Chrome
|
|
50
|
+
*/
|
|
51
|
+
const chromedriverOptions = caps['wdio:chromedriverOptions'] || {};
|
|
52
|
+
const cacheDir = chromedriverOptions.cacheDir || options.cacheDir || os.tmpdir();
|
|
53
|
+
const exist = await fsp.access(cacheDir).then(() => true, () => false);
|
|
54
|
+
if (!exist) {
|
|
55
|
+
await fsp.mkdir(cacheDir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
const { executablePath, buildId, platform } = await setupChrome(caps, cacheDir);
|
|
58
|
+
const chromedriverBinaryPath = computeExecutablePath({
|
|
59
|
+
browser: Browser.CHROMEDRIVER,
|
|
60
|
+
buildId,
|
|
61
|
+
cacheDir
|
|
62
|
+
});
|
|
63
|
+
const hasChromedriverInstalled = await fsp.access(chromedriverBinaryPath).then(() => true, () => false);
|
|
64
|
+
if (!hasChromedriverInstalled) {
|
|
65
|
+
log.info(`Downloading Chromedriver v${buildId}`);
|
|
66
|
+
await install({
|
|
67
|
+
...chromedriverOptions,
|
|
68
|
+
cacheDir,
|
|
69
|
+
platform,
|
|
70
|
+
buildId,
|
|
71
|
+
browser: Browser.CHROMEDRIVER,
|
|
72
|
+
downloadProgressCallback: (downloadedBytes, totalBytes) => downloadProgressCallback('Chromedriver', downloadedBytes, totalBytes)
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
log.info(`Using Chromedriver v${buildId} from cache directory ${cacheDir}`);
|
|
77
|
+
}
|
|
78
|
+
caps['goog:chromeOptions'] = deepmerge({ binary: executablePath }, caps['goog:chromeOptions'] || {});
|
|
79
|
+
chromedriverOptions.allowedOrigins = chromedriverOptions.allowedOrigins || ['*'];
|
|
80
|
+
chromedriverOptions.allowedIps = chromedriverOptions.allowedIps || [''];
|
|
81
|
+
const driverParams = parseParams({ port, ...chromedriverOptions });
|
|
82
|
+
driverProcess = cp.spawn(chromedriverBinaryPath, driverParams);
|
|
83
|
+
driver = `ChromeDriver v${buildId} with params ${driverParams.join(' ')}`;
|
|
84
|
+
}
|
|
85
|
+
else if (SUPPORTED_BROWSERNAMES.safari.includes(caps.browserName.toLowerCase())) {
|
|
86
|
+
const safaridriverOptions = caps['wdio:safaridriverOptions'] || {};
|
|
87
|
+
/**
|
|
88
|
+
* Safari
|
|
89
|
+
*/
|
|
90
|
+
driver = 'SafariDriver';
|
|
91
|
+
driverProcess = startSafaridriver({
|
|
92
|
+
useTechnologyPreview: Boolean(caps.browserName.match(/preview/i)),
|
|
93
|
+
...safaridriverOptions,
|
|
94
|
+
port
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* set "Host" header as it is required by Safaridriver
|
|
98
|
+
*/
|
|
99
|
+
options.headers = deepmerge({ Host: 'localhost' }, (options.headers || {}));
|
|
100
|
+
}
|
|
101
|
+
else if (SUPPORTED_BROWSERNAMES.firefox.includes(caps.browserName.toLowerCase())) {
|
|
102
|
+
/**
|
|
103
|
+
* Firefox
|
|
104
|
+
*/
|
|
105
|
+
const geckodriverOptions = caps['wdio:geckodriverOptions'] || {};
|
|
106
|
+
const cacheDir = geckodriverOptions.cacheDir || options.cacheDir || os.tmpdir();
|
|
107
|
+
driver = 'GeckoDriver';
|
|
108
|
+
driverProcess = await startGeckodriver({ ...geckodriverOptions, cacheDir, port });
|
|
109
|
+
}
|
|
110
|
+
else if (SUPPORTED_BROWSERNAMES.edge.includes(caps.browserName.toLowerCase())) {
|
|
111
|
+
/**
|
|
112
|
+
* Microsoft Edge
|
|
113
|
+
*/
|
|
114
|
+
const edgedriverOptions = caps['wdio:edgedriverOptions'] || {};
|
|
115
|
+
const cacheDir = edgedriverOptions.cacheDir || options.cacheDir || os.tmpdir();
|
|
116
|
+
driver = 'EdgeDriver';
|
|
117
|
+
driverProcess = await startEdgedriver({ ...edgedriverOptions, cacheDir, port });
|
|
118
|
+
/**
|
|
119
|
+
* Microsoft Edge is very particular when it comes to browser names
|
|
120
|
+
*/
|
|
121
|
+
caps.browserName = 'MicrosoftEdge';
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
throw new Error(`Unknown browser name "${caps.browserName}". Make sure to pick from one of the following ` +
|
|
125
|
+
Object.values(SUPPORTED_BROWSERNAMES).flat(Infinity));
|
|
126
|
+
}
|
|
127
|
+
if (options.outputDir) {
|
|
128
|
+
const logFile = path.resolve(options.outputDir, `wdio-${driver.split(' ').shift()?.toLowerCase()}-${port}.log`);
|
|
129
|
+
const logStream = fs.createWriteStream(logFile, { flags: 'w' });
|
|
130
|
+
driverProcess.stdout?.pipe(logStream);
|
|
131
|
+
driverProcess.stderr?.pipe(logStream);
|
|
132
|
+
}
|
|
133
|
+
await waitPort({ port, output: 'silent' });
|
|
134
|
+
options.hostname = '0.0.0.0';
|
|
135
|
+
options.port = port;
|
|
136
|
+
log.info(`Started ${driver} in ${Date.now() - start}ms on port ${port}`);
|
|
137
|
+
return driverProcess;
|
|
138
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { EdgedriverParameters } from 'edgedriver';
|
|
2
|
+
import type { Capabilities, Options } from '@wdio/types';
|
|
3
|
+
export declare function parseParams(params: EdgedriverParameters): string[];
|
|
4
|
+
export declare function getLocalChromePath(): string | undefined;
|
|
5
|
+
export declare function getBuildIdByPath(chromePath?: string): string | undefined;
|
|
6
|
+
export declare const downloadProgressCallback: (artifact: string, downloadedBytes: number, totalBytes: number) => void;
|
|
7
|
+
export declare function setupChrome(caps: Capabilities.Capabilities, cacheDir: string): Promise<{
|
|
8
|
+
platform: import("@puppeteer/browsers").BrowserPlatform;
|
|
9
|
+
executablePath: string;
|
|
10
|
+
buildId: string;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* helper method to determine if we need to start a browser driver
|
|
14
|
+
* which is: whenever the user has set connection options that differ from
|
|
15
|
+
* the default, or a port is set
|
|
16
|
+
*/
|
|
17
|
+
export declare function definesRemoteDriver(options: Options.WebDriver): boolean;
|
|
18
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/driver/utils.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAOxD,wBAAgB,WAAW,CAAC,MAAM,EAAE,oBAAoB,YAYvD;AAED,wBAAgB,kBAAkB,uBAOjC;AAED,wBAAgB,gBAAgB,CAAC,UAAU,CAAC,EAAE,MAAM,sBAenD;AAGD,eAAO,MAAM,wBAAwB,aAAc,MAAM,mBAAmB,MAAM,cAAc,MAAM,SAOrG,CAAA;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM;;;;GA2ClF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,WAO7D"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import cp from 'node:child_process';
|
|
5
|
+
import decamelize from 'decamelize';
|
|
6
|
+
import logger from '@wdio/logger';
|
|
7
|
+
import { getChromePath } from 'chrome-launcher';
|
|
8
|
+
import { install, canDownload, resolveBuildId, detectBrowserPlatform, Browser, ChromeReleaseChannel, computeExecutablePath } from '@puppeteer/browsers';
|
|
9
|
+
import { DEFAULTS } from '../constants.js';
|
|
10
|
+
const log = logger('webdriver');
|
|
11
|
+
const EXCLUDED_PARAMS = ['version', 'help'];
|
|
12
|
+
export function parseParams(params) {
|
|
13
|
+
return Object.entries(params)
|
|
14
|
+
.filter(([key,]) => !EXCLUDED_PARAMS.includes(key))
|
|
15
|
+
.map(([key, val]) => {
|
|
16
|
+
if (typeof val === 'boolean' && !val) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
const vals = Array.isArray(val) ? val : [val];
|
|
20
|
+
return vals.map((v) => `--${decamelize(key, { separator: '-' })}${typeof v === 'boolean' ? '' : `=${v}`}`);
|
|
21
|
+
})
|
|
22
|
+
.flat()
|
|
23
|
+
.filter(Boolean);
|
|
24
|
+
}
|
|
25
|
+
export function getLocalChromePath() {
|
|
26
|
+
try {
|
|
27
|
+
const chromePath = getChromePath();
|
|
28
|
+
return chromePath;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function getBuildIdByPath(chromePath) {
|
|
35
|
+
if (!chromePath) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
else if (os.platform() === 'win32') {
|
|
39
|
+
const versionPath = path.dirname(chromePath);
|
|
40
|
+
const contents = fs.readdirSync(versionPath);
|
|
41
|
+
const versions = contents.filter(a => /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/g.test(a));
|
|
42
|
+
// returning oldest in case there is an updated version and chrome still hasn't relaunched
|
|
43
|
+
const oldest = versions.sort((a, b) => a > b ? -1 : 1)[0];
|
|
44
|
+
return oldest;
|
|
45
|
+
}
|
|
46
|
+
const versionString = cp.execSync(`"${chromePath}" --version`).toString();
|
|
47
|
+
return versionString.split(' ').pop()?.trim();
|
|
48
|
+
}
|
|
49
|
+
let lastTimeCalled = Date.now();
|
|
50
|
+
export const downloadProgressCallback = (artifact, downloadedBytes, totalBytes) => {
|
|
51
|
+
if (Date.now() - lastTimeCalled < 1000) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const percentage = ((downloadedBytes / totalBytes) * 100).toFixed(2);
|
|
55
|
+
log.info(`Downloading ${artifact} ${percentage}%`);
|
|
56
|
+
lastTimeCalled = Date.now();
|
|
57
|
+
};
|
|
58
|
+
export async function setupChrome(caps, cacheDir) {
|
|
59
|
+
const platform = detectBrowserPlatform();
|
|
60
|
+
if (!platform) {
|
|
61
|
+
throw new Error('The current platform is not supported.');
|
|
62
|
+
}
|
|
63
|
+
if (!caps.browserVersion) {
|
|
64
|
+
const executablePath = getLocalChromePath();
|
|
65
|
+
const tag = getBuildIdByPath(executablePath);
|
|
66
|
+
/**
|
|
67
|
+
* verify that we have a valid Chrome browser installed
|
|
68
|
+
*/
|
|
69
|
+
if (tag) {
|
|
70
|
+
return {
|
|
71
|
+
platform,
|
|
72
|
+
executablePath,
|
|
73
|
+
buildId: await resolveBuildId(Browser.CHROME, platform, tag)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* otherwise download provided Chrome browser version or "stable"
|
|
79
|
+
*/
|
|
80
|
+
const tag = caps.browserVersion || ChromeReleaseChannel.STABLE;
|
|
81
|
+
const buildId = await resolveBuildId(Browser.CHROME, platform, tag);
|
|
82
|
+
const installOptions = {
|
|
83
|
+
cacheDir,
|
|
84
|
+
platform,
|
|
85
|
+
buildId,
|
|
86
|
+
browser: Browser.CHROME,
|
|
87
|
+
downloadProgressCallback: (downloadedBytes, totalBytes) => downloadProgressCallback('Chrome', downloadedBytes, totalBytes)
|
|
88
|
+
};
|
|
89
|
+
const isCombinationAvailable = await canDownload(installOptions);
|
|
90
|
+
if (!isCombinationAvailable) {
|
|
91
|
+
throw new Error(`Couldn't find a matching Chrome browser for tag "${buildId}" on platform "${platform}"`);
|
|
92
|
+
}
|
|
93
|
+
log.info(`Setting up Chrome v${buildId}`);
|
|
94
|
+
await install(installOptions);
|
|
95
|
+
const executablePath = computeExecutablePath(installOptions);
|
|
96
|
+
return { executablePath, buildId, platform };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* helper method to determine if we need to start a browser driver
|
|
100
|
+
* which is: whenever the user has set connection options that differ from
|
|
101
|
+
* the default, or a port is set
|
|
102
|
+
*/
|
|
103
|
+
export function definesRemoteDriver(options) {
|
|
104
|
+
return Boolean((options.protocol && options.protocol !== DEFAULTS.protocol.default) ||
|
|
105
|
+
(options.hostname && options.hostname !== DEFAULTS.hostname.default) ||
|
|
106
|
+
Boolean(options.port) ||
|
|
107
|
+
(options.path && options.path !== DEFAULTS.path.default));
|
|
108
|
+
}
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAA;AAExD,OAAO,OAAO,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAA;AAExD,OAAO,OAAO,MAAM,cAAc,CAAA;AAGlC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAyB,YAAY,EAAE,kBAAkB,EAAsB,MAAM,YAAY,CAAA;AACxG,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAgB,MAAM,YAAY,CAAA;AAIrE,MAAM,CAAC,OAAO,OAAO,SAAS;WACb,UAAU,CACnB,OAAO,EAAE,OAAO,CAAC,SAAS,EAC1B,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAClC,aAAa,KAAK,EAClB,oBAAoB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAC/C,OAAO,CAAC,MAAM,CAAC;IAyDlB;;OAEG;IACH,MAAM,CAAC,eAAe,CAClB,OAAO,CAAC,EAAE,aAAa,EACvB,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAClC,aAAa,KAAK,EAClB,cAAc,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GACzC,MAAM;IA0BT;;;;;;OAMG;WACU,aAAa,CAAC,QAAQ,EAAE,MAAM;IAW3C,MAAM,KAAK,SAAS,qBAEnB;CACJ;AAED;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAA;AAC9D,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import logger from '@wdio/logger';
|
|
|
3
3
|
import { webdriverMonad, sessionEnvironmentDetector } from '@wdio/utils';
|
|
4
4
|
import { validateConfig } from '@wdio/config';
|
|
5
5
|
import command from './command.js';
|
|
6
|
+
import { startWebDriver } from './driver/index.js';
|
|
6
7
|
import { BidiHandler } from './bidi/handler.js';
|
|
7
8
|
import { DEFAULTS } from './constants.js';
|
|
8
9
|
import { startWebDriverSession, getPrototype, getEnvironmentVars, setupDirectConnect } from './utils.js';
|
|
@@ -23,11 +24,15 @@ export default class WebDriver {
|
|
|
23
24
|
}
|
|
24
25
|
log.info('Initiate new session using the WebDriver protocol');
|
|
25
26
|
const requestedCapabilities = { ...params.capabilities };
|
|
27
|
+
const driverProcess = await startWebDriver(params);
|
|
26
28
|
const { sessionId, capabilities } = await startWebDriverSession(params);
|
|
27
29
|
const environment = sessionEnvironmentDetector({ capabilities, requestedCapabilities });
|
|
28
30
|
const environmentPrototype = getEnvironmentVars(environment);
|
|
29
31
|
const protocolCommands = getPrototype(environment);
|
|
30
|
-
const
|
|
32
|
+
const driverPrototype = {
|
|
33
|
+
_driverProcess: { value: driverProcess, configurable: false, writable: true }
|
|
34
|
+
};
|
|
35
|
+
const prototype = { ...protocolCommands, ...environmentPrototype, ...userPrototype, ...driverPrototype };
|
|
31
36
|
const monad = webdriverMonad({ ...params, requestedCapabilities }, modifier, prototype);
|
|
32
37
|
let handler;
|
|
33
38
|
if (capabilities.webSocketUrl) {
|
package/build/utils.js
CHANGED
|
@@ -268,7 +268,7 @@ export const getSessionError = (err, params = {}) => {
|
|
|
268
268
|
// browser driver / service is not started
|
|
269
269
|
if (err.code === 'ECONNREFUSED') {
|
|
270
270
|
return `Unable to connect to "${params.protocol}://${params.hostname}:${params.port}${params.path}", make sure browser driver is running on that address.` +
|
|
271
|
-
'\
|
|
271
|
+
'\nIt seems like the service failed to start or is rejecting any connections.';
|
|
272
272
|
}
|
|
273
273
|
if (err.message === 'unhandled request') {
|
|
274
274
|
return 'The browser driver couldn\'t start the session. Make sure you have set the "path" correctly!';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webdriver",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.14.0",
|
|
4
4
|
"description": "A Node.js bindings implementation for the W3C WebDriver and Mobile JSONWire Protocol",
|
|
5
5
|
"author": "Christian Bromann <mail@bromann.dev>",
|
|
6
6
|
"homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/webdriver",
|
|
@@ -36,17 +36,25 @@
|
|
|
36
36
|
"url": "https://github.com/webdriverio/webdriverio/issues"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
+
"@puppeteer/browsers": "^1.4.6",
|
|
39
40
|
"@types/node": "^20.1.0",
|
|
40
41
|
"@types/ws": "^8.5.3",
|
|
41
|
-
"@wdio/config": "8.
|
|
42
|
+
"@wdio/config": "8.14.0",
|
|
42
43
|
"@wdio/logger": "8.11.0",
|
|
43
44
|
"@wdio/protocols": "8.11.0",
|
|
44
|
-
"@wdio/types": "8.
|
|
45
|
-
"@wdio/utils": "8.
|
|
46
|
-
"
|
|
45
|
+
"@wdio/types": "8.14.0",
|
|
46
|
+
"@wdio/utils": "8.14.0",
|
|
47
|
+
"chrome-launcher": "^1.0.0",
|
|
48
|
+
"decamelize": "^6.0.0",
|
|
49
|
+
"deepmerge-ts": "^5.1.0",
|
|
50
|
+
"edgedriver": "^5.3.2",
|
|
51
|
+
"geckodriver": "^4.1.3",
|
|
52
|
+
"get-port": "^7.0.0",
|
|
47
53
|
"got": "^ 12.6.1",
|
|
48
54
|
"ky": "^0.33.0",
|
|
55
|
+
"safaridriver": "^0.1.0",
|
|
56
|
+
"wait-port": "^1.0.4",
|
|
49
57
|
"ws": "^8.8.0"
|
|
50
58
|
},
|
|
51
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "cb2092b007e6d2ac23a49aa30dae67d70e45906d"
|
|
52
60
|
}
|