@taqwright/taqwright 0.0.24
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/LICENSE +201 -0
- package/README.md +108 -0
- package/dist/auto-appium.d.ts +12 -0
- package/dist/auto-appium.js +77 -0
- package/dist/bin/branding.d.ts +6 -0
- package/dist/bin/branding.js +22 -0
- package/dist/bin/index.d.ts +2 -0
- package/dist/bin/index.js +321 -0
- package/dist/bin/init.d.ts +26 -0
- package/dist/bin/init.js +902 -0
- package/dist/bin/inspect.d.ts +9 -0
- package/dist/bin/inspect.js +91 -0
- package/dist/bin/report-branding.d.ts +2 -0
- package/dist/bin/report-branding.js +42 -0
- package/dist/branding-assets.d.ts +1 -0
- package/dist/branding-assets.js +1 -0
- package/dist/capabilities-helpers.d.ts +7 -0
- package/dist/capabilities-helpers.js +14 -0
- package/dist/capabilities.d.ts +6 -0
- package/dist/capabilities.js +86 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +235 -0
- package/dist/discovery-setup.d.ts +1 -0
- package/dist/discovery-setup.js +61 -0
- package/dist/discovery.d.ts +17 -0
- package/dist/discovery.js +55 -0
- package/dist/docs/configuration.html +376 -0
- package/dist/docs/custom-reporters.html +265 -0
- package/dist/docs/docker.html +339 -0
- package/dist/docs/docs.js +173 -0
- package/dist/docs/generating-tests.html +161 -0
- package/dist/docs/images/taqwright-html-report.png +0 -0
- package/dist/docs/index.html +13 -0
- package/dist/docs/installation.html +686 -0
- package/dist/docs/parallel.html +271 -0
- package/dist/docs/running-tests.html +385 -0
- package/dist/docs/styles.css +460 -0
- package/dist/docs/writing-tests.html +565 -0
- package/dist/doctor.d.ts +33 -0
- package/dist/doctor.js +508 -0
- package/dist/expect.d.ts +38 -0
- package/dist/expect.js +96 -0
- package/dist/fixture/artifact-mode.d.ts +2 -0
- package/dist/fixture/artifact-mode.js +7 -0
- package/dist/fixture/index.d.ts +15 -0
- package/dist/fixture/index.js +324 -0
- package/dist/images/taqwright-html-report.png +0 -0
- package/dist/images/taqwright_favicon.png +0 -0
- package/dist/images/taqwright_logo.png +0 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +7 -0
- package/dist/inspector/codegen-appium.d.ts +3 -0
- package/dist/inspector/codegen-appium.js +228 -0
- package/dist/inspector/devices.d.ts +41 -0
- package/dist/inspector/devices.js +422 -0
- package/dist/inspector/locator-suggester.d.ts +23 -0
- package/dist/inspector/locator-suggester.js +539 -0
- package/dist/inspector/recorder.d.ts +128 -0
- package/dist/inspector/recorder.js +162 -0
- package/dist/inspector/server.d.ts +39 -0
- package/dist/inspector/server.js +1210 -0
- package/dist/inspector/session.d.ts +84 -0
- package/dist/inspector/session.js +262 -0
- package/dist/inspector/ui.d.ts +1 -0
- package/dist/inspector/ui.js +5508 -0
- package/dist/keys.d.ts +3 -0
- package/dist/keys.js +28 -0
- package/dist/locator/index.d.ts +206 -0
- package/dist/locator/index.js +1506 -0
- package/dist/logger.d.ts +5 -0
- package/dist/logger.js +5 -0
- package/dist/mobile/index.d.ts +130 -0
- package/dist/mobile/index.js +762 -0
- package/dist/network/android.d.ts +5 -0
- package/dist/network/android.js +87 -0
- package/dist/network/ca.d.ts +10 -0
- package/dist/network/ca.js +136 -0
- package/dist/network/har.d.ts +90 -0
- package/dist/network/har.js +101 -0
- package/dist/network/host-proxy.d.ts +16 -0
- package/dist/network/host-proxy.js +134 -0
- package/dist/network/index.d.ts +26 -0
- package/dist/network/index.js +105 -0
- package/dist/network/ios-sim.d.ts +3 -0
- package/dist/network/ios-sim.js +29 -0
- package/dist/network/proxy.d.ts +13 -0
- package/dist/network/proxy.js +310 -0
- package/dist/providers/appium.d.ts +23 -0
- package/dist/providers/appium.js +288 -0
- package/dist/providers/browserstack/index.d.ts +5 -0
- package/dist/providers/browserstack/index.js +77 -0
- package/dist/providers/browserstack/utils.d.ts +1 -0
- package/dist/providers/browserstack/utils.js +6 -0
- package/dist/providers/cloud.d.ts +53 -0
- package/dist/providers/cloud.js +117 -0
- package/dist/providers/emulator/index.d.ts +8 -0
- package/dist/providers/emulator/index.js +47 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.js +33 -0
- package/dist/providers/lambdatest/index.d.ts +28 -0
- package/dist/providers/lambdatest/index.js +99 -0
- package/dist/providers/lambdatest/utils.d.ts +1 -0
- package/dist/providers/lambdatest/utils.js +6 -0
- package/dist/providers/local/index.d.ts +9 -0
- package/dist/providers/local/index.js +53 -0
- package/dist/providers/local-session.d.ts +16 -0
- package/dist/providers/local-session.js +55 -0
- package/dist/setup/archive.d.ts +2 -0
- package/dist/setup/archive.js +43 -0
- package/dist/setup/avd.d.ts +12 -0
- package/dist/setup/avd.js +103 -0
- package/dist/setup/index.d.ts +6 -0
- package/dist/setup/index.js +55 -0
- package/dist/setup/install-android.d.ts +2 -0
- package/dist/setup/install-android.js +70 -0
- package/dist/setup/install-appium.d.ts +1 -0
- package/dist/setup/install-appium.js +64 -0
- package/dist/setup/install-jdk.d.ts +1 -0
- package/dist/setup/install-jdk.js +58 -0
- package/dist/setup/paths.d.ts +16 -0
- package/dist/setup/paths.js +88 -0
- package/dist/setup/spawn-tool.d.ts +3 -0
- package/dist/setup/spawn-tool.js +11 -0
- package/dist/tracer/index.d.ts +34 -0
- package/dist/tracer/index.js +687 -0
- package/dist/tracer/proxy.d.ts +3 -0
- package/dist/tracer/proxy.js +60 -0
- package/dist/types/index.d.ts +189 -0
- package/dist/types/index.js +6 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +37 -0
- package/package.json +79 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { findConfigFile, loadTaqwrightConfig, getUseOptions } from '../config.js';
|
|
4
|
+
import { buildCapabilities } from '../capabilities.js';
|
|
5
|
+
import { isPortOpen } from '../auto-appium.js';
|
|
6
|
+
import { startInspectorServer } from '../inspector/server.js';
|
|
7
|
+
export async function runInspect(opts) {
|
|
8
|
+
const defaults = await resolveDefaults(opts);
|
|
9
|
+
const host = opts.host ?? 'localhost';
|
|
10
|
+
const port = await pickFreePort(opts.port ? Number(opts.port) : 4280, host);
|
|
11
|
+
const handle = await startInspectorServer({ defaults, host, port });
|
|
12
|
+
let shuttingDown = false;
|
|
13
|
+
const shutdown = async (code = 0) => {
|
|
14
|
+
if (shuttingDown)
|
|
15
|
+
return;
|
|
16
|
+
shuttingDown = true;
|
|
17
|
+
console.log('\ntaqwright inspect: shutting down…');
|
|
18
|
+
try {
|
|
19
|
+
await handle.session.cleanup();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
await handle.close();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
}
|
|
28
|
+
process.exit(code);
|
|
29
|
+
};
|
|
30
|
+
process.on('SIGINT', () => void shutdown(0));
|
|
31
|
+
process.on('SIGTERM', () => void shutdown(0));
|
|
32
|
+
console.log(`taqwright inspect: ready at ${handle.url}`);
|
|
33
|
+
console.log('Press Ctrl+C to stop.');
|
|
34
|
+
if (opts.open !== false)
|
|
35
|
+
openBrowser(handle.url);
|
|
36
|
+
}
|
|
37
|
+
async function resolveDefaults(opts) {
|
|
38
|
+
const configPath = opts.config ?? (await findConfigFile());
|
|
39
|
+
const projectRoot = configPath ? dirname(configPath) : undefined;
|
|
40
|
+
const baseFallback = {
|
|
41
|
+
project: undefined,
|
|
42
|
+
projectRoot,
|
|
43
|
+
testDir: 'tests',
|
|
44
|
+
appium: { host: 'localhost', port: 4723, path: '/' },
|
|
45
|
+
capabilities: {
|
|
46
|
+
platformName: 'Android',
|
|
47
|
+
'appium:automationName': 'UiAutomator2',
|
|
48
|
+
'appium:noReset': true,
|
|
49
|
+
'appium:newCommandTimeout': 240,
|
|
50
|
+
},
|
|
51
|
+
recordOnConnect: !!opts.record,
|
|
52
|
+
};
|
|
53
|
+
if (!configPath)
|
|
54
|
+
return baseFallback;
|
|
55
|
+
const cfg = await loadTaqwrightConfig(dirname(configPath));
|
|
56
|
+
const useOpts = getUseOptions(cfg, opts.project);
|
|
57
|
+
if (!useOpts) {
|
|
58
|
+
console.log('taqwright inspect: no project resolved from config — using built-in defaults.');
|
|
59
|
+
return { ...baseFallback, testDir: cfg?.testDir ?? 'tests' };
|
|
60
|
+
}
|
|
61
|
+
const projectName = opts.project ?? cfg?.projects.find((p) => p.use === useOpts)?.name ?? cfg?.projects[0]?.name;
|
|
62
|
+
return {
|
|
63
|
+
project: projectName,
|
|
64
|
+
projectRoot,
|
|
65
|
+
testDir: cfg?.testDir ?? 'tests',
|
|
66
|
+
appium: {
|
|
67
|
+
host: useOpts.appium?.host ?? 'localhost',
|
|
68
|
+
port: useOpts.appium?.port ?? 4723,
|
|
69
|
+
path: useOpts.appium?.path ?? '/',
|
|
70
|
+
},
|
|
71
|
+
capabilities: buildCapabilities(useOpts),
|
|
72
|
+
recordOnConnect: !!opts.record,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function pickFreePort(preferred, host) {
|
|
76
|
+
for (let p = preferred; p < preferred + 20; p++) {
|
|
77
|
+
if (!(await isPortOpen(host, p)))
|
|
78
|
+
return p;
|
|
79
|
+
}
|
|
80
|
+
return preferred;
|
|
81
|
+
}
|
|
82
|
+
function openBrowser(url) {
|
|
83
|
+
const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open';
|
|
84
|
+
const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];
|
|
85
|
+
try {
|
|
86
|
+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
|
|
87
|
+
child.unref();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { TAQWRIGHT_FAVICON_DATA_URI } from '../branding-assets.js';
|
|
4
|
+
const SENTINEL = '<!--taqwright-branding-->';
|
|
5
|
+
const REPORT_TITLE = 'Taqwright Test Report';
|
|
6
|
+
function injectionMarkup() {
|
|
7
|
+
const icon = JSON.stringify(TAQWRIGHT_FAVICON_DATA_URI);
|
|
8
|
+
const title = JSON.stringify(REPORT_TITLE);
|
|
9
|
+
const script = `(function(){` +
|
|
10
|
+
`var ICON=${icon};var TITLE=${title};` +
|
|
11
|
+
`function apply(){try{` +
|
|
12
|
+
`document.title=TITLE;` +
|
|
13
|
+
`var links=document.head?document.head.querySelectorAll('link[rel~="icon"]'):[];` +
|
|
14
|
+
`for(var i=0;i<links.length;i++){var el=links[i];if(el.hasAttribute('data-tw-icon'))continue;if(el.parentNode)el.parentNode.removeChild(el);}` +
|
|
15
|
+
`if(document.head&&!document.head.querySelector('link[data-tw-icon]')){` +
|
|
16
|
+
`var l=document.createElement('link');l.setAttribute('rel','icon');l.setAttribute('type','image/png');l.setAttribute('href',ICON);l.setAttribute('data-tw-icon','');document.head.appendChild(l);}` +
|
|
17
|
+
`}catch(e){}}` +
|
|
18
|
+
`apply();` +
|
|
19
|
+
`document.addEventListener('DOMContentLoaded',apply);` +
|
|
20
|
+
`window.addEventListener('load',apply);` +
|
|
21
|
+
`try{var mo=new MutationObserver(apply);mo.observe(document.head||document.documentElement,{childList:true});}catch(e){}` +
|
|
22
|
+
`var n=0,t=setInterval(function(){apply();if(++n>10)clearInterval(t);},500);` +
|
|
23
|
+
`})();`;
|
|
24
|
+
return `${SENTINEL}<script>${script}</script>`;
|
|
25
|
+
}
|
|
26
|
+
export function brandReportHtml(html) {
|
|
27
|
+
if (html.includes(SENTINEL))
|
|
28
|
+
return html;
|
|
29
|
+
const idx = html.indexOf('</head>');
|
|
30
|
+
if (idx === -1)
|
|
31
|
+
return html;
|
|
32
|
+
return html.slice(0, idx) + injectionMarkup() + html.slice(idx);
|
|
33
|
+
}
|
|
34
|
+
export function brandReportDir(dir) {
|
|
35
|
+
const file = join(dir, 'index.html');
|
|
36
|
+
if (!existsSync(file))
|
|
37
|
+
return;
|
|
38
|
+
const html = readFileSync(file, 'utf8');
|
|
39
|
+
const branded = brandReportHtml(html);
|
|
40
|
+
if (branded !== html)
|
|
41
|
+
writeFileSync(file, branded);
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const TAQWRIGHT_FAVICON_DATA_URI = "data:image/png;base64,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";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const TAQWRIGHT_FAVICON_DATA_URI = 'data:image/png;base64,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';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface IosParallelCapsOptions {
|
|
2
|
+
useNewWDA?: boolean;
|
|
3
|
+
wdaStartupRetries?: number;
|
|
4
|
+
wdaConnectionTimeout?: number;
|
|
5
|
+
wdaLaunchTimeout?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function iosParallelCaps(slot: number, opts?: IosParallelCapsOptions): Record<string, unknown>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function iosParallelCaps(slot, opts = {}) {
|
|
2
|
+
if (!Number.isInteger(slot) || slot < 0) {
|
|
3
|
+
throw new Error(`iosParallelCaps: slot must be a non-negative integer, got ${slot}`);
|
|
4
|
+
}
|
|
5
|
+
return {
|
|
6
|
+
'appium:wdaLocalPort': 8100 + slot,
|
|
7
|
+
'appium:mjpegServerPort': 9100 + slot,
|
|
8
|
+
'appium:derivedDataPath': `/tmp/wda-${slot}`,
|
|
9
|
+
'appium:useNewWDA': opts.useNewWDA ?? true,
|
|
10
|
+
'appium:wdaStartupRetries': opts.wdaStartupRetries ?? 4,
|
|
11
|
+
'appium:wdaConnectionTimeout': opts.wdaConnectionTimeout ?? 360_000,
|
|
12
|
+
'appium:wdaLaunchTimeout': opts.wdaLaunchTimeout ?? 120_000,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Capabilities } from '@wdio/types';
|
|
2
|
+
import { type TaqwrightUseOptions } from './types/index.js';
|
|
3
|
+
export declare function buildCapabilities(use: TaqwrightUseOptions): Capabilities.RequestedStandaloneCapabilities;
|
|
4
|
+
export declare const LOCAL_EMULATOR_CAP_KEYS: string[];
|
|
5
|
+
export declare function omitLocalEmulatorCaps(caps: Record<string, unknown>): Record<string, unknown>;
|
|
6
|
+
export declare function appiumRemoteOptions(use: TaqwrightUseOptions): Capabilities.RemoteConfig;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Platform } from './types/index.js';
|
|
2
|
+
export function buildCapabilities(use) {
|
|
3
|
+
const isIOS = use.platform === Platform.IOS;
|
|
4
|
+
const caps = {
|
|
5
|
+
platformName: isIOS ? 'iOS' : 'Android',
|
|
6
|
+
'appium:automationName': isIOS ? 'XCUITest' : 'UiAutomator2',
|
|
7
|
+
'appium:newCommandTimeout': use.appium?.newCommandTimeout ?? 240,
|
|
8
|
+
'appium:noReset': true,
|
|
9
|
+
};
|
|
10
|
+
const provider = use.device.provider;
|
|
11
|
+
const userCaps = use.capabilities ?? {};
|
|
12
|
+
const autoStartDevice = use.appium?.autoStartDevice !== false;
|
|
13
|
+
const androidAutoBoot = autoStartDevice &&
|
|
14
|
+
!isIOS &&
|
|
15
|
+
provider === 'emulator' &&
|
|
16
|
+
typeof use.device.name === 'string' &&
|
|
17
|
+
use.device.name.length > 0;
|
|
18
|
+
const declaredUdid = use.device.udid;
|
|
19
|
+
if (declaredUdid && !(androidAutoBoot && !('appium:udid' in userCaps))) {
|
|
20
|
+
caps['appium:udid'] = declaredUdid;
|
|
21
|
+
}
|
|
22
|
+
if (use.device.name) {
|
|
23
|
+
caps['appium:deviceName'] =
|
|
24
|
+
typeof use.device.name === 'string' ? use.device.name : use.device.name.source;
|
|
25
|
+
}
|
|
26
|
+
if (use.device.osVersion) {
|
|
27
|
+
caps['appium:platformVersion'] = use.device.osVersion;
|
|
28
|
+
}
|
|
29
|
+
if (use.buildPath) {
|
|
30
|
+
caps['appium:app'] = use.buildPath;
|
|
31
|
+
}
|
|
32
|
+
if (use.appBundleId) {
|
|
33
|
+
if (isIOS)
|
|
34
|
+
caps['appium:bundleId'] = use.appBundleId;
|
|
35
|
+
else
|
|
36
|
+
caps['appium:appPackage'] = use.appBundleId;
|
|
37
|
+
}
|
|
38
|
+
const isCloudProvider_ = provider === 'browserstack' || provider === 'lambdatest';
|
|
39
|
+
if (use.resetBetweenTests &&
|
|
40
|
+
use.buildPath &&
|
|
41
|
+
use.appBundleId &&
|
|
42
|
+
!isCloudProvider_ &&
|
|
43
|
+
!('appium:autoLaunch' in userCaps)) {
|
|
44
|
+
caps['appium:autoLaunch'] = false;
|
|
45
|
+
}
|
|
46
|
+
if (androidAutoBoot) {
|
|
47
|
+
caps['appium:avd'] = use.device.name;
|
|
48
|
+
if (!('appium:avdLaunchTimeout' in userCaps)) {
|
|
49
|
+
caps['appium:avdLaunchTimeout'] = 120_000;
|
|
50
|
+
}
|
|
51
|
+
if (!('appium:avdReadyTimeout' in userCaps)) {
|
|
52
|
+
caps['appium:avdReadyTimeout'] = 120_000;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (autoStartDevice && isIOS && provider === 'emulator') {
|
|
56
|
+
if (!('appium:simulatorStartupTimeout' in userCaps)) {
|
|
57
|
+
caps['appium:simulatorStartupTimeout'] = 120_000;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (isIOS && !('appium:forceSimulatorSoftwareKeyboardPresence' in userCaps)) {
|
|
61
|
+
caps['appium:forceSimulatorSoftwareKeyboardPresence'] = true;
|
|
62
|
+
}
|
|
63
|
+
return { ...caps, ...userCaps };
|
|
64
|
+
}
|
|
65
|
+
export const LOCAL_EMULATOR_CAP_KEYS = [
|
|
66
|
+
'appium:avd',
|
|
67
|
+
'appium:avdLaunchTimeout',
|
|
68
|
+
'appium:avdReadyTimeout',
|
|
69
|
+
];
|
|
70
|
+
export function omitLocalEmulatorCaps(caps) {
|
|
71
|
+
const out = {};
|
|
72
|
+
for (const [k, v] of Object.entries(caps)) {
|
|
73
|
+
if (!LOCAL_EMULATOR_CAP_KEYS.includes(k))
|
|
74
|
+
out[k] = v;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
export function appiumRemoteOptions(use) {
|
|
79
|
+
return {
|
|
80
|
+
hostname: use.appium?.host ?? 'localhost',
|
|
81
|
+
port: use.appium?.port ?? 4723,
|
|
82
|
+
path: use.appium?.path ?? '/',
|
|
83
|
+
logLevel: use.appium?.logLevel ?? 'warn',
|
|
84
|
+
capabilities: buildCapabilities(use),
|
|
85
|
+
};
|
|
86
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PlaywrightTestConfig } from '@playwright/test';
|
|
2
|
+
import { Platform, type TaqwrightConfig, type TaqwrightProjectConfig } from './types/index.js';
|
|
3
|
+
export declare const TAQWRIGHT_KEY = "__taqwright__";
|
|
4
|
+
export interface PlaywrightConfigWithEmbedded extends PlaywrightTestConfig {
|
|
5
|
+
[TAQWRIGHT_KEY]?: TaqwrightConfig;
|
|
6
|
+
}
|
|
7
|
+
export declare function findConfigFile(cwd?: string): Promise<string | undefined>;
|
|
8
|
+
export declare function loadTaqwrightConfig(cwd?: string): Promise<TaqwrightConfig | undefined>;
|
|
9
|
+
export declare function loadPlaywrightConfig(cwd?: string): Promise<PlaywrightConfigWithEmbedded>;
|
|
10
|
+
export declare function effectiveWorkers(project: TaqwrightProjectConfig, config: TaqwrightConfig): number;
|
|
11
|
+
export declare function resolveCliWorkers(config: TaqwrightConfig, projectFilter: string[], userWorkers?: string | number): number | undefined;
|
|
12
|
+
export declare function defineConfig(config: TaqwrightConfig): PlaywrightConfigWithEmbedded;
|
|
13
|
+
export declare function findParallelMisconfig(config: TaqwrightConfig): string | null;
|
|
14
|
+
export declare function findAutoStartDeviceMisconfig(config: TaqwrightConfig): string | null;
|
|
15
|
+
export declare function findAutoDiscoverMisconfig(config: TaqwrightConfig): string | null;
|
|
16
|
+
export declare function getUseOptions(config: TaqwrightConfig | undefined, projectName?: string): TaqwrightConfig['projects'][number]['use'] | undefined;
|
|
17
|
+
export { Platform };
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { access } from 'node:fs/promises';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
+
import { Platform } from './types/index.js';
|
|
5
|
+
export const TAQWRIGHT_KEY = '__taqwright__';
|
|
6
|
+
const CONFIG_FILES = [
|
|
7
|
+
'taqwright.config.ts',
|
|
8
|
+
'taqwright.config.mts',
|
|
9
|
+
'taqwright.config.js',
|
|
10
|
+
'taqwright.config.mjs',
|
|
11
|
+
];
|
|
12
|
+
export async function findConfigFile(cwd = process.cwd()) {
|
|
13
|
+
let dir = resolve(cwd);
|
|
14
|
+
while (true) {
|
|
15
|
+
for (const name of CONFIG_FILES) {
|
|
16
|
+
const candidate = join(dir, name);
|
|
17
|
+
try {
|
|
18
|
+
await access(candidate);
|
|
19
|
+
return candidate;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const parent = resolve(dir, '..');
|
|
25
|
+
if (parent === dir)
|
|
26
|
+
return undefined;
|
|
27
|
+
dir = parent;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function importConfigFile(filePath) {
|
|
31
|
+
const mod = await import(pathToFileURL(filePath).href);
|
|
32
|
+
let config = mod.default ?? mod;
|
|
33
|
+
if (config && typeof config === 'object' && 'default' in config) {
|
|
34
|
+
config = config.default;
|
|
35
|
+
}
|
|
36
|
+
return config;
|
|
37
|
+
}
|
|
38
|
+
export async function loadTaqwrightConfig(cwd) {
|
|
39
|
+
const file = await findConfigFile(cwd);
|
|
40
|
+
if (!file)
|
|
41
|
+
return undefined;
|
|
42
|
+
const cfg = await importConfigFile(file);
|
|
43
|
+
return cfg[TAQWRIGHT_KEY];
|
|
44
|
+
}
|
|
45
|
+
export async function loadPlaywrightConfig(cwd) {
|
|
46
|
+
const file = await findConfigFile(cwd);
|
|
47
|
+
if (!file)
|
|
48
|
+
return {};
|
|
49
|
+
return importConfigFile(file);
|
|
50
|
+
}
|
|
51
|
+
export function effectiveWorkers(project, config) {
|
|
52
|
+
return project.workers ?? config.workers ?? 1;
|
|
53
|
+
}
|
|
54
|
+
export function resolveCliWorkers(config, projectFilter, userWorkers) {
|
|
55
|
+
if (userWorkers !== undefined) {
|
|
56
|
+
const n = typeof userWorkers === 'number' ? userWorkers : Number.parseInt(userWorkers, 10);
|
|
57
|
+
return Number.isFinite(n) ? n : undefined;
|
|
58
|
+
}
|
|
59
|
+
let target;
|
|
60
|
+
if (projectFilter.length === 1) {
|
|
61
|
+
target = config.projects.find((p) => p.name === projectFilter[0]);
|
|
62
|
+
}
|
|
63
|
+
else if (projectFilter.length === 0 && config.projects.length === 1) {
|
|
64
|
+
target = config.projects[0];
|
|
65
|
+
}
|
|
66
|
+
return target ? effectiveWorkers(target, config) : undefined;
|
|
67
|
+
}
|
|
68
|
+
export function defineConfig(config) {
|
|
69
|
+
if (!config.projects || config.projects.length === 0) {
|
|
70
|
+
throw new Error('taqwright defineConfig: at least one project is required');
|
|
71
|
+
}
|
|
72
|
+
const parallelMisconfig = findParallelMisconfig(config);
|
|
73
|
+
if (parallelMisconfig)
|
|
74
|
+
throw new Error(parallelMisconfig);
|
|
75
|
+
const autoStartMisconfig = findAutoStartDeviceMisconfig(config);
|
|
76
|
+
if (autoStartMisconfig)
|
|
77
|
+
throw new Error(autoStartMisconfig);
|
|
78
|
+
const autoDiscoverMisconfig = findAutoDiscoverMisconfig(config);
|
|
79
|
+
if (autoDiscoverMisconfig)
|
|
80
|
+
throw new Error(autoDiscoverMisconfig);
|
|
81
|
+
const hasAutoDiscover = config.projects.some((p) => p.use.device.autoDiscover === true);
|
|
82
|
+
const hasAndroidPoolAutoBoot = config.projects.some((p) => {
|
|
83
|
+
const device = p.use.device;
|
|
84
|
+
return (p.use.platform === Platform.ANDROID &&
|
|
85
|
+
device.provider === 'emulator' &&
|
|
86
|
+
Array.isArray(device.pool) &&
|
|
87
|
+
device.pool.length > 0 &&
|
|
88
|
+
device.pool.every((e) => typeof e.name === 'string' && e.name.length > 0) &&
|
|
89
|
+
p.use.appium?.autoStartDevice !== false);
|
|
90
|
+
});
|
|
91
|
+
let globalSetup = config.globalSetup;
|
|
92
|
+
if (hasAutoDiscover || hasAndroidPoolAutoBoot) {
|
|
93
|
+
const internal = fileURLToPath(new URL('./discovery-setup.js', import.meta.url));
|
|
94
|
+
const existing = config.globalSetup
|
|
95
|
+
? Array.isArray(config.globalSetup)
|
|
96
|
+
? config.globalSetup
|
|
97
|
+
: [config.globalSetup]
|
|
98
|
+
: [];
|
|
99
|
+
globalSetup = [internal, ...existing];
|
|
100
|
+
}
|
|
101
|
+
const pwConfig = {
|
|
102
|
+
workers: Math.max(1, ...config.projects.map((p) => effectiveWorkers(p, config))),
|
|
103
|
+
fullyParallel: config.fullyParallel ?? false,
|
|
104
|
+
forbidOnly: config.forbidOnly,
|
|
105
|
+
timeout: config.timeout,
|
|
106
|
+
retries: config.retries,
|
|
107
|
+
outputDir: config.outputDir,
|
|
108
|
+
testDir: config.testDir,
|
|
109
|
+
testMatch: config.testMatch,
|
|
110
|
+
testIgnore: config.testIgnore,
|
|
111
|
+
reporter: config.reporter,
|
|
112
|
+
globalSetup,
|
|
113
|
+
globalTeardown: config.globalTeardown,
|
|
114
|
+
projects: config.projects.map((p) => ({
|
|
115
|
+
name: p.name,
|
|
116
|
+
timeout: p.timeout,
|
|
117
|
+
retries: p.retries,
|
|
118
|
+
testDir: p.testDir,
|
|
119
|
+
testMatch: p.testMatch,
|
|
120
|
+
testIgnore: p.testIgnore,
|
|
121
|
+
outputDir: p.outputDir,
|
|
122
|
+
grep: p.grep,
|
|
123
|
+
grepInvert: p.grepInvert,
|
|
124
|
+
dependencies: p.dependencies,
|
|
125
|
+
use: {
|
|
126
|
+
taqwrightProject: p.name,
|
|
127
|
+
},
|
|
128
|
+
})),
|
|
129
|
+
[TAQWRIGHT_KEY]: config,
|
|
130
|
+
};
|
|
131
|
+
return pwConfig;
|
|
132
|
+
}
|
|
133
|
+
export function findParallelMisconfig(config) {
|
|
134
|
+
const problems = [];
|
|
135
|
+
for (const project of config.projects) {
|
|
136
|
+
const workers = effectiveWorkers(project, config);
|
|
137
|
+
if (workers <= 1)
|
|
138
|
+
continue;
|
|
139
|
+
const device = project.use.device;
|
|
140
|
+
if (device.provider !== 'emulator' && device.provider !== 'local-device') {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (device.autoDiscover === true)
|
|
144
|
+
continue;
|
|
145
|
+
const pool = device.pool;
|
|
146
|
+
if (!pool || pool.length === 0) {
|
|
147
|
+
problems.push(`\`workers\` is ${workers} but project "${project.name}" ` +
|
|
148
|
+
`(provider: ${device.provider}) has no \`device.pool\`. Parallel ` +
|
|
149
|
+
`runs need a \`device.pool\` with at least ${workers} entries — ` +
|
|
150
|
+
`add one, or set \`workers: 1\`. Without a pool, multiple workers ` +
|
|
151
|
+
`collide on one device.`);
|
|
152
|
+
}
|
|
153
|
+
else if (pool.length < workers) {
|
|
154
|
+
problems.push(`\`workers\` is ${workers} but project "${project.name}"'s ` +
|
|
155
|
+
`\`device.pool\` has only ${pool.length} ` +
|
|
156
|
+
`entr${pool.length === 1 ? 'y' : 'ies'}. Grow it to at least ` +
|
|
157
|
+
`${workers}, or lower \`workers\`.`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return problems.length ? `taqwright: ${problems.join('\ntaqwright: ')}` : null;
|
|
161
|
+
}
|
|
162
|
+
export function findAutoStartDeviceMisconfig(config) {
|
|
163
|
+
const problems = [];
|
|
164
|
+
for (const project of config.projects) {
|
|
165
|
+
const use = project.use;
|
|
166
|
+
if (use.appium?.autoStartDevice !== true)
|
|
167
|
+
continue;
|
|
168
|
+
const device = use.device;
|
|
169
|
+
if (device.provider !== 'emulator' || use.platform !== Platform.ANDROID) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (device.autoDiscover === true)
|
|
173
|
+
continue;
|
|
174
|
+
const pool = device.pool;
|
|
175
|
+
const ok = pool
|
|
176
|
+
? pool.every((e) => typeof e.name === 'string' && e.name.length > 0)
|
|
177
|
+
: typeof device.name === 'string' && device.name.length > 0;
|
|
178
|
+
if (!ok) {
|
|
179
|
+
problems.push(`\`appium.autoStartDevice\` is set on project "${project.name}" ` +
|
|
180
|
+
`but it has no concrete AVD name to boot. autoStartDevice needs ` +
|
|
181
|
+
`a string \`device.name\` (or a string \`name\` on every ` +
|
|
182
|
+
`\`device.pool\` entry) equal to the AVD id (e.g. ` +
|
|
183
|
+
`'Pixel_7_API_34' — see \`emulator -list-avds\`). A RegExp ` +
|
|
184
|
+
`\`device.name\` can't be booted. Set a string name, or remove ` +
|
|
185
|
+
`\`appium.autoStartDevice\`.`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return problems.length ? `taqwright: ${problems.join('\ntaqwright: ')}` : null;
|
|
189
|
+
}
|
|
190
|
+
export function findAutoDiscoverMisconfig(config) {
|
|
191
|
+
const problems = [];
|
|
192
|
+
for (const project of config.projects) {
|
|
193
|
+
const device = project.use.device;
|
|
194
|
+
if (device.autoDiscover !== true)
|
|
195
|
+
continue;
|
|
196
|
+
if (device.provider !== 'emulator' && device.provider !== 'local-device') {
|
|
197
|
+
problems.push(`\`device.autoDiscover\` is set on project "${project.name}" but its ` +
|
|
198
|
+
`provider is "${device.provider}". Auto-discovery is for local ` +
|
|
199
|
+
`providers only (emulator / local-device); cloud grids manage their ` +
|
|
200
|
+
`own device queueing — remove \`autoDiscover\`.`);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if ((device.pool && device.pool.length > 0) || device.udid) {
|
|
204
|
+
problems.push(`\`device.autoDiscover\` on project "${project.name}" is mutually ` +
|
|
205
|
+
`exclusive with \`device.${device.pool?.length ? 'pool' : 'udid'}\` — ` +
|
|
206
|
+
`set one or the other. autoDiscover resolves the device set for you.`);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (device.provider === 'local-device' && project.use.platform === Platform.IOS) {
|
|
210
|
+
problems.push(`\`device.autoDiscover\` on project "${project.name}" is not yet ` +
|
|
211
|
+
`supported for local-device + iOS — there's no multi-device ` +
|
|
212
|
+
`enumerator for physical iPhones. Set \`device.udid\` or \`device.pool\`.`);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (device.provider === 'emulator' &&
|
|
216
|
+
project.use.platform === Platform.ANDROID &&
|
|
217
|
+
project.use.appium?.autoStartDevice === false) {
|
|
218
|
+
problems.push(`\`device.autoDiscover\` on project "${project.name}" needs ` +
|
|
219
|
+
`\`appium.autoStartDevice\` to boot/attach AVDs, but it's set to ` +
|
|
220
|
+
`false. Remove \`appium.autoStartDevice: false\`.`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return problems.length ? `taqwright: ${problems.join('\ntaqwright: ')}` : null;
|
|
224
|
+
}
|
|
225
|
+
export function getUseOptions(config, projectName) {
|
|
226
|
+
if (!config)
|
|
227
|
+
return undefined;
|
|
228
|
+
if (projectName) {
|
|
229
|
+
const match = config.projects.find((p) => p.name === projectName);
|
|
230
|
+
if (match)
|
|
231
|
+
return match.use;
|
|
232
|
+
}
|
|
233
|
+
return config.projects[0]?.use;
|
|
234
|
+
}
|
|
235
|
+
export { Platform };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function autoDiscoverGlobalSetup(): Promise<void>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Platform } from './types/index.js';
|
|
2
|
+
import { loadTaqwrightConfig, effectiveWorkers } from './config.js';
|
|
3
|
+
import { discoverAssignableDevices, selectDevicePool, resolvedPoolEnvKey, } from './discovery.js';
|
|
4
|
+
import { startIosSimulator, ensureAndroidAvdReady } from './inspector/devices.js';
|
|
5
|
+
import { logger } from './logger.js';
|
|
6
|
+
export default async function autoDiscoverGlobalSetup() {
|
|
7
|
+
const config = await loadTaqwrightConfig();
|
|
8
|
+
if (!config)
|
|
9
|
+
return;
|
|
10
|
+
for (const project of config.projects) {
|
|
11
|
+
const device = project.use.device;
|
|
12
|
+
const autoStartDevice = project.use.appium?.autoStartDevice !== false;
|
|
13
|
+
const isAutoDiscover = device.autoDiscover === true &&
|
|
14
|
+
(device.provider === 'emulator' || device.provider === 'local-device');
|
|
15
|
+
const isStaticAndroidPool = !device.autoDiscover &&
|
|
16
|
+
device.provider === 'emulator' &&
|
|
17
|
+
project.use.platform === Platform.ANDROID &&
|
|
18
|
+
Array.isArray(device.pool) &&
|
|
19
|
+
device.pool.length > 0 &&
|
|
20
|
+
autoStartDevice;
|
|
21
|
+
if (!isAutoDiscover && !isStaticAndroidPool)
|
|
22
|
+
continue;
|
|
23
|
+
if (isStaticAndroidPool) {
|
|
24
|
+
for (const entry of device.pool) {
|
|
25
|
+
if (typeof entry.name !== 'string' || !entry.name)
|
|
26
|
+
continue;
|
|
27
|
+
await ensureAndroidAvdReady(entry.name).catch((err) => {
|
|
28
|
+
logger.warn(`taqwright: could not pre-boot AVD ${entry.name} — ${err.message}`);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
logger.log(`taqwright: pre-booted ${device.pool.length} emulator(s) for project "${project.name}".`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const workers = effectiveWorkers(project, config);
|
|
35
|
+
const opts = {
|
|
36
|
+
platform: project.use.platform,
|
|
37
|
+
provider: device.provider,
|
|
38
|
+
osVersion: device.osVersion,
|
|
39
|
+
name: device.name,
|
|
40
|
+
};
|
|
41
|
+
const slots = await discoverAssignableDevices(opts);
|
|
42
|
+
const pool = selectDevicePool(slots, workers);
|
|
43
|
+
if (project.use.platform === Platform.IOS) {
|
|
44
|
+
for (const entry of pool) {
|
|
45
|
+
await startIosSimulator(entry.udid).catch((err) => {
|
|
46
|
+
logger.warn(`taqwright: could not pre-boot simulator ${entry.udid} — ${err.message}`);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else if (device.provider === 'emulator' && autoStartDevice) {
|
|
51
|
+
for (const entry of pool) {
|
|
52
|
+
const avd = entry.name ?? entry.udid.replace(/^avd:/, '');
|
|
53
|
+
await ensureAndroidAvdReady(avd).catch((err) => {
|
|
54
|
+
logger.warn(`taqwright: could not pre-boot AVD ${avd} — ${err.message}`);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
process.env[resolvedPoolEnvKey(project.name)] = JSON.stringify(pool);
|
|
59
|
+
logger.log(`taqwright: autoDiscover resolved ${pool.length} device(s) for project "${project.name}".`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Platform, type DevicePoolEntry } from './types/index.js';
|
|
2
|
+
import { type DeviceListing } from './inspector/devices.js';
|
|
3
|
+
export interface AssignableSlot {
|
|
4
|
+
udid: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
osVersion?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DiscoverOpts {
|
|
9
|
+
platform: Platform;
|
|
10
|
+
provider: 'emulator' | 'local-device';
|
|
11
|
+
osVersion?: string;
|
|
12
|
+
name?: string | RegExp;
|
|
13
|
+
}
|
|
14
|
+
export declare function toAssignableSlots(listing: DeviceListing, opts: DiscoverOpts): AssignableSlot[];
|
|
15
|
+
export declare function selectDevicePool(slots: AssignableSlot[], workers: number): DevicePoolEntry[];
|
|
16
|
+
export declare function discoverAssignableDevices(opts: DiscoverOpts): Promise<AssignableSlot[]>;
|
|
17
|
+
export declare function resolvedPoolEnvKey(projectName: string | undefined): string;
|