@wdio/browser-runner 8.3.11 → 8.5.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.
@@ -1 +1 @@
1
- {"version":3,"file":"mocha.d.ts","sourceRoot":"","sources":["../../../src/browser/frameworks/mocha.ts"],"names":[],"mappings":"AAUA,qBAAa,cAAc;;gBAKV,MAAM,EAAE,SAAS;IA+B9B,GAAG;CA+DN"}
1
+ {"version":3,"file":"mocha.d.ts","sourceRoot":"","sources":["../../../src/browser/frameworks/mocha.ts"],"names":[],"mappings":"AASA,qBAAa,cAAc;;gBAKV,MAAM,EAAE,SAAS;IA+B9B,GAAG;CA+DN"}
@@ -1,4 +1,3 @@
1
- import Mocha from 'mocha';
2
1
  import stringify from 'fast-safe-stringify';
3
2
  import { setupEnv, formatMessage } from '@wdio/mocha-framework/common';
4
3
  import { MESSAGE_TYPES, EVENTS } from '../../constants.js';
@@ -95,7 +94,7 @@ const BaseReporter = window.Mocha.reporters.html;
95
94
  class HTMLReporter extends BaseReporter {
96
95
  addCodeToggle() { }
97
96
  }
98
- Mocha.setup({
97
+ mocha.setup({
99
98
  ...window.__wdioEnv__.args,
100
99
  reporter: HTMLReporter
101
100
  });
@@ -7,6 +7,7 @@ declare global {
7
7
  __wdioEvents__: any[];
8
8
  __wdioSocket__: WebSocket;
9
9
  __wdioConnectPromise__: Promise<WebSocket>;
10
+ __wdioMockFactories__: Record<string, any>;
10
11
  }
11
12
  }
12
13
  export declare const socket: WebSocket;
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/browser/setup.ts"],"names":[],"mappings":"AAQA,KAAK,cAAc,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC,CAAA;AAC9D,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,cAAc,EAAE,cAAc,EAAE,CAAA;QAChC,YAAY,EAAE,MAAM,CAAA;QACpB,gBAAgB,EAAE,MAAM,CAAA;QACxB,cAAc,EAAE,GAAG,EAAE,CAAA;QACrB,cAAc,EAAE,SAAS,CAAA;QACzB,sBAAsB,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;KAC7C;CACJ;AAOD,eAAO,MAAM,MAAM,WAA+C,CAAA;AAClE,eAAO,MAAM,cAAc,oBAGzB,CAAA"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/browser/setup.ts"],"names":[],"mappings":"AAQA,KAAK,cAAc,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC,CAAA;AAC9D,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,cAAc,EAAE,cAAc,EAAE,CAAA;QAChC,YAAY,EAAE,MAAM,CAAA;QACpB,gBAAgB,EAAE,MAAM,CAAA;QACxB,cAAc,EAAE,GAAG,EAAE,CAAA;QACrB,cAAc,EAAE,SAAS,CAAA;QACzB,sBAAsB,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;QAC1C,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAC7C;CACJ;AAOD,eAAO,MAAM,MAAM,WAA+C,CAAA;AAClE,eAAO,MAAM,cAAc,oBAGzB,CAAA"}
@@ -1,5 +1,13 @@
1
+ import type { MaybeMocked } from '@vitest/spy';
2
+ import type { MockFactoryWithHelper } from '../types';
1
3
  /**
2
4
  * re-export mock module
3
5
  */
4
6
  export * from '@vitest/spy';
7
+ export declare function mock(path: string, factory?: MockFactoryWithHelper): Promise<unknown>;
8
+ export declare function unmock(moduleName: string): void;
9
+ /**
10
+ * utility helper for type conversions
11
+ */
12
+ export declare function mocked<T>(item: T): MaybeMocked<T>;
5
13
  //# sourceMappingURL=spy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spy.d.ts","sourceRoot":"","sources":["../../src/browser/spy.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"spy.d.ts","sourceRoot":"","sources":["../../src/browser/spy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAG9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAGrD;;GAEG;AACH,cAAc,aAAa,CAAA;AAa3B,wBAAsB,IAAI,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,oBA4BxE;AAGD,wBAAgB,MAAM,CAAC,UAAU,EAAE,MAAM,QAExC;AAeD;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,kBAA0C"}
@@ -1,4 +1,61 @@
1
+ import { MESSAGE_TYPES } from '../constants.js';
1
2
  /**
2
3
  * re-export mock module
3
4
  */
4
5
  export * from '@vitest/spy';
6
+ const a = document.createElement('a');
7
+ function resolveUrl(path) {
8
+ a.href = path;
9
+ return a.href;
10
+ }
11
+ const ERROR_MESSAGE = '[wdio] There was an error, when mocking a module. If you are using the "mock" factory, make sure there are no top level variables inside, since this call is hoisted to top of the file. Read more: https://webdriver.io/docs/component-testing/mocking';
12
+ const socket = window.__wdioSocket__;
13
+ const mockResolver = new Map();
14
+ const origin = window.__wdioSpec__.split('/').slice(0, -1).join('/');
15
+ window.__wdioMockFactories__ = [];
16
+ export async function mock(path, factory) {
17
+ /**
18
+ * mock calls without factory parameter should get removed from the source code
19
+ * by the mock hoisting plugin
20
+ */
21
+ if (!factory) {
22
+ return;
23
+ }
24
+ const mockLocalFile = path.startsWith('/') || path.startsWith('./');
25
+ const mockPath = mockLocalFile
26
+ ? (new URL(resolveUrl(window.__wdioSpec__.split('/').slice(0, -1).join('/') + '/' + path))).pathname
27
+ : path;
28
+ try {
29
+ const resolvedMock = await factory(() => (import(mockLocalFile ? `/@mock${mockPath}` : `/node_modules/.vite/deps/${mockPath}.js`)));
30
+ socket.send(JSON.stringify({
31
+ type: MESSAGE_TYPES.mockRequest,
32
+ value: { path: mockPath, origin, namedExports: Object.keys(resolvedMock) }
33
+ }));
34
+ window.__wdioMockFactories__[mockPath] = resolvedMock;
35
+ return new Promise((resolve) => mockResolver.set(mockPath, resolve));
36
+ }
37
+ catch (err) {
38
+ throw new Error(ERROR_MESSAGE + '\n' + err.stack);
39
+ }
40
+ }
41
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
42
+ export function unmock(moduleName) {
43
+ // NO-OP: call gets removed by recast
44
+ }
45
+ socket.addEventListener('message', (ev) => {
46
+ try {
47
+ const { type, value } = JSON.parse(ev.data);
48
+ const resolver = mockResolver.get(value.path);
49
+ if (type !== MESSAGE_TYPES.mockResponse || !resolver) {
50
+ return;
51
+ }
52
+ return resolver(null);
53
+ }
54
+ catch {
55
+ // ignore
56
+ }
57
+ });
58
+ /**
59
+ * utility helper for type conversions
60
+ */
61
+ export function mocked(item) { return item; }
@@ -20,11 +20,15 @@ export declare enum MESSAGE_TYPES {
20
20
  commandRequestMessage = 1,
21
21
  commandResponseMessage = 2,
22
22
  hookTriggerMessage = 3,
23
- hookResultMessage = 4
23
+ hookResultMessage = 4,
24
+ mockRequest = 5,
25
+ mockResponse = 6
24
26
  }
