-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathlauncher.ts
134 lines (113 loc) · 5.82 KB
/
launcher.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import util from 'node:util';
import path from 'node:path';
import { readPackageUp, type NormalizedReadResult } from 'read-package-up';
import { SevereServiceError } from 'webdriverio';
import type { Services, Options, Capabilities } from '@wdio/types';
import log from '@wdio/electron-utils/log';
import { getAppBuildInfo, getBinaryPath, getElectronVersion } from '@wdio/electron-utils';
import { getChromeOptions, getChromedriverOptions, getElectronCapabilities } from './capabilities.js';
import { getChromiumVersion } from './versions.js';
import { APP_NOT_FOUND_ERROR, CUSTOM_CAPABILITY_NAME } from './constants.js';
import type { ElectronServiceOptions } from '@wdio/electron-types';
export type ElectronServiceCapabilities = Capabilities.TestrunnerCapabilities & {
[CUSTOM_CAPABILITY_NAME]?: ElectronServiceOptions;
};
export default class ElectronLaunchService implements Services.ServiceInstance {
#globalOptions: ElectronServiceOptions;
#projectRoot: string;
constructor(globalOptions: ElectronServiceOptions, _caps: unknown, config: Options.Testrunner) {
this.#globalOptions = globalOptions;
this.#projectRoot = config.rootDir || process.cwd();
}
async onPrepare(_config: Options.Testrunner, capabilities: ElectronServiceCapabilities) {
const capsList = Array.isArray(capabilities)
? capabilities
: Object.values(capabilities as Capabilities.RequestedMultiremoteCapabilities).map(
(multiremoteOption) => (multiremoteOption as Capabilities.WithRequestedCapabilities).capabilities,
);
const caps = capsList.flatMap((cap) => getElectronCapabilities(cap) as WebdriverIO.Capabilities);
const pkg =
(await readPackageUp({ cwd: this.#projectRoot })) ||
({ packageJson: { dependencies: {}, devDependencies: {} } } as NormalizedReadResult);
if (!caps.length) {
const noElectronCapabilityError = new Error('No Electron browser found in capabilities');
log.error(noElectronCapabilityError);
throw noElectronCapabilityError;
}
const localElectronVersion = getElectronVersion(pkg);
await Promise.all(
caps.map(async (cap) => {
const electronVersion = cap.browserVersion || localElectronVersion || '';
const chromiumVersion = await getChromiumVersion(electronVersion);
log.info(`Found Electron v${electronVersion} with Chromedriver v${chromiumVersion}`);
if (Number.parseInt(electronVersion.split('.')[0]) < 26 && !cap['wdio:chromedriverOptions']?.binary) {
const invalidElectronVersionError = new SevereServiceError(
'Electron version must be 26 or higher for auto-configuration of Chromedriver. If you want to use an older version of Electron, you must configure Chromedriver manually using the wdio:chromedriverOptions capability',
);
log.error(invalidElectronVersionError.message);
throw invalidElectronVersionError;
}
let {
appBinaryPath,
appEntryPoint,
appArgs = ['--no-sandbox'],
} = Object.assign({}, this.#globalOptions, cap[CUSTOM_CAPABILITY_NAME]);
if (appEntryPoint) {
if (appBinaryPath) {
log.warn('Both appEntryPoint and appBinaryPath are set, appBinaryPath will be ignored');
}
const electronBinary = process.platform === 'win32' ? 'electron.CMD' : 'electron';
appBinaryPath = path.join(this.#projectRoot, 'node_modules', '.bin', electronBinary);
appArgs = [`--app=${appEntryPoint}`, ...appArgs];
log.debug('App entry point: ', appEntryPoint, appBinaryPath, appArgs);
} else if (!appBinaryPath) {
log.info('No app binary specified, attempting to detect one...');
try {
const appBuildInfo = await getAppBuildInfo(pkg);
try {
appBinaryPath = await getBinaryPath(pkg.path, appBuildInfo, electronVersion);
log.info(`Detected app binary at ${appBinaryPath}`);
} catch (_e) {
const buildToolName = appBuildInfo.isForge ? 'Electron Forge' : 'electron-builder';
const suggestedCompileCommand = `npx ${
appBuildInfo.isForge ? 'electron-forge make' : 'electron-builder build'
}`;
throw new Error(util.format(APP_NOT_FOUND_ERROR, appBinaryPath, buildToolName, suggestedCompileCommand));
}
} catch (e) {
log.error(e);
throw new SevereServiceError((e as Error).message);
}
}
cap.browserName = 'chrome';
cap['goog:chromeOptions'] = getChromeOptions({ appBinaryPath, appArgs }, cap);
// disable WebDriver Bidi session
cap['wdio:enforceWebDriverClassic'] = true;
const chromedriverOptions = getChromedriverOptions(cap);
if (!chromiumVersion && Object.keys(chromedriverOptions).length > 0) {
cap['wdio:chromedriverOptions'] = chromedriverOptions;
}
const browserVersion = chromiumVersion || cap.browserVersion;
if (browserVersion) {
cap.browserVersion = browserVersion;
} else if (!cap['wdio:chromedriverOptions']?.binary) {
const invalidBrowserVersionOptsError = new Error(
'You must install Electron locally, or provide a custom Chromedriver path / browserVersion value for each Electron capability',
);
log.error(invalidBrowserVersionOptsError);
throw invalidBrowserVersionOptsError;
}
/**
* attach custom capability to be able to identify Electron instances
* in the worker process
*/
cap[CUSTOM_CAPABILITY_NAME] = cap[CUSTOM_CAPABILITY_NAME] || {};
log.debug('setting capability', cap);
}),
).catch((err) => {
const msg = `Failed setting up Electron session: ${err.stack}`;
log.error(msg);
throw new SevereServiceError(msg);
});
}
}