@wdio/browser-runner 9.0.0-alpha.78 → 9.0.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/browser/driver.js +249 -234
- package/build/browser/expect.js +107 -148
- package/build/browser/frameworks/mocha.d.ts.map +1 -1
- package/build/browser/integrations/stencil.js +370 -407
- package/build/browser/mock.d.ts +3 -2
- package/build/browser/mock.d.ts.map +1 -1
- package/build/browser/mock.js +78 -34
- package/build/browser/setup.js +313 -37
- package/build/browser/spy.d.ts.map +1 -1
- package/build/browser/spy.js +29 -40
- package/build/browser/utils.d.ts +7 -0
- package/build/browser/utils.d.ts.map +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1465 -171
- package/build/types.d.ts +19 -2
- package/build/types.d.ts.map +1 -1
- package/build/utils.d.ts +3 -3
- package/build/utils.d.ts.map +1 -1
- package/build/vite/constants.d.ts +3 -0
- package/build/vite/constants.d.ts.map +1 -1
- package/build/vite/plugins/esbuild.d.ts.map +1 -1
- package/build/vite/plugins/testrunner.d.ts.map +1 -1
- package/build/vite/server.d.ts +0 -1
- package/build/vite/server.d.ts.map +1 -1
- package/build/vite/utils.d.ts.map +1 -1
- package/package.json +57 -26
- package/build/browser/commands/debug.js +0 -6
- package/build/browser/frameworks/mocha.js +0 -320
- package/build/browser/utils.js +0 -61
- package/build/communicator.js +0 -82
- package/build/constants.js +0 -89
- package/build/types.js +0 -1
- package/build/utils.js +0 -86
- package/build/vite/constants.js +0 -55
- package/build/vite/frameworks/index.js +0 -19
- package/build/vite/frameworks/nuxt.js +0 -61
- package/build/vite/frameworks/stencil.js +0 -165
- package/build/vite/frameworks/tailwindcss.js +0 -28
- package/build/vite/mock.js +0 -50
- package/build/vite/plugins/esbuild.js +0 -25
- package/build/vite/plugins/mockHoisting.js +0 -312
- package/build/vite/plugins/testrunner.js +0 -152
- package/build/vite/plugins/worker.js +0 -12
- package/build/vite/server.js +0 -104
- package/build/vite/types.js +0 -1
- package/build/vite/utils.js +0 -223
- /package/{LICENSE-MIT → LICENSE} +0 -0
package/build/communicator.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import libSourceMap from 'istanbul-lib-source-maps';
|
|
2
|
-
import libCoverage from 'istanbul-lib-coverage';
|
|
3
|
-
import logger from '@wdio/logger';
|
|
4
|
-
import { MESSAGE_TYPES } from '@wdio/types';
|
|
5
|
-
import { SESSIONS } from './constants.js';
|
|
6
|
-
import { WDIO_EVENT_NAME } from './constants.js';
|
|
7
|
-
const log = logger('@wdio/browser-runner');
|
|
8
|
-
export class ServerWorkerCommunicator {
|
|
9
|
-
#mapStore = libSourceMap.createSourceMapStore();
|
|
10
|
-
#config;
|
|
11
|
-
#msgId = 0;
|
|
12
|
-
/**
|
|
13
|
-
* keep track of custom commands per session
|
|
14
|
-
*/
|
|
15
|
-
#customCommands = new Map();
|
|
16
|
-
/**
|
|
17
|
-
* keep track of request/response messages on browser/worker level
|
|
18
|
-
*/
|
|
19
|
-
#pendingMessages = new Map();
|
|
20
|
-
coverageMaps = [];
|
|
21
|
-
constructor(config) {
|
|
22
|
-
this.#config = config;
|
|
23
|
-
}
|
|
24
|
-
register(server, worker) {
|
|
25
|
-
server.onBrowserEvent((data, client) => this.#onBrowserEvent(data, client, worker));
|
|
26
|
-
worker.on('message', this.#onWorkerMessage.bind(this));
|
|
27
|
-
}
|
|
28
|
-
async #onWorkerMessage(payload) {
|
|
29
|
-
if (payload.name === 'sessionStarted' && !SESSIONS.has(payload.cid)) {
|
|
30
|
-
SESSIONS.set(payload.cid, {
|
|
31
|
-
args: this.#config.mochaOpts || {},
|
|
32
|
-
config: this.#config,
|
|
33
|
-
capabilities: payload.content.capabilities,
|
|
34
|
-
sessionId: payload.content.sessionId,
|
|
35
|
-
injectGlobals: payload.content.injectGlobals
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
if (payload.name === 'sessionEnded') {
|
|
39
|
-
SESSIONS.delete(payload.cid);
|
|
40
|
-
}
|
|
41
|
-
if (payload.name === 'workerEvent' && payload.args.type === MESSAGE_TYPES.coverageMap) {
|
|
42
|
-
const coverageMapData = payload.args.value;
|
|
43
|
-
this.coverageMaps.push(await this.#mapStore.transformCoverage(libCoverage.createCoverageMap(coverageMapData)));
|
|
44
|
-
}
|
|
45
|
-
if (payload.name === 'workerEvent' && payload.args.type === MESSAGE_TYPES.customCommand) {
|
|
46
|
-
const { commandName, cid } = payload.args.value;
|
|
47
|
-
if (!this.#customCommands.has(cid)) {
|
|
48
|
-
this.#customCommands.set(cid, new Set());
|
|
49
|
-
}
|
|
50
|
-
const customCommands = this.#customCommands.get(cid) || new Set();
|
|
51
|
-
customCommands.add(commandName);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (payload.name === 'workerResponse') {
|
|
55
|
-
const msg = this.#pendingMessages.get(payload.args.id);
|
|
56
|
-
if (!msg) {
|
|
57
|
-
return log.error(`Couldn't find message with id ${payload.args.id} from type ${payload.args.message.type}`);
|
|
58
|
-
}
|
|
59
|
-
this.#pendingMessages.delete(payload.args.id);
|
|
60
|
-
return msg.client.send(WDIO_EVENT_NAME, payload.args.message);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
#onBrowserEvent(message, client, worker) {
|
|
64
|
-
/**
|
|
65
|
-
* some browser events don't need to go through the worker process
|
|
66
|
-
*/
|
|
67
|
-
if (message.type === MESSAGE_TYPES.initiateBrowserStateRequest) {
|
|
68
|
-
const result = {
|
|
69
|
-
type: MESSAGE_TYPES.initiateBrowserStateResponse,
|
|
70
|
-
value: {
|
|
71
|
-
customCommands: [...(this.#customCommands.get(message.value.cid) || [])]
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
return client.send(WDIO_EVENT_NAME, result);
|
|
75
|
-
}
|
|
76
|
-
const id = this.#msgId++;
|
|
77
|
-
const msg = { id, client };
|
|
78
|
-
this.#pendingMessages.set(id, msg);
|
|
79
|
-
const args = { id, message };
|
|
80
|
-
return worker.postMessage('workerRequest', args, true);
|
|
81
|
-
}
|
|
82
|
-
}
|
package/build/constants.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
export const SESSIONS = new Map();
|
|
2
|
-
export const WDIO_EVENT_NAME = 'wdio:workerMessage';
|
|
3
|
-
export const EVENTS = {
|
|
4
|
-
'suite': 'suite:start',
|
|
5
|
-
'suite end': 'suite:end',
|
|
6
|
-
'test': 'test:start',
|
|
7
|
-
'test end': 'test:end',
|
|
8
|
-
'hook': 'hook:start',
|
|
9
|
-
'hook end': 'hook:end',
|
|
10
|
-
'pass': 'test:pass',
|
|
11
|
-
'fail': 'test:fail',
|
|
12
|
-
'retry': 'test:retry',
|
|
13
|
-
'pending': 'test:pending'
|
|
14
|
-
};
|
|
15
|
-
export const FRAMEWORK_SUPPORT_ERROR = 'Currently only "mocha" is supported as framework when using @wdio/browser-runner.';
|
|
16
|
-
export const DEFAULT_INCLUDE = ['**'];
|
|
17
|
-
export const DEFAULT_FILE_EXTENSIONS = ['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.tsx', '.jsx', '.vue', '.svelte'];
|
|
18
|
-
export const DEFAULT_REPORTS_DIRECTORY = 'coverage';
|
|
19
|
-
export const DEFAULT_AUTOMOCK = true;
|
|
20
|
-
export const DEFAULT_MOCK_DIRECTORY = '__mocks__';
|
|
21
|
-
export const SUMMARY_REPORTER = 'json-summary';
|
|
22
|
-
export const COVERAGE_FACTORS = ['lines', 'functions', 'branches', 'statements'];
|
|
23
|
-
export const DEFAULT_COVERAGE_REPORTS = ['text', 'html', 'clover', SUMMARY_REPORTER];
|
|
24
|
-
export const GLOBAL_TRESHOLD_REPORTING = 'ERROR: Coverage for %s (%s%) does not meet global threshold (%s%)';
|
|
25
|
-
export const FILE_TRESHOLD_REPORTING = 'ERROR: Coverage for %s (%s%) does not meet threshold (%s%) for %s';
|
|
26
|
-
export const MOCHA_VARIABELS = /*css*/ `:root {
|
|
27
|
-
--mocha-color: #000;
|
|
28
|
-
--mocha-bg-color: #fff;
|
|
29
|
-
--mocha-pass-icon-color: #00d6b2;
|
|
30
|
-
--mocha-pass-color: #fff;
|
|
31
|
-
--mocha-pass-shadow-color: rgba(0, 0, 0, .2);
|
|
32
|
-
--mocha-pass-mediump-color: #c09853;
|
|
33
|
-
--mocha-pass-slow-color: #b94a48;
|
|
34
|
-
--mocha-test-pending-color: #0b97c4;
|
|
35
|
-
--mocha-test-pending-icon-color: #0b97c4;
|
|
36
|
-
--mocha-test-fail-color: #c00;
|
|
37
|
-
--mocha-test-fail-icon-color: #c00;
|
|
38
|
-
--mocha-test-fail-pre-color: #000;
|
|
39
|
-
--mocha-test-fail-pre-error-color: #c00;
|
|
40
|
-
--mocha-test-html-error-color: #000;
|
|
41
|
-
--mocha-box-shadow-color: #eee;
|
|
42
|
-
--mocha-box-bottom-color: #ddd;
|
|
43
|
-
--mocha-test-replay-color: #000;
|
|
44
|
-
--mocha-test-replay-bg-color: #eee;
|
|
45
|
-
--mocha-stats-color: #888;
|
|
46
|
-
--mocha-stats-em-color: #000;
|
|
47
|
-
--mocha-stats-hover-color: #eee;
|
|
48
|
-
--mocha-error-color: #c00;
|
|
49
|
-
|
|
50
|
-
--mocha-code-comment: #ddd;
|
|
51
|
-
--mocha-code-init: #2f6fad;
|
|
52
|
-
--mocha-code-string: #5890ad;
|
|
53
|
-
--mocha-code-keyword: #8a6343;
|
|
54
|
-
--mocha-code-number: #2f6fad;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
@media (prefers-color-scheme: dark) {
|
|
58
|
-
:root {
|
|
59
|
-
--mocha-color: #fff;
|
|
60
|
-
--mocha-bg-color: #222;
|
|
61
|
-
--mocha-pass-icon-color: #00d6b2;
|
|
62
|
-
--mocha-pass-color: #222;
|
|
63
|
-
--mocha-pass-shadow-color: rgba(255, 255, 255, .2);
|
|
64
|
-
--mocha-pass-mediump-color: #f1be67;
|
|
65
|
-
--mocha-pass-slow-color: #f49896;
|
|
66
|
-
--mocha-test-pending-color: #0b97c4;
|
|
67
|
-
--mocha-test-pending-icon-color: #0b97c4;
|
|
68
|
-
--mocha-test-fail-color: #f44;
|
|
69
|
-
--mocha-test-fail-icon-color: #f44;
|
|
70
|
-
--mocha-test-fail-pre-color: #fff;
|
|
71
|
-
--mocha-test-fail-pre-error-color: #f44;
|
|
72
|
-
--mocha-test-html-error-color: #fff;
|
|
73
|
-
--mocha-box-shadow-color: #444;
|
|
74
|
-
--mocha-box-bottom-color: #555;
|
|
75
|
-
--mocha-test-replay-color: #fff;
|
|
76
|
-
--mocha-test-replay-bg-color: #444;
|
|
77
|
-
--mocha-stats-color: #aaa;
|
|
78
|
-
--mocha-stats-em-color: #fff;
|
|
79
|
-
--mocha-stats-hover-color: #444;
|
|
80
|
-
--mocha-error-color: #f44;
|
|
81
|
-
|
|
82
|
-
--mocha-code-comment: #ddd;
|
|
83
|
-
--mocha-code-init: #9cc7f1;
|
|
84
|
-
--mocha-code-string: #80d4ff;
|
|
85
|
-
--mocha-code-keyword: #e3a470;
|
|
86
|
-
--mocha-code-number: #4ca7ff;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
`;
|
package/build/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/build/utils.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import util from 'node:util';
|
|
2
|
-
import { deepmerge } from 'deepmerge-ts';
|
|
3
|
-
import logger from '@wdio/logger';
|
|
4
|
-
import { COVERAGE_FACTORS, GLOBAL_TRESHOLD_REPORTING, FILE_TRESHOLD_REPORTING } from './constants.js';
|
|
5
|
-
const log = logger('@wdio/browser-runner');
|
|
6
|
-
export function makeHeadless(options, caps) {
|
|
7
|
-
const capability = caps.alwaysMatch || caps;
|
|
8
|
-
if (!capability.browserName) {
|
|
9
|
-
throw new Error('No "browserName" defined in capability object. It seems you are trying to run tests ' +
|
|
10
|
-
'in a non web environment, however WebdriverIOs browser runner only supports web environments');
|
|
11
|
-
}
|
|
12
|
-
if (
|
|
13
|
-
// either user sets headless option implicitly
|
|
14
|
-
(typeof options.headless === 'boolean' && !options.headless) ||
|
|
15
|
-
// or CI environment is set
|
|
16
|
-
(typeof process.env.CI !== 'undefined' && !process.env.CI) ||
|
|
17
|
-
// or non are set
|
|
18
|
-
(typeof options.headless !== 'boolean' && typeof process.env.CI === 'undefined')) {
|
|
19
|
-
return caps;
|
|
20
|
-
}
|
|
21
|
-
if (capability.browserName === 'chrome') {
|
|
22
|
-
return deepmerge(capability, {
|
|
23
|
-
'goog:chromeOptions': {
|
|
24
|
-
args: ['headless', 'disable-gpu']
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
else if (capability.browserName === 'firefox') {
|
|
29
|
-
return deepmerge(capability, {
|
|
30
|
-
'moz:firefoxOptions': {
|
|
31
|
-
args: ['-headless']
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
else if (capability.browserName === 'msedge' || capability.browserName === 'edge') {
|
|
36
|
-
return deepmerge(capability, {
|
|
37
|
-
'ms:edgeOptions': {
|
|
38
|
-
args: ['--headless']
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
log.error(`Headless mode not supported for browser "${capability.browserName}"`);
|
|
43
|
-
return caps;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Open with devtools open when in watch mode
|
|
47
|
-
*/
|
|
48
|
-
export function adjustWindowInWatchMode(config, caps) {
|
|
49
|
-
if (!config.watch) {
|
|
50
|
-
return caps;
|
|
51
|
-
}
|
|
52
|
-
const capability = caps.alwaysMatch || caps;
|
|
53
|
-
if (config.watch && capability.browserName === 'chrome') {
|
|
54
|
-
return deepmerge(capability, {
|
|
55
|
-
'goog:chromeOptions': {
|
|
56
|
-
args: ['auto-open-devtools-for-tabs', 'window-size=1600,1200'],
|
|
57
|
-
prefs: {
|
|
58
|
-
devtools: {
|
|
59
|
-
preferences: {
|
|
60
|
-
'panel-selectedTab': '"console"'
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* TODO: add support for other browsers (if possible)
|
|
69
|
-
* } else if (...) { }
|
|
70
|
-
*/
|
|
71
|
-
return caps;
|
|
72
|
-
}
|
|
73
|
-
export function getCoverageByFactor(options, summary, fileName) {
|
|
74
|
-
return COVERAGE_FACTORS.map((factor) => {
|
|
75
|
-
const treshold = options[factor];
|
|
76
|
-
if (!treshold) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (summary[factor].pct >= treshold) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
return fileName
|
|
83
|
-
? util.format(FILE_TRESHOLD_REPORTING, factor, summary[factor].pct, treshold, fileName)
|
|
84
|
-
: util.format(GLOBAL_TRESHOLD_REPORTING, factor, summary[factor].pct, treshold);
|
|
85
|
-
}).filter(Boolean);
|
|
86
|
-
}
|
package/build/vite/constants.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import topLevelAwait from 'vite-plugin-top-level-await';
|
|
2
|
-
import { esbuildCommonjs } from '@originjs/vite-plugin-commonjs';
|
|
3
|
-
import { codeFrameFix } from './plugins/esbuild.js';
|
|
4
|
-
export const PRESET_DEPENDENCIES = {
|
|
5
|
-
react: ['@vitejs/plugin-react', 'default', {
|
|
6
|
-
babel: {
|
|
7
|
-
assumptions: {
|
|
8
|
-
setPublicClassFields: true
|
|
9
|
-
},
|
|
10
|
-
parserOpts: {
|
|
11
|
-
plugins: ['decorators-legacy', 'classProperties']
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}],
|
|
15
|
-
preact: ['@preact/preset-vite', 'default', undefined],
|
|
16
|
-
vue: ['@vitejs/plugin-vue', 'default', undefined],
|
|
17
|
-
svelte: ['@sveltejs/vite-plugin-svelte', 'svelte', undefined],
|
|
18
|
-
solid: ['vite-plugin-solid', 'default', undefined],
|
|
19
|
-
stencil: undefined,
|
|
20
|
-
lit: undefined
|
|
21
|
-
};
|
|
22
|
-
export const DEFAULT_VITE_CONFIG = {
|
|
23
|
-
configFile: false,
|
|
24
|
-
server: { host: 'localhost' },
|
|
25
|
-
logLevel: 'silent',
|
|
26
|
-
plugins: [topLevelAwait()],
|
|
27
|
-
build: {
|
|
28
|
-
sourcemap: 'inline',
|
|
29
|
-
commonjsOptions: {
|
|
30
|
-
include: [/node_modules/],
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
optimizeDeps: {
|
|
34
|
-
/**
|
|
35
|
-
* the following deps are CJS packages and need to be optimized (compiled to ESM) by Vite
|
|
36
|
-
*/
|
|
37
|
-
include: [
|
|
38
|
-
'expect', 'minimatch', 'css-shorthand-properties', 'lodash.merge', 'lodash.zip', 'ws',
|
|
39
|
-
'lodash.clonedeep', 'lodash.pickby', 'lodash.flattendeep', 'aria-query', 'grapheme-splitter',
|
|
40
|
-
'css-value', 'rgb2hex', 'p-iteration', 'deepmerge-ts', 'jest-util', 'jest-matcher-utils', 'split2'
|
|
41
|
-
],
|
|
42
|
-
esbuildOptions: {
|
|
43
|
-
logLevel: 'silent',
|
|
44
|
-
// Node.js global to browser globalThis
|
|
45
|
-
define: {
|
|
46
|
-
global: 'globalThis',
|
|
47
|
-
},
|
|
48
|
-
// Enable esbuild polyfill plugins
|
|
49
|
-
plugins: [
|
|
50
|
-
esbuildCommonjs(['@testing-library/vue']),
|
|
51
|
-
codeFrameFix()
|
|
52
|
-
],
|
|
53
|
-
},
|
|
54
|
-
}
|
|
55
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { isNuxtFramework, optimizeForNuxt } from './nuxt.js';
|
|
2
|
-
import { isUsingTailwindCSS, optimizeForTailwindCSS } from './tailwindcss.js';
|
|
3
|
-
import { isUsingStencilJS, optimizeForStencil } from './stencil.js';
|
|
4
|
-
export default async function updateViteConfig(options, config) {
|
|
5
|
-
const optimizations = {};
|
|
6
|
-
const rootDir = options.rootDir || config.rootDir || process.cwd();
|
|
7
|
-
const isNuxt = await isNuxtFramework(rootDir);
|
|
8
|
-
if (isNuxt) {
|
|
9
|
-
Object.assign(optimizations, await optimizeForNuxt(options, config));
|
|
10
|
-
}
|
|
11
|
-
const isTailwind = await isUsingTailwindCSS(rootDir);
|
|
12
|
-
if (isTailwind) {
|
|
13
|
-
Object.assign(optimizations, await optimizeForTailwindCSS(rootDir));
|
|
14
|
-
}
|
|
15
|
-
if (await isUsingStencilJS(rootDir, options)) {
|
|
16
|
-
Object.assign(optimizations, await optimizeForStencil(rootDir));
|
|
17
|
-
}
|
|
18
|
-
return optimizations;
|
|
19
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import url from 'node:url';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import logger from '@wdio/logger';
|
|
4
|
-
import { resolve } from 'import-meta-resolve';
|
|
5
|
-
import { hasFileByExtensions, hasDir } from '../utils.js';
|
|
6
|
-
const log = logger('@wdio/browser-runner:NuxtOptimization');
|
|
7
|
-
export async function isNuxtFramework(rootDir) {
|
|
8
|
-
return (await Promise.all([
|
|
9
|
-
hasFileByExtensions(path.join(rootDir, 'nuxt.config')),
|
|
10
|
-
hasDir(path.join(rootDir, '.nuxt'))
|
|
11
|
-
])).filter(Boolean).length > 0;
|
|
12
|
-
}
|
|
13
|
-
export async function optimizeForNuxt(options, config) {
|
|
14
|
-
const Unimport = (await import('unimport/unplugin')).default;
|
|
15
|
-
const { scanDirExports, scanExports } = await import('unimport');
|
|
16
|
-
const { loadNuxtConfig } = await import('@nuxt/kit');
|
|
17
|
-
const rootDir = config.rootDir || process.cwd();
|
|
18
|
-
const nuxtOptions = await loadNuxtConfig({ rootDir });
|
|
19
|
-
if (nuxtOptions.imports?.autoImport === false) {
|
|
20
|
-
return {};
|
|
21
|
-
}
|
|
22
|
-
const nuxtDepPath = await resolve('nuxt', import.meta.url);
|
|
23
|
-
const composablesDirs = [];
|
|
24
|
-
for (const layer of nuxtOptions._layers) {
|
|
25
|
-
composablesDirs.push(path.resolve(layer.config.srcDir, 'composables'));
|
|
26
|
-
composablesDirs.push(path.resolve(layer.config.srcDir, 'utils'));
|
|
27
|
-
for (const dir of (layer.config.imports?.dirs ?? [])) {
|
|
28
|
-
if (!dir) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
composablesDirs.push(path.resolve(layer.config.srcDir, dir));
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
const composableImports = await Promise.all([
|
|
35
|
-
scanDirExports([
|
|
36
|
-
...composablesDirs,
|
|
37
|
-
path.resolve(path.dirname(url.fileURLToPath(nuxtDepPath)), 'app', 'components')
|
|
38
|
-
]),
|
|
39
|
-
scanExports(path.resolve(path.dirname(url.fileURLToPath(nuxtDepPath)), 'app', 'composables', 'index.js'))
|
|
40
|
-
]).then((scannedExports) => scannedExports.flat().map((ci) => {
|
|
41
|
-
/**
|
|
42
|
-
* auto mock nuxt composables as they can't be loaded within the browser
|
|
43
|
-
*/
|
|
44
|
-
if (ci.from.includes('/nuxt/dist/app/composables')) {
|
|
45
|
-
ci.from = 'virtual:wdio';
|
|
46
|
-
ci.name = 'wrappedFn';
|
|
47
|
-
}
|
|
48
|
-
return ci;
|
|
49
|
-
}));
|
|
50
|
-
const viteConfig = {
|
|
51
|
-
resolve: {
|
|
52
|
-
alias: nuxtOptions.alias || {}
|
|
53
|
-
},
|
|
54
|
-
plugins: [Unimport.vite({
|
|
55
|
-
presets: ['vue'],
|
|
56
|
-
imports: composableImports
|
|
57
|
-
})]
|
|
58
|
-
};
|
|
59
|
-
log.info(`Optimized Vite config for Nuxt project at ${rootDir}`);
|
|
60
|
-
return viteConfig;
|
|
61
|
-
}
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import url from 'node:url';
|
|
3
|
-
import { findStaticImports, parseStaticImport } from 'mlly';
|
|
4
|
-
import { hasFileByExtensions } from '../utils.js';
|
|
5
|
-
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
|
6
|
-
const STENCIL_IMPORT = '@stencil/core';
|
|
7
|
-
export async function isUsingStencilJS(rootDir, options) {
|
|
8
|
-
return Boolean(options.preset === 'stencil' || await hasFileByExtensions(path.join(rootDir, 'stencil.config')));
|
|
9
|
-
}
|
|
10
|
-
export async function optimizeForStencil(rootDir) {
|
|
11
|
-
const stencilConfig = await importStencilConfig(rootDir);
|
|
12
|
-
const stencilPlugins = stencilConfig.config.plugins;
|
|
13
|
-
const stencilOptimizations = {
|
|
14
|
-
plugins: [await stencilVitePlugin(rootDir)],
|
|
15
|
-
optimizeDeps: { include: [] }
|
|
16
|
-
};
|
|
17
|
-
if (stencilPlugins) {
|
|
18
|
-
const esbuildPlugin = stencilPlugins.find((plugin) => plugin.name === 'esbuild-plugin');
|
|
19
|
-
if (esbuildPlugin) {
|
|
20
|
-
stencilOptimizations.optimizeDeps?.include?.push(...esbuildPlugin.options.include);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* testing helper from the stencil core package is unfortunately exported as CJS
|
|
25
|
-
* module, in order to be able to use it in the browser we have to optimize it
|
|
26
|
-
* it to compile it to ESM
|
|
27
|
-
*/
|
|
28
|
-
stencilOptimizations.optimizeDeps?.include?.push('@wdio/browser-runner/stencil > @stencil/core/internal/testing/index.js');
|
|
29
|
-
return stencilOptimizations;
|
|
30
|
-
}
|
|
31
|
-
async function stencilVitePlugin(rootDir) {
|
|
32
|
-
const { transpileSync, ts } = await import('@stencil/core/compiler/stencil.js');
|
|
33
|
-
const stencilHelperPath = path.resolve(__dirname, '..', '..', 'browser', 'integrations', 'stencil.js');
|
|
34
|
-
return {
|
|
35
|
-
name: 'wdio-stencil',
|
|
36
|
-
enforce: 'pre',
|
|
37
|
-
resolveId(source) {
|
|
38
|
-
if (source === '@wdio/browser-runner/stencil') {
|
|
39
|
-
return stencilHelperPath;
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
transform: function (code, id) {
|
|
43
|
-
const staticImports = findStaticImports(code);
|
|
44
|
-
const stencilImports = staticImports
|
|
45
|
-
.filter((imp) => imp.specifier === STENCIL_IMPORT)
|
|
46
|
-
.map((imp) => parseStaticImport(imp));
|
|
47
|
-
const isStencilComponent = stencilImports.some((imp) => 'Component' in (imp.namedImports || {}));
|
|
48
|
-
/**
|
|
49
|
-
* if file doesn't define a Stencil component
|
|
50
|
-
*/
|
|
51
|
-
if (!isStencilComponent) {
|
|
52
|
-
/**
|
|
53
|
-
* if a test imports the `@wdio/browser-runner/stencil` package we want to automatically
|
|
54
|
-
* import `h` and `Fragment` from the `@stencil/core` package
|
|
55
|
-
*/
|
|
56
|
-
const stencilHelperImport = staticImports.find((imp) => imp.specifier === '@wdio/browser-runner/stencil');
|
|
57
|
-
if (stencilHelperImport) {
|
|
58
|
-
const imports = parseStaticImport(stencilHelperImport);
|
|
59
|
-
if ('render' in (imports.namedImports || {})) {
|
|
60
|
-
code = injectStencilImports(code, stencilImports);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return { code };
|
|
64
|
-
}
|
|
65
|
-
const tsCompilerOptions = getCompilerOptions(ts, rootDir);
|
|
66
|
-
const opts = {
|
|
67
|
-
componentExport: 'module',
|
|
68
|
-
componentMetadata: 'compilerstatic',
|
|
69
|
-
coreImportPath: '@stencil/core/internal/client',
|
|
70
|
-
currentDirectory: rootDir,
|
|
71
|
-
file: path.basename(id),
|
|
72
|
-
module: 'esm',
|
|
73
|
-
sourceMap: 'inline',
|
|
74
|
-
style: 'static',
|
|
75
|
-
proxy: 'defineproperty',
|
|
76
|
-
styleImportData: 'queryparams',
|
|
77
|
-
transformAliasedImportPaths: process.env.__STENCIL_TRANSPILE_PATHS__ === 'true',
|
|
78
|
-
target: tsCompilerOptions?.target || 'es2018',
|
|
79
|
-
paths: tsCompilerOptions?.paths,
|
|
80
|
-
baseUrl: tsCompilerOptions?.baseUrl,
|
|
81
|
-
};
|
|
82
|
-
const transpiledCode = transpileSync(code, opts);
|
|
83
|
-
/**
|
|
84
|
-
* StencilJS applies only a getter to the component without having a setter defined.
|
|
85
|
-
* This causes issue in the browser as there is a check that the setter is defined
|
|
86
|
-
* if the getter is defined. We can work around this by defining a setter.
|
|
87
|
-
*/
|
|
88
|
-
let transformedCode = transpiledCode.code.replace('static get style()', 'static set style(_) {}\n static get style()');
|
|
89
|
-
/**
|
|
90
|
-
* StencilJS does not import the `h` or `Fragment` function by default. We need to add it so the user
|
|
91
|
-
* doesn't need to.
|
|
92
|
-
*/
|
|
93
|
-
transformedCode = injectStencilImports(transformedCode, stencilImports);
|
|
94
|
-
/**
|
|
95
|
-
* Ensure that CSS imports by Stencil have an `&inline` query parameter
|
|
96
|
-
*/
|
|
97
|
-
findStaticImports(transformedCode)
|
|
98
|
-
.filter((imp) => imp.specifier.includes('&encapsulation=shadow'))
|
|
99
|
-
.forEach((imp) => {
|
|
100
|
-
const cssPath = path.resolve(path.dirname(id), imp.specifier);
|
|
101
|
-
transformedCode = transformedCode.replace(imp.code, `import ${imp.imports.trim()} from '/@fs/${cssPath}&inline';\n`);
|
|
102
|
-
});
|
|
103
|
-
return {
|
|
104
|
-
...transpiledCode,
|
|
105
|
-
code: transformedCode,
|
|
106
|
-
inputFilePath: id
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* StencilJS does not import the `h` or `Fragment` function by default. We need to add it so the user
|
|
113
|
-
* doesn't need to.
|
|
114
|
-
*/
|
|
115
|
-
function injectStencilImports(code, imports) {
|
|
116
|
-
const hasRenderFunctionImport = imports.some((imp) => 'h' in (imp.namedImports || {}));
|
|
117
|
-
if (!hasRenderFunctionImport) {
|
|
118
|
-
code = `import { h } from '@stencil/core/internal/client';\n${code}`;
|
|
119
|
-
}
|
|
120
|
-
const hasFragmentImport = imports.some((imp) => 'Fragment' in (imp.namedImports || {}));
|
|
121
|
-
if (!hasFragmentImport) {
|
|
122
|
-
code = `import { Fragment } from '@stencil/core/internal/client';\n${code}`;
|
|
123
|
-
}
|
|
124
|
-
return code;
|
|
125
|
-
}
|
|
126
|
-
let _tsCompilerOptions = null;
|
|
127
|
-
/**
|
|
128
|
-
* Read the TypeScript compiler configuration file from disk
|
|
129
|
-
* @param rootDir the location to search for the config file
|
|
130
|
-
* @returns the configuration, or `null` if the file cannot be found
|
|
131
|
-
*/
|
|
132
|
-
function getCompilerOptions(ts, rootDir) {
|
|
133
|
-
if (_tsCompilerOptions) {
|
|
134
|
-
return _tsCompilerOptions;
|
|
135
|
-
}
|
|
136
|
-
if (typeof rootDir !== 'string') {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
const tsconfigFilePath = ts.findConfigFile(rootDir, ts.sys.fileExists);
|
|
140
|
-
if (!tsconfigFilePath) {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
const tsconfigResults = ts.readConfigFile(tsconfigFilePath, ts.sys.readFile);
|
|
144
|
-
if (tsconfigResults.error) {
|
|
145
|
-
throw new Error(tsconfigResults.error);
|
|
146
|
-
}
|
|
147
|
-
const parseResult = ts.parseJsonConfigFileContent(tsconfigResults.config, ts.sys, rootDir, undefined, tsconfigFilePath);
|
|
148
|
-
_tsCompilerOptions = parseResult.options;
|
|
149
|
-
return _tsCompilerOptions;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* helper method to import a Stencil config file
|
|
153
|
-
*/
|
|
154
|
-
export async function importStencilConfig(rootDir) {
|
|
155
|
-
const configPath = path.join(rootDir, 'stencil.config.ts');
|
|
156
|
-
const config = await import(configPath).catch(() => ({ config: {} }));
|
|
157
|
-
/**
|
|
158
|
-
* if we import the config within a CJS environment we need to
|
|
159
|
-
* access the default property even though there is a named export
|
|
160
|
-
*/
|
|
161
|
-
if ('default' in config) {
|
|
162
|
-
return config.default;
|
|
163
|
-
}
|
|
164
|
-
return config;
|
|
165
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import url from 'node:url';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { resolve } from 'import-meta-resolve';
|
|
4
|
-
import { hasFileByExtensions } from '../utils.js';
|
|
5
|
-
/**
|
|
6
|
-
* returns `true` if TailwindCSS config exist but no postcss.config
|
|
7
|
-
* (if a `postcss.config` exists it will be automatically picked up by Vite)
|
|
8
|
-
*/
|
|
9
|
-
export function isUsingTailwindCSS(rootDir) {
|
|
10
|
-
return Promise.all([
|
|
11
|
-
hasFileByExtensions(path.join(rootDir, 'tailwind.config')),
|
|
12
|
-
hasFileByExtensions(path.join(rootDir, 'postcss.config'))
|
|
13
|
-
]).then(([hasTailwindConfig, hasPostCSSConfig]) => {
|
|
14
|
-
return hasTailwindConfig && !hasPostCSSConfig;
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* add tailwind plugin if installed as dependency
|
|
19
|
-
*/
|
|
20
|
-
export async function optimizeForTailwindCSS(rootDir) {
|
|
21
|
-
const viteConfig = {};
|
|
22
|
-
const tailwindcssPath = await resolve('tailwindcss', url.pathToFileURL(path.resolve(rootDir, 'index.js')).href);
|
|
23
|
-
const tailwindcss = (await import(tailwindcssPath)).default;
|
|
24
|
-
viteConfig.css = {
|
|
25
|
-
postcss: { plugins: [tailwindcss] }
|
|
26
|
-
};
|
|
27
|
-
return viteConfig;
|
|
28
|
-
}
|
package/build/vite/mock.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { getManualMocks } from './utils.js';
|
|
3
|
-
import { DEFAULT_MOCK_DIRECTORY, DEFAULT_AUTOMOCK } from '../constants.js';
|
|
4
|
-
const FIXTURE_PREFIX = '/@fixture/';
|
|
5
|
-
export class MockHandler {
|
|
6
|
-
#automock;
|
|
7
|
-
#automockDir;
|
|
8
|
-
#manualMocksList;
|
|
9
|
-
#mocks = new Map();
|
|
10
|
-
#unmocked = [];
|
|
11
|
-
manualMocks = [];
|
|
12
|
-
constructor(options, config) {
|
|
13
|
-
this.#automock = typeof options.automock === 'boolean' ? options.automock : DEFAULT_AUTOMOCK;
|
|
14
|
-
this.#automockDir = path.resolve(config.rootDir, options.automockDir || DEFAULT_MOCK_DIRECTORY);
|
|
15
|
-
this.#manualMocksList = getManualMocks(this.#automockDir);
|
|
16
|
-
}
|
|
17
|
-
get mocks() {
|
|
18
|
-
return this.#mocks;
|
|
19
|
-
}
|
|
20
|
-
unmock(moduleName) {
|
|
21
|
-
this.#unmocked.push(moduleName);
|
|
22
|
-
}
|
|
23
|
-
async resolveId(id) {
|
|
24
|
-
const manualMocksList = await this.#manualMocksList;
|
|
25
|
-
const mockPath = manualMocksList.find((m) => (
|
|
26
|
-
// e.g. someModule
|
|
27
|
-
id === m[1].replace(path.sep, '/') ||
|
|
28
|
-
// e.g. @some/module
|
|
29
|
-
id.slice(1) === m[1].replace(path.sep, '/')));
|
|
30
|
-
/**
|
|
31
|
-
* return manual mock if `automock` is enabled or a manual mock was set via `mock('module')`
|
|
32
|
-
*/
|
|
33
|
-
if ((this.manualMocks.includes(id) || this.#automock) && mockPath && !this.#unmocked.includes(id)) {
|
|
34
|
-
return mockPath[0];
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* return fixture
|
|
38
|
-
*/
|
|
39
|
-
if (id.startsWith(FIXTURE_PREFIX)) {
|
|
40
|
-
return path.resolve(this.#automockDir, id.slice(FIXTURE_PREFIX.length));
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* reset manual mocks between tests
|
|
45
|
-
*/
|
|
46
|
-
resetMocks() {
|
|
47
|
-
this.manualMocks = [];
|
|
48
|
-
this.#unmocked = [];
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
export function codeFrameFix() {
|
|
3
|
-
return {
|
|
4
|
-
name: 'wdio:codeFrameFix',
|
|
5
|
-
setup(build) {
|
|
6
|
-
build.onLoad({ filter: /@babel\/code-frame/, namespace: 'file' },
|
|
7
|
-
/**
|
|
8
|
-
* mock @babel/code-frame as it fails in Safari due
|
|
9
|
-
* to usage of chalk
|
|
10
|
-
*/
|
|
11
|
-
async ({ path: id }) => {
|
|
12
|
-
const code = await fs.readFile(id).then((buf) => buf.toString(), () => undefined);
|
|
13
|
-
if (!code) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
return {
|
|
17
|
-
contents: code.replace('require("@babel/highlight");', /*js*/ `{
|
|
18
|
-
shouldHighlight: false,
|
|
19
|
-
reset: () => {}
|
|
20
|
-
}`)
|
|
21
|
-
};
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
}
|