25
27
  export declare const DEFAULT_INCLUDE: string[];
26
28
  export declare const DEFAULT_FILE_EXTENSIONS: string[];
27
29
  export declare const DEFAULT_REPORTS_DIRECTORY = "coverage";
30
+ export declare const DEFAULT_AUTOMOCK = true;
31
+ export declare const DEFAULT_MOCK_DIRECTORY = "__mocks__";
28
32
  export declare const SUMMARY_REPORTER = "json-summary";
29
33
  export declare const COVERAGE_FACTORS: readonly ["lines", "functions", "branches", "statements"];
30
34
  export declare const DEFAULT_COVERAGE_REPORTS: (keyof ReportOptions)[];
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,eAAO,MAAM,QAAQ,0BAAiC,CAAA;AACtD,eAAO,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAa,CAAA;AAEvE,eAAO,MAAM,MAAM;;;;;;;;;;;CAWT,CAAA;AAEV,eAAO,MAAM,uBAAuB,wFAAsF,CAAA;AAE1H,oBAAY,aAAa;IACrB,cAAc,IAAI;IAClB,qBAAqB,IAAA;IACrB,sBAAsB,IAAA;IACtB,kBAAkB,IAAA;IAClB,iBAAiB,IAAA;CACpB;AAED,eAAO,MAAM,eAAe,UAAS,CAAA;AACrC,eAAO,MAAM,uBAAuB,UAAoF,CAAA;AACxH,eAAO,MAAM,yBAAyB,aAAa,CAAA;AACnD,eAAO,MAAM,gBAAgB,iBAAiB,CAAA;AAC9C,eAAO,MAAM,gBAAgB,2DAA4D,CAAA;AACzF,eAAO,MAAM,wBAAwB,EAAE,CAAC,MAAM,aAAa,CAAC,EAAiD,CAAA;AAC7G,eAAO,MAAM,yBAAyB,sEAAsE,CAAA;AAC5G,eAAO,MAAM,uBAAuB,sEAAsE,CAAA"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,eAAO,MAAM,QAAQ,0BAAiC,CAAA;AACtD,eAAO,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAa,CAAA;AAEvE,eAAO,MAAM,MAAM;;;;;;;;;;;CAWT,CAAA;AAEV,eAAO,MAAM,uBAAuB,wFAAsF,CAAA;AAE1H,oBAAY,aAAa;IACrB,cAAc,IAAI;IAClB,qBAAqB,IAAA;IACrB,sBAAsB,IAAA;IACtB,kBAAkB,IAAA;IAClB,iBAAiB,IAAA;IACjB,WAAW,IAAA;IACX,YAAY,IAAA;CACf;AAED,eAAO,MAAM,eAAe,UAAS,CAAA;AACrC,eAAO,MAAM,uBAAuB,UAAoF,CAAA;AACxH,eAAO,MAAM,yBAAyB,aAAa,CAAA;AACnD,eAAO,MAAM,gBAAgB,OAAO,CAAA;AACpC,eAAO,MAAM,sBAAsB,cAAc,CAAA;AACjD,eAAO,MAAM,gBAAgB,iBAAiB,CAAA;AAC9C,eAAO,MAAM,gBAAgB,2DAA4D,CAAA;AACzF,eAAO,MAAM,wBAAwB,EAAE,CAAC,MAAM,aAAa,CAAC,EAAiD,CAAA;AAC7G,eAAO,MAAM,yBAAyB,sEAAsE,CAAA;AAC5G,eAAO,MAAM,uBAAuB,sEAAsE,CAAA"}
@@ -20,10 +20,14 @@ export var MESSAGE_TYPES;
20
20
  MESSAGE_TYPES[MESSAGE_TYPES["commandResponseMessage"] = 2] = "commandResponseMessage";
21
21
  MESSAGE_TYPES[MESSAGE_TYPES["hookTriggerMessage"] = 3] = "hookTriggerMessage";
22
22
  MESSAGE_TYPES[MESSAGE_TYPES["hookResultMessage"] = 4] = "hookResultMessage";
23
+ MESSAGE_TYPES[MESSAGE_TYPES["mockRequest"] = 5] = "mockRequest";
24
+ MESSAGE_TYPES[MESSAGE_TYPES["mockResponse"] = 6] = "mockResponse";
23
25
  })(MESSAGE_TYPES || (MESSAGE_TYPES = {}));
24
26
  export const DEFAULT_INCLUDE = ['**'];
25
27
  export const DEFAULT_FILE_EXTENSIONS = ['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.tsx', '.jsx', '.vue', '.svelte'];
26
28
  export const DEFAULT_REPORTS_DIRECTORY = 'coverage';
29
+ export const DEFAULT_AUTOMOCK = true;
30
+ export const DEFAULT_MOCK_DIRECTORY = '__mocks__';
27
31
  export const SUMMARY_REPORTER = 'json-summary';
28
32
  export const COVERAGE_FACTORS = ['lines', 'functions', 'branches', 'statements'];
29
33
  export const DEFAULT_COVERAGE_REPORTS = ['text', 'html', 'clover', SUMMARY_REPORTER];
package/build/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import type { RunArgs, WorkerInstance } from '@wdio/local-runner';
2
1
  import LocalRunner from '@wdio/local-runner';
2
+ import type { RunArgs, WorkerInstance } from '@wdio/local-runner';
3
3
  import type { Options } from '@wdio/types';
