@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
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import url from 'node:url';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import fs from 'node:fs/promises';
|
|
5
|
-
import logger from '@wdio/logger';
|
|
6
|
-
import { parse, print, visit, types } from 'recast';
|
|
7
|
-
import typescriptParser from 'recast/parsers/typescript.js';
|
|
8
|
-
const log = logger('@wdio/browser-runner:mockHoisting');
|
|
9
|
-
const INTERNALS_TO_IGNORE = [
|
|
10
|
-
'@vite/client', 'vite/dist/client', '/webdriverio/build/', '/@wdio/', '/webdriverio/node_modules/',
|
|
11
|
-
'virtual:wdio', '?html-proxy', '/__fixtures__/', '/__mocks__/', '/.vite/deps/@testing-library_vue.js'
|
|
12
|
-
];
|
|
13
|
-
const b = types.builders;
|
|
14
|
-
const MOCK_PREFIX = '/@mock';
|
|
15
|
-
export function mockHoisting(mockHandler) {
|
|
16
|
-
let spec = null;
|
|
17
|
-
let isTestDependency = false;
|
|
18
|
-
const sessionMocks = new Set();
|
|
19
|
-
const importMap = new Map();
|
|
20
|
-
return [{
|
|
21
|
-
name: 'wdio:mockHoisting:pre',
|
|
22
|
-
enforce: 'pre',
|
|
23
|
-
resolveId: mockHandler.resolveId.bind(mockHandler),
|
|
24
|
-
load: async function (id) {
|
|
25
|
-
if (id.startsWith(MOCK_PREFIX)) {
|
|
26
|
-
try {
|
|
27
|
-
const orig = await fs.readFile(id.slice(MOCK_PREFIX.length + (os.platform() === 'win32' ? 1 : 0)));
|
|
28
|
-
return orig.toString();
|
|
29
|
-
}
|
|
30
|
-
catch (err) {
|
|
31
|
-
log.error(`Failed to read file (${id}) for mocking: ${err.message}`);
|
|
32
|
-
return '';
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}, {
|
|
37
|
-
name: 'wdio:mockHoisting',
|
|
38
|
-
enforce: 'post',
|
|
39
|
-
transform(code, id) {
|
|
40
|
-
const isSpecFile = id === spec;
|
|
41
|
-
if (isSpecFile) {
|
|
42
|
-
isTestDependency = true;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* only transform files:
|
|
46
|
-
*/
|
|
47
|
-
if (
|
|
48
|
-
// where loading was inititated through the test file
|
|
49
|
-
(!isTestDependency && (
|
|
50
|
-
// however when files are inlined they will be loaded when parsing file under test
|
|
51
|
-
// in this case we want to transform them, but make sure we exclude these paths
|
|
52
|
-
id.includes('/node_modules/') || id.includes('/?cid=') || id.startsWith('virtual:'))) ||
|
|
53
|
-
// are not Vite or WebdriverIO internals
|
|
54
|
-
INTERNALS_TO_IGNORE.find((f) => id.includes(f)) ||
|
|
55
|
-
// when the spec file is actually mocking any dependencies
|
|
56
|
-
(!isSpecFile && sessionMocks.size === 0)) {
|
|
57
|
-
return { code };
|
|
58
|
-
}
|
|
59
|
-
let ast;
|
|
60
|
-
const start = Date.now();
|
|
61
|
-
try {
|
|
62
|
-
ast = parse(code, {
|
|
63
|
-
parser: typescriptParser,
|
|
64
|
-
sourceFileName: id,
|
|
65
|
-
sourceRoot: path.dirname(id)
|
|
66
|
-
});
|
|
67
|
-
log.trace(`Parsed file for mocking: ${id} in ${Date.now() - start}ms`);
|
|
68
|
-
}
|
|
69
|
-
catch (err) {
|
|
70
|
-
return { code };
|
|
71
|
-
}
|
|
72
|
-
let importIndex = 0;
|
|
73
|
-
let mockFunctionName;
|
|
74
|
-
let unmockFunctionName;
|
|
75
|
-
const mockCalls = [];
|
|
76
|
-
visit(ast, {
|
|
77
|
-
/**
|
|
78
|
-
* find function name for mock and unmock calls
|
|
79
|
-
*/
|
|
80
|
-
visitImportDeclaration: function (path) {
|
|
81
|
-
const dec = path.value;
|
|
82
|
-
const source = dec.source.value;
|
|
83
|
-
if (!dec.specifiers || dec.specifiers.length === 0 || source !== '@wdio/browser-runner') {
|
|
84
|
-
return this.traverse(path);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* get name of mock function variable
|
|
88
|
-
*/
|
|
89
|
-
const mockSpecifier = dec.specifiers
|
|
90
|
-
.filter((s) => s.type === types.namedTypes.ImportSpecifier.toString())
|
|
91
|
-
.find((s) => s.imported.name === 'mock');
|
|
92
|
-
if (mockSpecifier && mockSpecifier.local) {
|
|
93
|
-
mockFunctionName = mockSpecifier.local.name;
|
|
94
|
-
}
|
|
95
|
-
const unmockSpecifier = dec.specifiers
|
|
96
|
-
.filter((s) => s.type === types.namedTypes.ImportSpecifier.toString())
|
|
97
|
-
.find((s) => s.imported.name === 'unmock');
|
|
98
|
-
if (unmockSpecifier && unmockSpecifier.local) {
|
|
99
|
-
unmockFunctionName = unmockSpecifier.local.name;
|
|
100
|
-
}
|
|
101
|
-
mockCalls.push(dec);
|
|
102
|
-
path.prune();
|
|
103
|
-
return this.traverse(path);
|
|
104
|
-
},
|
|
105
|
-
/**
|
|
106
|
-
* detect which modules are supposed to be mocked
|
|
107
|
-
*/
|
|
108
|
-
...(isSpecFile ? {
|
|
109
|
-
visitExpressionStatement: function (path) {
|
|
110
|
-
const exp = path.value;
|
|
111
|
-
if (exp.expression.type !== types.namedTypes.CallExpression.toString()) {
|
|
112
|
-
return this.traverse(path);
|
|
113
|
-
}
|
|
114
|
-
const callExp = exp.expression;
|
|
115
|
-
const isUnmockCall = unmockFunctionName && callExp.callee.name === unmockFunctionName;
|
|
116
|
-
const isMockCall = mockFunctionName && callExp.callee.name === mockFunctionName;
|
|
117
|
-
if (!isMockCall && !isUnmockCall) {
|
|
118
|
-
return this.traverse(path);
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* hoist unmock calls
|
|
122
|
-
*/
|
|
123
|
-
if (isUnmockCall && callExp.arguments[0] && typeof callExp.arguments[0].value === 'string') {
|
|
124
|
-
mockHandler.unmock(callExp.arguments[0].value);
|
|
125
|
-
}
|
|
126
|
-
else if (isMockCall) {
|
|
127
|
-
/**
|
|
128
|
-
* if only one mock argument is set, we take the fixture from the automock directory
|
|
129
|
-
*/
|
|
130
|
-
const mockCall = exp.expression;
|
|
131
|
-
if (mockCall.arguments.length === 1) {
|
|
132
|
-
/**
|
|
133
|
-
* enable manual mock
|
|
134
|
-
*/
|
|
135
|
-
mockHandler.manualMocks.push(mockCall.arguments[0].value);
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
if (exp.expression.arguments.length) {
|
|
139
|
-
sessionMocks.add(exp.expression.arguments[0].value);
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* hoist mock calls
|
|
143
|
-
*/
|
|
144
|
-
mockCalls.push(exp);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
path.prune();
|
|
148
|
-
this.traverse(path);
|
|
149
|
-
}
|
|
150
|
-
} : {})
|
|
151
|
-
});
|
|
152
|
-
visit(ast, {
|
|
153
|
-
/**
|
|
154
|
-
* rewrite import statements
|
|
155
|
-
*/
|
|
156
|
-
visitImportDeclaration: function (nodePath) {
|
|
157
|
-
const dec = nodePath.value;
|
|
158
|
-
const source = dec.source.value;
|
|
159
|
-
if (!dec.specifiers || dec.specifiers.length === 0) {
|
|
160
|
-
return this.traverse(nodePath);
|
|
161
|
-
}
|
|
162
|
-
const newImportIdentifier = `__wdio_import${importIndex++}`;
|
|
163
|
-
const isMockedModule = Boolean(
|
|
164
|
-
// matches if a dependency is mocked
|
|
165
|
-
sessionMocks.has(source) ||
|
|
166
|
-
// matches if a relative file is mocked
|
|
167
|
-
(source.startsWith('.') &&
|
|
168
|
-
[...sessionMocks.values()].find((m) => {
|
|
169
|
-
const fileImportPath = path.resolve(path.dirname(id), source);
|
|
170
|
-
const fileImportPathSliced = fileImportPath.slice(0, path.extname(fileImportPath).length * -1);
|
|
171
|
-
const testMockPath = path.resolve(path.dirname(spec || '/'), m);
|
|
172
|
-
const testMockPathSliced = testMockPath.slice(0, path.extname(testMockPath).length * -1);
|
|
173
|
-
return fileImportPathSliced === testMockPathSliced && fileImportPathSliced.length > 0;
|
|
174
|
-
})));
|
|
175
|
-
/**
|
|
176
|
-
* add to import map if module is mocked and imported in the test file
|
|
177
|
-
*/
|
|
178
|
-
if (isMockedModule && isSpecFile) {
|
|
179
|
-
importMap.set(source, newImportIdentifier);
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Assign imports outside of spec files or when module gets mocked
|
|
183
|
-
* into custom import identifier, e.g.
|
|
184
|
-
*
|
|
185
|
-
* from:
|
|
186
|
-
* import { foo } from 'bar'
|
|
187
|
-
*
|
|
188
|
-
* to:
|
|
189
|
-
* import * as __wdio_import0 from 'bar'
|
|
190
|
-
*/
|
|
191
|
-
if (!isSpecFile || isMockedModule) {
|
|
192
|
-
const newNode = b.importDeclaration([b.importNamespaceSpecifier(b.identifier(newImportIdentifier))], b.literal(source));
|
|
193
|
-
mockCalls.unshift(newNode);
|
|
194
|
-
}
|
|
195
|
-
// determine file extension length so we can cut it out, fall back to infinity if no extension is set
|
|
196
|
-
const mockExtensionLengh = (path.extname(source).length * -1) || Infinity;
|
|
197
|
-
const wdioImportModuleIdentifier = source.startsWith('.') || source.startsWith('/')
|
|
198
|
-
? url.pathToFileURL(path.resolve(path.dirname(id), source).slice(0, mockExtensionLengh)).pathname
|
|
199
|
-
: source;
|
|
200
|
-
const isNamespaceImport = dec.specifiers.length === 1 && dec.specifiers[0].type === types.namedTypes.ImportNamespaceSpecifier.toString();
|
|
201
|
-
const mockImport = isSpecFile && !isMockedModule
|
|
202
|
-
/**
|
|
203
|
-
* within spec files we transform import declarations into import expresssions, e.g.
|
|
204
|
-
* from: import { foo } from 'bar'
|
|
205
|
-
* to: const { foo } = await wdioImport('bar', await import('bar'))
|
|
206
|
-
*
|
|
207
|
-
* in order to hoist `mock(...)` calls and have them run first
|
|
208
|
-
*/
|
|
209
|
-
? b.variableDeclaration('const', [
|
|
210
|
-
b.variableDeclarator(isNamespaceImport
|
|
211
|
-
/**
|
|
212
|
-
* we deal with a ImportNamespaceSpecifier, e.g.:
|
|
213
|
-
* import * as foo from 'bar'
|
|
214
|
-
*/
|
|
215
|
-
? dec.specifiers[0].local
|
|
216
|
-
/**
|
|
217
|
-
* we deal with default or named import, e.g.
|
|
218
|
-
* import foo from 'bar'
|
|
219
|
-
* or
|
|
220
|
-
* import { foo } from 'bar'
|
|
221
|
-
*/
|
|
222
|
-
: b.objectPattern(dec.specifiers.map((s) => {
|
|
223
|
-
if (s.type === types.namedTypes.ImportDefaultSpecifier.toString()) {
|
|
224
|
-
return b.property('init', b.identifier('default'), b.identifier(s.local.name));
|
|
225
|
-
}
|
|
226
|
-
return b.property('init', b.identifier(s.imported.name), b.identifier(s.local.name));
|
|
227
|
-
})), b.awaitExpression(b.importExpression(b.literal(source))))
|
|
228
|
-
])
|
|
229
|
-
/**
|
|
230
|
-
* outside of spec files we transform import declarations so that the imported module gets
|
|
231
|
-
* wrapped within `wdioImport`, e.g.:
|
|
232
|
-
*
|
|
233
|
-
* from:
|
|
234
|
-
* import { foo } from 'bar'
|
|
235
|
-
*
|
|
236
|
-
* to:
|
|
237
|
-
* import { foo as __wdio_import0 } from 'bar'
|
|
238
|
-
* const { foo } = await wdioImport('bar', __wdio_import0)
|
|
239
|
-
*/
|
|
240
|
-
: b.variableDeclaration('const', [
|
|
241
|
-
b.variableDeclarator(dec.specifiers.length === 1 && dec.specifiers[0].type === types.namedTypes.ImportNamespaceSpecifier.toString()
|
|
242
|
-
? b.identifier(dec.specifiers[0].local.name)
|
|
243
|
-
: b.objectPattern(dec.specifiers.map((s) => {
|
|
244
|
-
if (s.type === types.namedTypes.ImportDefaultSpecifier.toString()) {
|
|
245
|
-
return b.property('init', b.identifier('default'), b.identifier(s.local.name));
|
|
246
|
-
}
|
|
247
|
-
return b.property('init', b.identifier(s.imported.name), b.identifier(s.local.name));
|
|
248
|
-
})), b.callExpression(b.identifier('wdioImport'), [
|
|
249
|
-
b.literal(wdioImportModuleIdentifier),
|
|
250
|
-
b.identifier(newImportIdentifier)
|
|
251
|
-
]))
|
|
252
|
-
]);
|
|
253
|
-
nodePath.replace(mockImport);
|
|
254
|
-
this.traverse(nodePath);
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
ast.program.body.unshift(...mockCalls.map((mc) => {
|
|
258
|
-
const exp = mc;
|
|
259
|
-
if (exp.expression && exp.expression.type === types.namedTypes.CallExpression.toString()) {
|
|
260
|
-
const mockCallExpression = exp.expression;
|
|
261
|
-
const mockedModule = mockCallExpression.arguments[0].value;
|
|
262
|
-
const mockFactory = mockCallExpression.arguments[1];
|
|
263
|
-
/**
|
|
264
|
-
* add actual module as 3rd parameter to the mock call if imported in the same test file
|
|
265
|
-
*/
|
|
266
|
-
if (importMap.has(mockedModule)) {
|
|
267
|
-
mockCallExpression.arguments.push(b.identifier(importMap.get(mockedModule)));
|
|
268
|
-
}
|
|
269
|
-
else if (mockFactory.params.length > 0) {
|
|
270
|
-
/**
|
|
271
|
-
* `importMap` only has an entry if the module is imported in the same test file.
|
|
272
|
-
* However if the user mocks a dependency of a different dependency we need to add
|
|
273
|
-
* the import manually if the users wants to access the original module.
|
|
274
|
-
*/
|
|
275
|
-
const newImportIdentifier = `__wdio_import${importIndex++}`;
|
|
276
|
-
ast.program.body.unshift(b.importDeclaration([b.importNamespaceSpecifier(b.identifier(newImportIdentifier))], b.literal(mockedModule)));
|
|
277
|
-
mockCallExpression.arguments.push(b.identifier(newImportIdentifier));
|
|
278
|
-
}
|
|
279
|
-
return b.expressionStatement(b.awaitExpression(mockCallExpression));
|
|
280
|
-
}
|
|
281
|
-
return mc;
|
|
282
|
-
}));
|
|
283
|
-
try {
|
|
284
|
-
const newCode = print(ast, { sourceMapName: id });
|
|
285
|
-
log.trace(`Transformed file for mocking: ${id} in ${Date.now() - start}ms`);
|
|
286
|
-
return newCode;
|
|
287
|
-
}
|
|
288
|
-
catch (err) {
|
|
289
|
-
log.trace(`Failed to transformed file (${id}) for mocking: ${err.stack}`);
|
|
290
|
-
return { code };
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
configureServer(server) {
|
|
294
|
-
return () => {
|
|
295
|
-
server.middlewares.use('/', async (req, res, next) => {
|
|
296
|
-
if (!req.originalUrl) {
|
|
297
|
-
return next();
|
|
298
|
-
}
|
|
299
|
-
const urlParsed = url.parse(req.originalUrl);
|
|
300
|
-
const urlParamString = new URLSearchParams(urlParsed.query || '');
|
|
301
|
-
const specParam = urlParamString.get('spec');
|
|
302
|
-
if (specParam) {
|
|
303
|
-
mockHandler.resetMocks();
|
|
304
|
-
isTestDependency = false;
|
|
305
|
-
spec = os.platform() === 'win32' ? specParam.slice(1) : specParam;
|
|
306
|
-
}
|
|
307
|
-
return next();
|
|
308
|
-
});
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
}];
|
|
312
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import url from 'node:url';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { builtinModules } from 'node:module';
|
|
4
|
-
import logger from '@wdio/logger';
|
|
5
|
-
import { polyfillPath } from 'modern-node-polyfills';
|
|
6
|
-
import { deepmerge } from 'deepmerge-ts';
|
|
7
|
-
import { resolve } from 'import-meta-resolve';
|
|
8
|
-
import { WebDriverProtocol, MJsonWProtocol, AppiumProtocol, ChromiumProtocol, SauceLabsProtocol, SeleniumProtocol, GeckoProtocol } from '@wdio/protocols';
|
|
9
|
-
import { SESSIONS } from '../../constants.js';
|
|
10
|
-
import { getTemplate, getErrorTemplate, normalizeId } from '../utils.js';
|
|
11
|
-
const log = logger('@wdio/browser-runner:plugin');
|
|
12
|
-
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
|
13
|
-
const commands = deepmerge(WebDriverProtocol, MJsonWProtocol, AppiumProtocol, ChromiumProtocol, SauceLabsProtocol, SeleniumProtocol, GeckoProtocol);
|
|
14
|
-
const protocolCommandList = Object.values(commands).map((endpoint) => Object.values(endpoint).map(({ command }) => command)).flat();
|
|
15
|
-
const WDIO_PACKAGES = ['webdriverio', 'expect-webdriverio'];
|
|
16
|
-
const virtualModuleId = 'virtual:wdio';
|
|
17
|
-
const resolvedVirtualModuleId = '\0' + virtualModuleId;
|
|
18
|
-
/**
|
|
19
|
-
* these modules are used in Node.js environments only and
|
|
20
|
-
* don't need to be compiled, we just have them point to a
|
|
21
|
-
* mocked module that returns a matching interface without
|
|
22
|
-
* functionality
|
|
23
|
-
*/
|
|
24
|
-
const MODULES_TO_MOCK = [
|
|
25
|
-
'import-meta-resolve', 'puppeteer-core', 'archiver', 'glob', 'ws', 'decamelize',
|
|
26
|
-
'geckodriver', 'safaridriver', 'edgedriver', '@puppeteer/browsers', 'locate-app', 'wait-port',
|
|
27
|
-
'lodash.isequal', '@wdio/repl'
|
|
28
|
-
];
|
|
29
|
-
const POLYFILLS = [
|
|
30
|
-
...builtinModules,
|
|
31
|
-
...builtinModules.map((m) => `node:${m}`)
|
|
32
|
-
];
|
|
33
|
-
export function testrunner(options) {
|
|
34
|
-
const browserModules = path.resolve(__dirname, '..', '..', 'browser');
|
|
35
|
-
const automationProtocolPath = `/@fs${url.pathToFileURL(path.resolve(browserModules, 'driver.js')).pathname}`;
|
|
36
|
-
const mockModulePath = path.resolve(browserModules, 'mock.js');
|
|
37
|
-
const setupModulePath = path.resolve(browserModules, 'setup.js');
|
|
38
|
-
const spyModulePath = path.resolve(browserModules, 'spy.js');
|
|
39
|
-
const wdioExpectModulePath = path.resolve(browserModules, 'expect.js');
|
|
40
|
-
return [{
|
|
41
|
-
name: 'wdio:testrunner',
|
|
42
|
-
enforce: 'pre',
|
|
43
|
-
resolveId: async (id) => {
|
|
44
|
-
if (id === virtualModuleId) {
|
|
45
|
-
return resolvedVirtualModuleId;
|
|
46
|
-
}
|
|
47
|
-
if (POLYFILLS.includes(id)) {
|
|
48
|
-
return polyfillPath(normalizeId(id.replace('/promises', '')));
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* fake the content of this package and load the implementation of the mock
|
|
52
|
-
* features from the browser module directory
|
|
53
|
-
*/
|
|
54
|
-
if (id === '@wdio/browser-runner') {
|
|
55
|
-
return spyModulePath;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* allow to load the setup script from a script tag
|
|
59
|
-
*/
|
|
60
|
-
if (id.endsWith('@wdio/browser-runner/setup')) {
|
|
61
|
-
return setupModulePath;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* run WebdriverIO assertions within the Node.js context so we can do things like
|
|
65
|
-
* visual assertions or snapshot testing
|
|
66
|
-
*/
|
|
67
|
-
if (id === 'expect-webdriverio') {
|
|
68
|
-
return wdioExpectModulePath;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* make sure WDIO imports are resolved properly as ESM module
|
|
72
|
-
*/
|
|
73
|
-
if (id.startsWith('@wdio') || WDIO_PACKAGES.includes(id)) {
|
|
74
|
-
return url.fileURLToPath(await resolve(id, import.meta.url));
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* mock out imports that we can't transpile into browser land
|
|
78
|
-
*/
|
|
79
|
-
if (MODULES_TO_MOCK.includes(id)) {
|
|
80
|
-
return mockModulePath;
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
load(id) {
|
|
84
|
-
/**
|
|
85
|
-
* provide a list of protocol commands to generate the prototype in the browser
|
|
86
|
-
*/
|
|
87
|
-
if (id === resolvedVirtualModuleId) {
|
|
88
|
-
return /*js*/ `
|
|
89
|
-
import { fn } from '@wdio/browser-runner'
|
|
90
|
-
export const commands = ${JSON.stringify(protocolCommandList)}
|
|
91
|
-
export const automationProtocolPath = ${JSON.stringify(automationProtocolPath)}
|
|
92
|
-
export const wrappedFn = (...args) => fn()(...args)
|
|
93
|
-
`;
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
transform(code, id) {
|
|
97
|
-
if (id.includes('.vite/deps/expect.js')) {
|
|
98
|
-
return {
|
|
99
|
-
code: code.replace('var fs = _interopRequireWildcard(require_graceful_fs());', 'var fs = {};').replace('var expect_default = require_build11();', 'var expect_default = require_build11();\nwindow.expect = expect_default.default;').replace('process.stdout.isTTY', 'false')
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
return { code };
|
|
103
|
-
},
|
|
104
|
-
configureServer(server) {
|
|
105
|
-
return () => {
|
|
106
|
-
server.middlewares.use(async (req, res, next) => {
|
|
107
|
-
log.info(`Received request for: ${req.originalUrl}`);
|
|
108
|
-
/**
|
|
109
|
-
* don't return test page when sourcemaps are requested
|
|
110
|
-
*/
|
|
111
|
-
if (!req.originalUrl || req.url?.endsWith('.map') || req.url?.endsWith('.wasm')) {
|
|
112
|
-
return next();
|
|
113
|
-
}
|
|
114
|
-
const cookies = ((req.headers.cookie && req.headers.cookie.split(';')) || []).map((c) => c.trim());
|
|
115
|
-
const urlParsed = url.parse(req.originalUrl);
|
|
116
|
-
const urlParamString = new URLSearchParams(urlParsed.query || '');
|
|
117
|
-
const cid = urlParamString.get('cid') || cookies.find((c) => c.includes('WDIO_CID'))?.split('=').pop();
|
|
118
|
-
const spec = urlParamString.get('spec') || cookies.find((c) => c.includes('WDIO_SPEC'))?.split('=').pop();
|
|
119
|
-
if (!cid || !SESSIONS.has(cid)) {
|
|
120
|
-
log.error(`No environment found for ${cid || 'non determined environment'}`);
|
|
121
|
-
return next();
|
|
122
|
-
}
|
|
123
|
-
if (!spec) {
|
|
124
|
-
log.error('No spec file was defined to run for this environment');
|
|
125
|
-
return next();
|
|
126
|
-
}
|
|
127
|
-
const env = SESSIONS.get(cid);
|
|
128
|
-
try {
|
|
129
|
-
const template = await getTemplate(options, env, spec);
|
|
130
|
-
log.debug(`Render template for ${req.originalUrl}`);
|
|
131
|
-
res.end(await server.transformIndexHtml(`${req.originalUrl}`, template));
|
|
132
|
-
}
|
|
133
|
-
catch (err) {
|
|
134
|
-
const template = getErrorTemplate(req.originalUrl, err);
|
|
135
|
-
log.error(`Failed to render template: ${err.message}`);
|
|
136
|
-
res.end(await server.transformIndexHtml(`${req.originalUrl}`, template));
|
|
137
|
-
}
|
|
138
|
-
return next();
|
|
139
|
-
});
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
}, {
|
|
143
|
-
name: 'modern-node-polyfills',
|
|
144
|
-
async resolveId(id, _, ctx) {
|
|
145
|
-
if (ctx.ssr || !builtinModules.includes(id)) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
id = normalizeId(id);
|
|
149
|
-
return { id: await polyfillPath(id), moduleSideEffects: false };
|
|
150
|
-
},
|
|
151
|
-
}];
|
|
152
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { WDIO_EVENT_NAME } from '../../constants.js';
|
|
2
|
-
/**
|
|
3
|
-
* a Vite plugin to help communicate with the worker process
|
|
4
|
-
*/
|
|
5
|
-
export function workerPlugin(onSocketEvent) {
|
|
6
|
-
return {
|
|
7
|
-
name: 'wdio:worker',
|
|
8
|
-
configureServer({ ws }) {
|
|
9
|
-
ws.on(WDIO_EVENT_NAME, onSocketEvent);
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
}
|
package/build/vite/server.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { EventEmitter } from 'node:events';
|
|
3
|
-
import getPort from 'get-port';
|
|
4
|
-
import logger from '@wdio/logger';
|
|
5
|
-
import istanbulPlugin from 'vite-plugin-istanbul';
|
|
6
|
-
import { deepmerge } from 'deepmerge-ts';
|
|
7
|
-
import { createServer } from 'vite';
|
|
8
|
-
import { testrunner } from './plugins/testrunner.js';
|
|
9
|
-
import { mockHoisting } from './plugins/mockHoisting.js';
|
|
10
|
-
import { workerPlugin } from './plugins/worker.js';
|
|
11
|
-
import { userfriendlyImport } from './utils.js';
|
|
12
|
-
import { MockHandler } from './mock.js';
|
|
13
|
-
import { PRESET_DEPENDENCIES, DEFAULT_VITE_CONFIG } from './constants.js';
|
|
14
|
-
import { DEFAULT_INCLUDE, DEFAULT_FILE_EXTENSIONS } from '../constants.js';
|
|
15
|
-
const log = logger('@wdio/browser-runner:ViteServer');
|
|
16
|
-
const DEFAULT_CONFIG_ENV = {
|
|
17
|
-
command: 'serve',
|
|
18
|
-
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development'
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Class that sets up the Vite server with correct configuration based on given WebdriverIO options.
|
|
22
|
-
*/
|
|
23
|
-
export class ViteServer extends EventEmitter {
|
|
24
|
-
#options;
|
|
25
|
-
#config;
|
|
26
|
-
#viteConfig;
|
|
27
|
-
#server;
|
|
28
|
-
#mockHandler;
|
|
29
|
-
#socketEventHandler = [];
|
|
30
|
-
get config() {
|
|
31
|
-
return this.#viteConfig;
|
|
32
|
-
}
|
|
33
|
-
constructor(options, config, optimizations) {
|
|
34
|
-
super();
|
|
35
|
-
this.#options = options;
|
|
36
|
-
this.#config = config;
|
|
37
|
-
this.#mockHandler = new MockHandler(options, config);
|
|
38
|
-
const root = options.rootDir || config.rootDir || process.cwd();
|
|
39
|
-
this.#viteConfig = deepmerge(DEFAULT_VITE_CONFIG, optimizations, {
|
|
40
|
-
root,
|
|
41
|
-
plugins: [
|
|
42
|
-
testrunner(options),
|
|
43
|
-
mockHoisting(this.#mockHandler),
|
|
44
|
-
workerPlugin((payload, client) => (this.#socketEventHandler.forEach((handler) => handler(payload, client))))
|
|
45
|
-
]
|
|
46
|
-
});
|
|
47
|
-
if (options.coverage && options.coverage.enabled) {
|
|
48
|
-
log.info('Capturing test coverage enabled');
|
|
49
|
-
// @ts-expect-error istanbul plugin seems to incorrectly export
|
|
50
|
-
// its type for our setup
|
|
51
|
-
const plugin = istanbulPlugin;
|
|
52
|
-
this.#viteConfig.plugins?.push(plugin({
|
|
53
|
-
cwd: config.rootDir,
|
|
54
|
-
include: DEFAULT_INCLUDE,
|
|
55
|
-
extension: DEFAULT_FILE_EXTENSIONS,
|
|
56
|
-
forceBuildInstrument: true,
|
|
57
|
-
...options.coverage
|
|
58
|
-
}));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
onBrowserEvent(handler) {
|
|
62
|
-
this.#socketEventHandler.push(handler);
|
|
63
|
-
}
|
|
64
|
-
async start() {
|
|
65
|
-
const vitePort = await getPort();
|
|
66
|
-
this.#viteConfig = deepmerge(this.#viteConfig, {
|
|
67
|
-
server: {
|
|
68
|
-
host: '0.0.0.0',
|
|
69
|
-
port: vitePort
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
/**
|
|
73
|
-
* load additional Vite plugins for framework
|
|
74
|
-
*/
|
|
75
|
-
if (this.#options.preset) {
|
|
76
|
-
const [pkg, importProp, opts] = PRESET_DEPENDENCIES[this.#options.preset] || [];
|
|
77
|
-
const plugin = (await userfriendlyImport(this.#options.preset, pkg))[importProp || 'default'];
|
|
78
|
-
if (plugin) {
|
|
79
|
-
this.#viteConfig.plugins.push(plugin(opts));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* merge custom `viteConfig` last into the object
|
|
84
|
-
*/
|
|
85
|
-
if (this.#options.viteConfig) {
|
|
86
|
-
const { plugins, ...configToMerge } = typeof this.#options.viteConfig === 'string'
|
|
87
|
-
? (await import(path.resolve(this.#config.rootDir || process.cwd(), this.#options.viteConfig))).default
|
|
88
|
-
: typeof this.#options.viteConfig === 'function'
|
|
89
|
-
? await this.#options.viteConfig(DEFAULT_CONFIG_ENV)
|
|
90
|
-
: this.#options.viteConfig;
|
|
91
|
-
this.#viteConfig = deepmerge(this.#viteConfig, configToMerge);
|
|
92
|
-
this.#viteConfig.plugins = [...(plugins || []), ...this.#viteConfig.plugins];
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* initialize Vite
|
|
96
|
-
*/
|
|
97
|
-
this.#server = await createServer(this.#viteConfig);
|
|
98
|
-
await this.#server.listen();
|
|
99
|
-
log.info(`Vite server started successfully on port ${vitePort}, root directory: ${this.#viteConfig.root}`);
|
|
100
|
-
}
|
|
101
|
-
async close() {
|
|
102
|
-
await this.#server?.close();
|
|
103
|
-
}
|
|
104
|
-
}
|
package/build/vite/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|