4
- import type { BrowserRunnerOptions as BrowserRunnerOptionsImport } from './types.js';
4
+ import type { MaybeMocked, MaybeMockedDeep, MaybePartiallyMocked, MaybePartiallyMockedDeep } from '@vitest/spy';
5
+ import type { BrowserRunnerOptions as BrowserRunnerOptionsImport, MockFactoryWithHelper } from './types.js';
5
6
  export default class BrowserRunner extends LocalRunner {
6
7
  #private;
7
8
  private options;
@@ -31,4 +32,63 @@ declare global {
31
32
  * re-export mock types
32
33
  */
33
34
  export * from '@vitest/spy';
35
+ /**
36
+ * The following exports are meaningless and only there to allow proper type support.
37
+ * The actual implementation can be found in /src/browser.spy.ts
38
+ */
39
+ /**
40
+ * Makes all imports to passed module to be mocked.
41
+ *
42
+ * If there is a factory, will return it's result. The call to `mock` is hoisted to the top of the file,
43
+ * so you don't have access to variables declared in the global file scope, if you didn't put them before imports!
44
+ *
45
+ * If __mocks__ folder with file of the same name exist, all imports will return it.
46
+ *
47
+ * If there is no __mocks__ folder or a file with the same name inside, will call original module and mock it.
48
+ *
49
+ * @param {string} path Path to the module.
50
+ * @param {MockFactoryWithHelper} factory (optional) Factory for the mocked module. Has the highest priority.
51
+ */
52
+ export declare function mock(path: string, factory?: MockFactoryWithHelper): void;
53
+ /**
54
+ * Removes module from mocked registry. All subsequent calls to import will return original module even if it was mocked.
55
+ *
56
+ * @param path Path to the module.
57
+ */
58
+ export declare function unmock(moduleName: string): void;
59
+ /**
60
+ * Type helpers for TypeScript. In reality just returns the object that was passed.
61
+ * @example
62
+ * import example from './example'
63
+ * vi.mock('./example')
64
+ *
65
+ * test('1+1 equals 2' async () => {
66
+ * vi.mocked(example.calc).mockRestore()
67
+ *
68
+ * const res = example.calc(1, '+', 1)
69
+ *
70
+ * expect(res).toBe(2)
71
+ * })
72
+ *
73
+ * @param item Anything that can be mocked
74
+ * @returns
75
+ */
76
+ export declare function mocked<T>(item: T, deep?: true): MaybeMockedDeep<T>;
77
+ export declare function mocked<T>(item: T, deep?: false): MaybeMockedDeep<T>;
78
+ export declare function mocked<T>(item: T, options: {
79
+ partial?: false;
80
+ deep?: false;
81
+ }): MaybeMocked<T>;
82
+ export declare function mocked<T>(item: T, options: {
83
+ partial?: false;
84
+ deep: true;
85
+ }): MaybeMockedDeep<T>;
86
+ export declare function mocked<T>(item: T, options: {
87
+ partial: true;
88
+ deep?: false;
89
+ }): MaybePartiallyMocked<T>;
90
+ export declare function mocked<T>(item: T, options: {
91
+ partial: true;
92
+ deep: true;
93
+ }): MaybePartiallyMockedDeep<T>;
34
94
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACjE,OAAO,WAAW,MAAM,oBAAoB,CAAA;AAQ5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAS1C,OAAO,KAAK,EAAE,oBAAoB,IAAI,0BAA0B,EAAmB,MAAM,YAAY,CAAA;AAGrG,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;;IAU9C,OAAO,CAAC,OAAO;IACf,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU;IAJzC,OAAO,CAAC,aAAa,CAAoB;gBAG7B,OAAO,EAAE,0BAA0B,EACjC,OAAO,EAAE,OAAO,CAAC,UAAU;IAczC;;OAEG;IACG,UAAU;IAoBhB,GAAG,CAAE,OAAO,EAAE,OAAO,GAAG,cAAc;IAoBtC;;;;OAIG;IACG,QAAQ;YA8CA,wBAAwB;CA2DzC;AAED,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,WAAW,CAAC;QAClB,UAAU,oBAAqB,SAAQ,0BAA0B;SAAG;KACvE;CACJ;AAED;;GAEG;AACH,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,WAAW,MAAM,oBAAoB,CAAA;AAO5C,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAS/G,OAAO,KAAK,EAAE,oBAAoB,IAAI,0BAA0B,EAAmB,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAG5H,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;;IAU9C,OAAO,CAAC,OAAO;IACf,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU;IAJzC,OAAO,CAAC,aAAa,CAAoB;gBAG7B,OAAO,EAAE,0BAA0B,EACjC,OAAO,EAAE,OAAO,CAAC,UAAU;IAczC;;OAEG;IACG,UAAU;IAoBhB,GAAG,CAAE,OAAO,EAAE,OAAO,GAAG,cAAc;IAoBtC;;;;OAIG;IACG,QAAQ;YA8CA,wBAAwB;CA2DzC;AAED,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,WAAW,CAAC;QAClB,UAAU,oBAAqB,SAAQ,0BAA0B;SAAG;KACvE;CACJ;AAED;;GAEG;AACH,cAAc,aAAa,CAAA;AAE3B;;;GAGG;AAEH;;;;;;;;;;;;GAYG;AAEH,wBAAgB,IAAI,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,QAAI;AAEvE;;;;GAIG;AAEH,wBAAgB,MAAM,CAAC,UAAU,EAAE,MAAM,QAAI;AAE7C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC,CAAA;AACnE,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,KAAK,GAAG,eAAe,CAAC,CAAC,CAAC,CAAA;AACpE,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE;IACxC,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,IAAI,CAAC,EAAE,KAAK,CAAC;CAChB,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;AACnB,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE;IACxC,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,IAAI,EAAE,IAAI,CAAC;CACd,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;AACvB,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE;IACxC,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC;CAChB,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;AAC5B,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE;IACxC,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;CACd,GAAG,wBAAwB,CAAC,CAAC,CAAC,CAAA"}
package/build/index.js CHANGED
@@ -166,3 +166,31 @@ export default class BrowserRunner extends LocalRunner {
166
166
  * re-export mock types
167
167
  */
168
168
  export * from '@vitest/spy';
169
+ /**
170
+ * The following exports are meaningless and only there to allow proper type support.
171
+ * The actual implementation can be found in /src/browser.spy.ts
172
+ */
173
+ /**
174
+ * Makes all imports to passed module to be mocked.
175
+ *
176
+ * If there is a factory, will return it's result. The call to `mock` is hoisted to the top of the file,
177
+ * so you don't have access to variables declared in the global file scope, if you didn't put them before imports!
178
+ *
179
+ * If __mocks__ folder with file of the same name exist, all imports will return it.
180
+ *
181
+ * If there is no __mocks__ folder or a file with the same name inside, will call original module and mock it.
182
+ *
183
+ * @param {string} path Path to the module.
184
+ * @param {MockFactoryWithHelper} factory (optional) Factory for the mocked module. Has the highest priority.
185
+ */
186
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
187
+ export function mock(path, factory) { }
188
+ /**
189
+ * Removes module from mocked registry. All subsequent calls to import will return original module even if it was mocked.
190
+ *
191
+ * @param path Path to the module.
192
+ */
193
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
194
+ export function unmock(moduleName) { }
195
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
196
+ export function mocked(item, options) { }
package/build/types.d.ts CHANGED
@@ -92,6 +92,16 @@ export interface BrowserRunnerOptions {
92
92
  * test coverage settings
93
93
  */
94
94
  coverage?: CoverageOptions;
95
+ /**
96
+ * If set to `true` WebdriverIO will automatically mock dependencies within the `automockDir` directory
97
+ * @default true
98
+ */
99
+ automock?: boolean;
100
+ /**
101
+ * Path of auto-mock directory. This tells WebdriverIO where to look for dependencies to mock out.
102
+ * @default ./__mocks__
103
+ */
104
+ automockDir?: string;
95
105
  }
96
106
  export interface RunArgs extends Workers.WorkerRunPayload {
97
107
  command: string;
@@ -105,5 +115,6 @@ export interface Environment {
105
115
  sessionId: string;
106
116
  injectGlobals: boolean;
107
117
  }
118
+ export type MockFactoryWithHelper = (importOriginal: <T = unknown>() => Promise<T>) => any;
108
119
  export {};
109
120
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAEjE,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,WAAW,EAAE,WAAW,CAAA;QACxB,iBAAiB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;KAC9C;CACJ;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAA;AACrF,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;AAChC,KAAK,gBAAgB,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,cAAc,GAAG,MAAM,CAAA;AAClL,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,qBAAqB,EAAE,SAAS,GAAG,WAAW,GAAG,sBAAsB,CAAC;IAClH;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC,gBAAgB,CAAC,CAAA;IACtC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,oBAAoB;IACjC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB;;OAEG;IACH,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC7B;AAED,MAAM,WAAW,OAAQ,SAAQ,OAAO,CAAC,gBAAgB;IACrD,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,GAAG,CAAA;IACT,GAAG,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,SAAS,CAAA;IACf,MAAM,EAAE,OAAO,CAAC,UAAU,CAAA;IAC1B,YAAY,EAAE,YAAY,CAAC,gBAAgB,CAAA;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,OAAO,CAAA;CACzB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAEjE,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,WAAW,EAAE,WAAW,CAAA;QACxB,iBAAiB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;KAC9C;CACJ;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAA;AACrF,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;AAChC,KAAK,gBAAgB,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,cAAc,GAAG,MAAM,CAAA;AAClL,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,qBAAqB,EAAE,SAAS,GAAG,WAAW,GAAG,sBAAsB,CAAC;IAClH;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC,gBAAgB,CAAC,CAAA;IACtC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,oBAAoB;IACjC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB;;OAEG;IACH,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,OAAQ,SAAQ,OAAO,CAAC,gBAAgB;IACrD,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,GAAG,CAAA;IACT,GAAG,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,SAAS,CAAA;IACf,MAAM,EAAE,OAAO,CAAC,UAAU,CAAA;IAC1B,YAAY,EAAE,YAAY,CAAC,gBAAgB,CAAA;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,GAAG,OAAO,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { Options } from '@wdio/types';
2
+ import type { MockRequestEvent } from './types';
3
+ export declare class MockHandler {
4
+ #private;
5
+ manualMocks: string[];
6
+ constructor(options: WebdriverIO.BrowserRunnerOptions, config: Options.Testrunner);
7
+ get mocks(): Map<string, MockRequestEvent>;
8
+ addMock(mock: MockRequestEvent): void;
9
+ unmock(moduleName: string): void;
10
+ resolveId(id: string): Promise<string | undefined>;
11
+ /**
12
+ * reset manual mocks between tests
13
+ */
14
+ resetMocks(): void;
15
+ }
16
+ //# sourceMappingURL=mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/vite/mock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAI1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI/C,qBAAa,WAAW;;IAOpB,WAAW,EAAE,MAAM,EAAE,CAAK;gBAEb,OAAO,EAAE,WAAW,CAAC,oBAAoB,EAAE,MAAM,EAAE,OAAO,CAAC,UAAU;IAMlF,IAAI,KAAK,kCAER;IAED,OAAO,CAAE,IAAI,EAAE,gBAAgB;IAI/B,MAAM,CAAE,UAAU,EAAE,MAAM;IAIpB,SAAS,CAAE,EAAE,EAAE,MAAM;IAwB3B;;OAEG;IACH,UAAU;CAIb"}
@@ -0,0 +1,53 @@
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
+ addMock(mock) {
21
+ this.#mocks.set(mock.path, mock);
22
+ }
23
+ unmock(moduleName) {
24
+ this.#unmocked.push(moduleName);
25
+ }
26
+ async resolveId(id) {
27
+ const manualMocksList = await this.#manualMocksList;
28
+ const mockPath = manualMocksList.find((m) => (
29
+ // e.g. someModule
30
+ id === m[1].replace(path.sep, '/') ||
31
+ // e.g. @some/module
32
+ id.slice(1) === m[1].replace(path.sep, '/')));
33
+ /**
34
+ * return manual mock if `automock` is enabled or a manual mock was set via `mock('module')`
35
+ */
36
+ if ((this.manualMocks.includes(id) || this.#automock) && mockPath && !this.#unmocked.includes(id)) {
37
+ return mockPath[0];
38
+ }
39
+ /**
40
+ * return fixture
41
+ */
42
+ if (id.startsWith(FIXTURE_PREFIX)) {
43
+ return path.resolve(this.#automockDir, id.slice(FIXTURE_PREFIX.length));
44
+ }
45
+ }
46
+ /**
47
+ * reset manual mocks between tests
48
+ */
49
+ resetMocks() {
50
+ this.manualMocks = [];
51
+ this.#unmocked = [];
52
+ }
53
+ }
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { MockHandler } from '../mock.js';
3
+ export declare function mockHoisting(mockHandler: MockHandler): Plugin[];
4
+ //# sourceMappingURL=mockHoisting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mockHoisting.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/mockHoisting.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAM7C,wBAAgB,YAAY,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,EAAE,CA2M/D"}
@@ -0,0 +1,195 @@
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 b = types.builders;
10
+ const MOCK_PREFIX = '/@mock';
11
+ export function mockHoisting(mockHandler) {
12
+ let spec = null;
13
+ return [{
14
+ name: 'wdio:mockHoisting:pre',
15
+ enforce: 'pre',
16
+ resolveId: mockHandler.resolveId.bind(mockHandler),
17
+ load: async function (id) {
18
+ if (id.startsWith(MOCK_PREFIX)) {
19
+ try {
20
+ const orig = await fs.readFile(id.slice(MOCK_PREFIX.length + (os.platform() === 'win32' ? 1 : 0)));
21
+ return orig.toString();
22
+ }
23
+ catch (err) {
24
+ log.error(`Failed to read file (${id}) for mocking: ${err.message}`);
25
+ return '';
26
+ }
27
+ }
28
+ const preBundledDepName = path.basename(id).split('?')[0];
29
+ const mockedMod = (
30
+ // mocked file
31
+ mockHandler.mocks.get(os.platform() === 'win32' ? `/${id}` : id) ||
32
+ // mocked dependency
33
+ mockHandler.mocks.get(path.basename(id, path.extname(id))) ||
34
+ // pre-bundled deps e.g. /node_modules/.vite/deps/algoliasearch_lite.js?v=e31c24e
35
+ [...mockHandler.mocks.values()].find((mock) => `${mock.path.replace('/', '_')}.js` === preBundledDepName));
36
+ if (mockedMod) {
37
+ const newCode = mockedMod.namedExports.map((ne) => {
38
+ if (ne === 'default') {
39
+ return /*js*/ `export default window.__wdioMockFactories__['${mockedMod.path}'].default;`;
40
+ }
41
+ return /*js*/ `export const ${ne} = window.__wdioMockFactories__['${mockedMod.path}']['${ne}'];`;
42
+ });
43
+ if (!mockedMod.namedExports.includes('default')) {
44
+ newCode.push(/*js*/ `export default window.__wdioMockFactories__['${mockedMod.path}'];`);
45
+ }
46
+ log.debug(`Resolve mock for module "${mockedMod.path}"`);
47
+ return newCode.join('\n');
48
+ }
49
+ }
50
+ }, {
51
+ name: 'wdio:mockHoisting',
52
+ enforce: 'post',
53
+ transform(code, id) {
54
+ /**
55
+ * only transform when spec file is transformed
56
+ */
57
+ if (id !== spec) {
58
+ return { code };
59
+ }
60
+ const ast = parse(code, {
61
+ parser: typescriptParser,
62
+ sourceFileName: id,
63
+ sourceRoot: path.dirname(id)
64
+ });
65
+ let mockFunctionName;
66
+ let unmockFunctionName;
67
+ const mockCalls = [];
68
+ /**
69
+ * rewrite import statements into variable declarations, e.g. from
70
+ *
71
+ * import React, { RC } from 'react'
72
+ *
73
+ * to
74
+ *
75
+ * var { default: React, RC: RC } = await import("react")
76
+ *
77
+ * so we can hoist the mock call
78
+ */
79
+ visit(ast, {
80
+ visitImportDeclaration: function (path) {
81
+ const dec = path.value;
82
+ const source = dec.source.value;
83
+ /**
84
+ * get name of mock function variable
85
+ */
86
+ if (source === '@wdio/browser-runner') {
87
+ const mockSpecifier = dec.specifiers
88
+ .filter((s) => s.type === types.namedTypes.ImportSpecifier.toString())
89
+ .find((s) => s.imported.name === 'mock');
90
+ if (mockSpecifier && mockSpecifier.local) {
91
+ mockFunctionName = mockSpecifier.local.name;
92
+ }
93
+ const unmockSpecifier = dec.specifiers
94
+ .filter((s) => s.type === types.namedTypes.ImportSpecifier.toString())
95
+ .find((s) => s.imported.name === 'unmock');
96
+ if (unmockSpecifier && unmockSpecifier.local) {
97
+ unmockFunctionName = unmockSpecifier.local.name;
98
+ }
99
+ mockCalls.push(dec);
100
+ path.prune();
101
+ return this.traverse(path);
102
+ }
103
+ const newNode = b.variableDeclaration('const', [
104
+ b.variableDeclarator((dec.specifiers?.length === 1 && dec.specifiers[0].type === types.namedTypes.ImportNamespaceSpecifier.toString())
105
+ /**
106
+ * we deal with a ImportNamespaceSpecifier, e.g.:
107
+ * import * as foo from 'bar'
108
+ */
109
+ ? dec.specifiers[0].local
110
+ /**
111
+ * we deal with default or named import, e.g.
112
+ * import foo from 'bar'
113
+ * or
114
+ * import { foo } from 'bar'
115
+ */
116
+ : b.objectPattern(dec.specifiers.map((s) => {
117
+ if (s.type === types.namedTypes.ImportDefaultSpecifier.toString()) {
118
+ return b.property('init', b.identifier('default'), b.identifier(s.local.name));
119
+ }
120
+ return b.property('init', b.identifier(s.imported.name), b.identifier(s.local.name));
121
+ })), b.awaitExpression(b.importExpression(b.literal(source))))
122
+ ]);
123
+ path.replace(newNode);
124
+ this.traverse(path);
125
+ },
126
+ visitExpressionStatement: function (path) {
127
+ const exp = path.value;
128
+ if (exp.expression.type !== types.namedTypes.CallExpression.toString()) {
129
+ return this.traverse(path);
130
+ }
131
+ const callExp = exp.expression;
132
+ const isUnmockCall = unmockFunctionName && callExp.callee.name === unmockFunctionName;
133
+ const isMockCall = mockFunctionName && callExp.callee.name === mockFunctionName;
134
+ if (!isMockCall && !isUnmockCall) {
135
+ return this.traverse(path);
136
+ }
137
+ /**
138
+ * hoist unmock calls
139
+ */
140
+ if (isUnmockCall && callExp.arguments[0] && typeof callExp.arguments[0].value === 'string') {
141
+ mockHandler.unmock(callExp.arguments[0].value);
142
+ }
143
+ else if (isMockCall) {
144
+ /**
145
+ * if only one mock argument is set, we take the fixture from the automock directory
146
+ */
147
+ const mockCall = exp.expression;
148
+ if (mockCall.arguments.length === 1) {
149
+ /**
150
+ * enable manual mock
151
+ */
152
+ mockHandler.manualMocks.push(mockCall.arguments[0].value);
153
+ }
154
+ else {
155
+ /**
156
+ * hoist mock calls
157
+ */
158
+ mockCalls.push(exp);
159
+ }
160
+ }
161
+ path.prune();
162
+ this.traverse(path);
163
+ }
164
+ });
165
+ ast.program.body.unshift(...mockCalls.map((mc) => {
166
+ const exp = mc;
167
+ if (exp.expression && exp.expression.type === types.namedTypes.CallExpression.toString()) {
168
+ return b.expressionStatement(b.awaitExpression(exp.expression));
169
+ }
170
+ return mc;
171
+ }));
172
+ const newCode = print(ast, {
173
+ sourceMapName: id
174
+ });
175
+ return newCode;
176
+ },
177
+ configureServer(server) {
178
+ return () => {
179
+ server.middlewares.use('/', async (req, res, next) => {
180
+ if (!req.url) {
181
+ return next();
182
+ }
183
+ const urlParsed = url.parse(req.url);
184
+ const urlParamString = new URLSearchParams(urlParsed.query || '');
185
+ const specParam = urlParamString.get('spec');
186
+ if (specParam) {
187
+ mockHandler.resetMocks();
188
+ spec = os.platform() === 'win32' ? specParam.slice(1) : specParam;
189
+ }
190
+ return next();
191
+ });
192
+ };
193
+ }
194
+ }];
195
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"testrunner.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/testrunner.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AA2ClC,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,oBAAoB,GAAG,MAAM,EAAE,CAgI9E"}
1
+ {"version":3,"file":"testrunner.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/testrunner.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AA0ClC,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,oBAAoB,GAAG,MAAM,EAAE,CAgI9E"}
@@ -25,8 +25,7 @@ const POLYFILLS = [
25
25
  const FETCH_FROM_ESM = [
26
26
  'serialize-error', 'minimatch', 'css-shorthand-properties', 'lodash.merge', 'lodash.zip',
27
27
  'lodash.clonedeep', 'lodash.pickby', 'lodash.flattendeep', 'aria-query', 'grapheme-splitter',
28
- 'css-value', 'rgb2hex', 'p-iteration', 'fast-safe-stringify', 'deepmerge-ts',
29
- 'mocha'
28
+ 'css-value', 'rgb2hex', 'p-iteration', 'fast-safe-stringify', 'deepmerge-ts'
30
29
  ];
31
30
  export function testrunner(options) {
32
31
  const automationProtocolPath = `/@fs${url.pathToFileURL(path.resolve(__dirname, '..', '..', 'browser', 'driver.js')).pathname}`;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/vite/server.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAM1C,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAA;AAGpC,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,MAAM,CAAA;AAGvD,OAAO,KAAK,EAAY,OAAO,EAAE,MAAM,aAAa,CAAA;AAMpD,OAAO,KAAK,EAAgB,gBAAgB,EAA6E,MAAM,YAAY,CAAA;AAY3I,qBAAa,UAAW,SAAQ,YAAY;;IAQxC,IAAI,YAAY,gCAEf;IAED,IAAI,MAAM,0BAET;gBAEY,OAAO,EAAE,WAAW,CAAC,oBAAoB,EAAE,MAAM,EAAE,OAAO,CAAC,UAAU;IA6B5E,KAAK;IAwCL,KAAK;IA2IX,OAAO,CAAC,cAAc;IAetB,WAAW,CAAE,MAAM,EAAE,gBAAgB;CAUxC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/vite/server.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAM1C,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAA;AAGpC,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,MAAM,CAAA;AAGvD,OAAO,KAAK,EAAY,OAAO,EAAE,MAAM,aAAa,CAAA;AAQpD,OAAO,KAAK,EACM,gBAAgB,EAEjC,MAAM,YAAY,CAAA;AAYnB,qBAAa,UAAW,SAAQ,YAAY;;IASxC,IAAI,YAAY,gCAEf;IAED,IAAI,MAAM,0BAET;gBAEY,OAAO,EAAE,WAAW,CAAC,oBAAoB,EAAE,MAAM,EAAE,OAAO,CAAC,UAAU;IAiC5E,KAAK;IAwCL,KAAK;IAkJX,OAAO,CAAC,cAAc;IAetB,WAAW,CAAE,MAAM,EAAE,gBAAgB;CAUxC"}
@@ -8,7 +8,9 @@ import { executeHooksWithArgs } from '@wdio/utils';
8
8
  import { createServer } from 'vite';
9
9
  import istanbulPlugin from 'vite-plugin-istanbul';
10
10
  import { testrunner } from './plugins/testrunner.js';
11
+ import { mockHoisting } from './plugins/mockHoisting.js';
11
12
  import { userfriendlyImport } from './utils.js';
13
+ import { MockHandler } from './mock.js';
12
14
  import { PRESET_DEPENDENCIES, DEFAULT_VITE_CONFIG } from './constants.js';
13
15
  import { MESSAGE_TYPES, DEFAULT_INCLUDE, DEFAULT_FILE_EXTENSIONS } from '../constants.js';
14
16
  import { BROWSER_POOL, SESSIONS } from '../constants.js';
@@ -21,6 +23,7 @@ export class ViteServer extends EventEmitter {
21
23
  #viteConfig;
22
24
  #wss;
23
25
  #server;
26
+ #mockHandler;
24
27
  get socketServer() {
25
28
  return this.#wss;
26
29
  }
@@ -30,12 +33,16 @@ export class ViteServer extends EventEmitter {
30
33
  constructor(options, config) {
31
34
  super();
32
35
  this.#options = options;
36
+ this.#mockHandler = new MockHandler(options, config);
33
37
  if (options.preset && options.viteConfig) {
34
38
  throw new Error('Invalid runner configuration: "preset" and "viteConfig" options are defined but only one of each can be used at the same time');
35
39
  }
36
40
  this.#viteConfig = deepmerge(DEFAULT_VITE_CONFIG, {
37
41
  root: options.rootDir || process.cwd(),
38
- plugins: [testrunner(options)]
42
+ plugins: [
43
+ testrunner(options),
44
+ mockHoisting(this.#mockHandler)
45
+ ]
39
46
  });
40
47
  if (options.coverage && options.coverage.enabled) {
41
48
  log.info('Capturing test coverage enabled');
@@ -110,6 +117,13 @@ export class ViteServer extends EventEmitter {
110
117
  if (payload.type === MESSAGE_TYPES.commandRequestMessage) {
111
118
  return this.#handleCommand(ws, payload.value);
112
119
  }
120
+ if (payload.type === MESSAGE_TYPES.mockRequest) {
121
+ this.#mockHandler.addMock(payload.value);
122
+ return ws.send(JSON.stringify({
123
+ type: MESSAGE_TYPES.mockResponse,
124
+ value: payload.value
125
+ }));
126
+ }
113
127
  throw new Error(`Unknown socket message ${JSON.stringify(payload)}`);
114
128
  }
115
129
  catch (err) {
@@ -10,6 +10,8 @@ export type SocketMessageValue = {
10
10
  [MESSAGE_TYPES.commandResponseMessage]: CommandResponseEvent;
11
11
  [MESSAGE_TYPES.hookTriggerMessage]: HookTriggerEvent;
12
12
  [MESSAGE_TYPES.hookResultMessage]: HookResultEvent;
13
+ [MESSAGE_TYPES.mockRequest]: MockRequestEvent;
14
+ [MESSAGE_TYPES.mockResponse]: MockResponseEvent;
13
15
  };
14
16
  export type SocketMessagePayload<T extends MESSAGE_TYPES> = T extends any ? SocketMessagePayloadType<T> : never;
15
17
  export type SocketMessage = SocketMessagePayload<MESSAGE_TYPES>;
@@ -40,5 +42,13 @@ export interface CommandResponseEvent {
40
42
  result?: unknown;
41
43
  error?: ErrorObject;
42
44
  }
45
+ export interface MockRequestEvent {
46
+ path: string;
47
+ origin: string;
48
+ namedExports: string[];
49
+ }
50
+ export interface MockResponseEvent {
51
+ path: string;
52
+ }
43
53
  export {};
44
54
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/vite/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,UAAU,wBAAwB,CAAC,CAAC,SAAS,aAAa;IACtD,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAA;CAC/B;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC7B,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,YAAY,CAAA;IAC5C,CAAC,aAAa,CAAC,qBAAqB,CAAC,EAAE,mBAAmB,CAAA;IAC1D,CAAC,aAAa,CAAC,sBAAsB,CAAC,EAAE,oBAAoB,CAAA;IAC5D,CAAC,aAAa,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,CAAA;IACpD,CAAC,aAAa,CAAC,iBAAiB,CAAC,EAAE,eAAe,CAAA;CACrD,CAAA;AAED,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,aAAa,IAAI,CAAC,SAAS,GAAG,GACnE,wBAAwB,CAAC,CAAC,CAAC,GAC3B,KAAK,CAAA;AAEX,MAAM,MAAM,aAAa,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAA;AAE/D,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,cAAc,CAAA;IACpB,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;IACjD,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;CACd;AAED,UAAU,2BAA2B;IACjC,EAAE,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,gBAAiB,SAAQ,2BAA2B;IACjE,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,eAAgB,SAAQ,2BAA2B;IAChE,KAAK,CAAC,EAAE,WAAW,CAAA;CACtB;AAED,MAAM,WAAW,mBAAoB,SAAQ,2BAA2B;IACpE,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,OAAO,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,WAAW,CAAA;CACtB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/vite/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,UAAU,wBAAwB,CAAC,CAAC,SAAS,aAAa;IACtD,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAA;CAC/B;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC7B,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,YAAY,CAAA;IAC5C,CAAC,aAAa,CAAC,qBAAqB,CAAC,EAAE,mBAAmB,CAAA;IAC1D,CAAC,aAAa,CAAC,sBAAsB,CAAC,EAAE,oBAAoB,CAAA;IAC5D,CAAC,aAAa,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,CAAA;IACpD,CAAC,aAAa,CAAC,iBAAiB,CAAC,EAAE,eAAe,CAAA;IAClD,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAA;IAC7C,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,iBAAiB,CAAA;CAClD,CAAA;AAED,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,aAAa,IAAI,CAAC,SAAS,GAAG,GACnE,wBAAwB,CAAC,CAAC,CAAC,GAC3B,KAAK,CAAA;AAEX,MAAM,MAAM,aAAa,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAA;AAE/D,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,cAAc,CAAA;IACpB,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;IACjD,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;CACd;AAED,UAAU,2BAA2B;IACjC,EAAE,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,gBAAiB,SAAQ,2BAA2B;IACjE,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,eAAgB,SAAQ,2BAA2B;IAChE,KAAK,CAAC,EAAE,WAAW,CAAA;CACtB;AAED,MAAM,WAAW,mBAAoB,SAAQ,2BAA2B;IACpE,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,OAAO,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,WAAW,CAAA;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAA;CACf"}
@@ -1,6 +1,9 @@
1
+ /// <reference types="node" />
1
2
  import type { Environment, FrameworkPreset } from '../types.js';
2
- export declare function getTemplate(options: WebdriverIO.BrowserRunnerOptions, env: Environment, spec: string): Promise<string>;
3
+ export declare function getTemplate(options: WebdriverIO.BrowserRunnerOptions, env: Environment, spec: string, processEnv?: NodeJS.ProcessEnv): Promise<string>;
3
4
  export declare function userfriendlyImport(preset: FrameworkPreset, pkg?: string): Promise<any>;
4
5
  export declare function normalizeId(id: string, base?: string): string;
6
+ export declare function getFilesFromDirectory(dir: string): Promise<string[]>;
7
+ export declare function getManualMocks(automockDir: string): Promise<[string, string][]>;
5
8
  export declare function getErrorTemplate(filename: string, error: Error): string;
6
9
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/vite/utils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE/D,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,oBAAoB,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,mBAyE1G;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,MAAM,gBAa7E;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAY7D;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,UAU9D"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/vite/utils.ts"],"names":[],"mappings":";AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE/D,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,oBAAoB,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,oBAAc,mBAsFpI;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,MAAM,gBAa7E;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAY7D;AAED,wBAAsB,qBAAqB,CAAE,GAAG,EAAE,MAAM,qBAavD;AAGD,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,+BAgBvD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,UAU9D"}
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import url from 'node:url';
3
3
  import path from 'node:path';
4
4
  import { resolve } from 'import-meta-resolve';
5
- export async function getTemplate(options, env, spec) {
5
+ export async function getTemplate(options, env, spec, processEnv = process.env) {
6
6
  const root = options.rootDir || process.cwd();
7
7
  const rootFileUrl = url.pathToFileURL(root).href;
8
8
  let vueDeps = '';
@@ -30,14 +30,19 @@ export async function getTemplate(options, env, spec) {
30
30
  }
31
31
  }
32
32
  const mochaCSSHref = await resolve('mocha', `${rootFileUrl}/node_modules`).then((p) => path.join(url.fileURLToPath(path.dirname(p)), 'mocha.css'), () => 'https://unpkg.com/mocha@10.0.0/mocha.css');
33
+ const mochaJSSrc = await resolve('mocha', `${rootFileUrl}/node_modules`).then((p) => path.join(url.fileURLToPath(path.dirname(p)), 'mocha.js'), () => 'https://unpkg.com/mocha@10.0.0/mocha.js');
34
+ const sourceMapSupportDir = path.dirname(url.fileURLToPath(await resolve('source-map-support', import.meta.url)));
33
35
  return /* html */ `
34
36
  <!doctype html>
35
37
  <html>
36
38
  <head>
37
39
  <title>WebdriverIO Browser Test</title>
38
40
  <link rel="icon" type="image/x-icon" href="https://webdriver.io/img/favicon.png">
39
- <link rel="stylesheet" href="${mochaCSSHref}">
41
+ <link rel="stylesheet" href="${mochaCSSHref}"><script type="module" src="${mochaJSSrc}"></script>
42
+ <script src="/@fs/${sourceMapSupportDir}/browser-source-map-support.js"></script>
40
43
  <script type="module">
44
+ sourceMapSupport.install()
45
+
41
46
  /**
42
47
  * Inject environment variables
43
48
  */
@@ -50,7 +55,8 @@ export async function getTemplate(options, env, spec) {
50
55
  window.__wdioErrors__ = []
51
56
  addEventListener('error', (ev) => window.__wdioErrors__.push({
52
57
  filename: ev.filename,
53
- message: ev.message
58
+ message: ev.message,
59
+ error: ev.error.stack
54
60
  }))
55
61
  /**
56
62
  * mock process
@@ -65,6 +71,9 @@ export async function getTemplate(options, env, spec) {
65
71
  <body>
66
72
  <div id="mocha"></div>
67
73
  <script async type="module" src="@wdio/browser-runner/setup"></script>
74
+ <script type="module">
75
+ window.process.env = ${JSON.stringify(processEnv)}
76
+ </script>
68
77
  </body>
69
78
  </html>`;
70
79
  }
@@ -92,6 +101,37 @@ export function normalizeId(id, base) {
92
101
  .replace(/[?&]v=\w+/, '?') // remove ?v= query
93
102
  .replace(/\?$/, ''); // remove end query mark
94
103
  }
104
+ export async function getFilesFromDirectory(dir) {
105
+ let files = await fs.readdir(dir);
106
+ files = (await Promise.all(files.map(async (file) => {
107
+ const filePath = path.join(dir, file);
108
+ const stats = await fs.stat(filePath);
109
+ if (stats.isDirectory()) {
110
+ return getFilesFromDirectory(filePath);
111
+ }
112
+ else if (stats.isFile()) {
113
+ return filePath;
114
+ }
115
+ }))).filter(Boolean);
116
+ return files.reduce((all, folderContents) => all.concat(folderContents), []);
117
+ }
118
+ let mockedModulesList;
119
+ export async function getManualMocks(automockDir) {
120
+ /**
121
+ * read available mocks only one time
122
+ */
123
+ if (!mockedModulesList) {
124
+ mockedModulesList = (await getFilesFromDirectory(automockDir))
125
+ /**
126
+ * seperate to module name and actual path
127
+ */
128
+ .map((filePath) => [
129
+ filePath,
130
+ filePath.slice(automockDir.length + 1).slice(0, -path.extname(filePath).length)
131
+ ]);
132
+ }
133
+ return mockedModulesList;
134
+ }
95
135
  export function getErrorTemplate(filename, error) {
96
136
  return /*html*/ `
97
137
  <pre>${error.stack}</pre>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wdio/browser-runner",
3
- "version": "8.3.11",
3
+ "version": "8.5.0",
4
4
  "description": "A WebdriverIO runner to run unit tests tests in the browser.",
5
5
  "author": "Christian Bromann <mail@bromann.dev>",
6
6
  "homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-browser-runner",
@@ -31,14 +31,15 @@
31
31
  "@originjs/vite-plugin-commonjs": "^1.0.3",
32
32
  "@types/istanbul-lib-source-maps": "^4.0.1",
33
33
  "@types/node": "^18.14.0",
34
- "@vitest/spy": "^0.28.5",
35
- "@wdio/globals": "8.3.11",
36
- "@wdio/local-runner": "8.3.11",
34
+ "@vitest/spy": "^0.29.1",
35
+ "@wdio/globals": "8.4.0",
36
+ "@wdio/local-runner": "8.4.0",
37
37
  "@wdio/logger": "8.1.0",
38
- "@wdio/mocha-framework": "8.3.0",
38
+ "@wdio/mocha-framework": "8.4.0",
39
39
  "@wdio/protocols": "8.3.11",
40
- "@wdio/types": "8.3.0",
41
- "@wdio/utils": "8.3.0",
40
+ "@wdio/types": "8.4.0",
41
+ "@wdio/utils": "8.4.0",
42
+ "ast-types": "^0.14.2",
42
43
  "deepmerge-ts": "^4.3.0",
43
44
  "expect-webdriverio": "^4.1.2",
44
45
  "fast-safe-stringify": "^2.1.1",
@@ -49,12 +50,14 @@
49
50
  "istanbul-lib-source-maps": "^4.0.1",
50
51
  "istanbul-reports": "^3.1.5",
51
52
  "modern-node-polyfills": "^0.1.0",
53
+ "recast": "^0.22.0",
52
54
  "serialize-error": "^11.0.0",
55
+ "source-map-support": "^0.5.21",
53
56
  "vite": "^4.1.3",
54
57
  "vite-plugin-istanbul": "^4.0.0",
55
58
  "vite-plugin-top-level-await": "^1.2.4",
56
- "webdriver": "8.3.11",
57
- "webdriverio": "8.3.11",
59
+ "webdriver": "8.4.0",
60
+ "webdriverio": "8.4.0",
58
61
  "ws": "^8.12.1"
59
62
  },
60
63
  "scripts": {
@@ -65,7 +68,7 @@
65
68
  },
66
69
  "devDependencies": {
67
70
  "@types/ws": "^8.5.4",
68
- "@wdio/runner": "8.3.11"
71
+ "@wdio/runner": "8.4.0"
69
72
  },
70
- "gitHead": "d54b501e286d4f2c14b1f0e038d7253bd0c1a9b0"
73
+ "gitHead": "7f6a6146ad7c9ec1d310e675b5e767cccd4fc3d9"
71
74
  }