@wdio/browser-runner 8.3.11 → 8.4.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"}
@@ -2,4 +2,7 @@
2
2
  * re-export mock module
3
3
  */
4
4
  export * from '@vitest/spy';
5
+ type MockFactoryWithHelper = (importOriginal: <T = unknown>() => Promise<T>) => any;
6
+ export declare function mock(path: string, factory?: MockFactoryWithHelper): Promise<unknown>;
7
+ export declare function unmock(moduleName: string): void;
5
8
  //# 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":"AAGA;;GAEG;AACH,cAAc,aAAa,CAAA;AAE3B,KAAK,qBAAqB,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,GAAG,OAAO,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;AAapF,wBAAsB,IAAI,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,oBA4BxE;AAGD,wBAAgB,MAAM,CAAC,UAAU,EAAE,MAAM,QAExC"}
@@ -1,4 +1,57 @@
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
+ });
@@ -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/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;
@@ -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"}
@@ -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,CAwM/D"}
@@ -0,0 +1,192 @@
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 mockedMod = (
29
+ // mocked file
30
+ mockHandler.mocks.get(os.platform() === 'win32' ? `/${id}` : id) ||
31
+ // mocked dependency
32
+ mockHandler.mocks.get(path.basename(id, path.extname(id))));
33
+ if (mockedMod) {
34
+ const newCode = mockedMod.namedExports.map((ne) => {
35
+ if (ne === 'default') {
36
+ return /*js*/ `export default window.__wdioMockFactories__['${mockedMod.path}'].default;`;
37
+ }
38
+ return /*js*/ `export const ${ne} = window.__wdioMockFactories__['${mockedMod.path}']['${ne}'];`;
39
+ });
40
+ if (!mockedMod.namedExports.includes('default')) {
41
+ newCode.push(/*js*/ `export default window.__wdioMockFactories__['${mockedMod.path}'];`);
42
+ }
43
+ log.debug(`Resolve mock for module "${mockedMod.path}"`);
44
+ return newCode.join('\n');
45
+ }
46
+ }
47
+ }, {
48
+ name: 'wdio:mockHoisting',
49
+ enforce: 'post',
50
+ transform(code, id) {
51
+ /**
52
+ * only transform when spec file is transformed
53
+ */
54
+ if (id !== spec) {
55
+ return { code };
56
+ }
57
+ const ast = parse(code, {
58
+ parser: typescriptParser,
59
+ sourceFileName: id,
60
+ sourceRoot: path.dirname(id)
61
+ });
62
+ let mockFunctionName;
63
+ let unmockFunctionName;
64
+ const mockCalls = [];
65
+ /**
66
+ * rewrite import statements into variable declarations, e.g. from
67
+ *
68
+ * import React, { RC } from 'react'
69
+ *
70
+ * to
71
+ *
72
+ * var { default: React, RC: RC } = await import("react")
73
+ *
74
+ * so we can hoist the mock call
75
+ */
76
+ visit(ast, {
77
+ visitImportDeclaration: function (path) {
78
+ const dec = path.value;
79
+ const source = dec.source.value;
80
+ /**
81
+ * get name of mock function variable
82
+ */
83
+ if (source === '@wdio/browser-runner') {
84
+ const mockSpecifier = dec.specifiers
85
+ .filter((s) => s.type === types.namedTypes.ImportSpecifier.toString())
86
+ .find((s) => s.imported.name === 'mock');
87
+ if (mockSpecifier && mockSpecifier.local) {
88
+ mockFunctionName = mockSpecifier.local.name;
89
+ }
90
+ const unmockSpecifier = dec.specifiers
91
+ .filter((s) => s.type === types.namedTypes.ImportSpecifier.toString())
92
+ .find((s) => s.imported.name === 'unmock');
93
+ if (unmockSpecifier && unmockSpecifier.local) {
94
+ unmockFunctionName = unmockSpecifier.local.name;
95
+ }
96
+ mockCalls.push(dec);
97
+ path.prune();
98
+ return this.traverse(path);
99
+ }
100
+ const newNode = b.variableDeclaration('const', [
101
+ b.variableDeclarator((dec.specifiers?.length === 1 && dec.specifiers[0].type === types.namedTypes.ImportNamespaceSpecifier.toString())
102
+ /**
103
+ * we deal with a ImportNamespaceSpecifier, e.g.:
104
+ * import * as foo from 'bar'
105
+ */
106
+ ? dec.specifiers[0].local
107
+ /**
108
+ * we deal with default or named import, e.g.
109
+ * import foo from 'bar'
110
+ * or
111
+ * import { foo } from 'bar'
112
+ */
113
+ : b.objectPattern(dec.specifiers.map((s) => {
114
+ if (s.type === types.namedTypes.ImportDefaultSpecifier.toString()) {
115
+ return b.property('init', b.identifier('default'), b.identifier(s.local.name));
116
+ }
117
+ return b.property('init', b.identifier(s.imported.name), b.identifier(s.local.name));
118
+ })), b.awaitExpression(b.importExpression(b.literal(source))))
119
+ ]);
120
+ path.replace(newNode);
121
+ this.traverse(path);
122
+ },
123
+ visitExpressionStatement: function (path) {
124
+ const exp = path.value;
125
+ if (exp.expression.type !== types.namedTypes.CallExpression.toString()) {
126
+ return this.traverse(path);
127
+ }
128
+ const callExp = exp.expression;
129
+ const isUnmockCall = unmockFunctionName && callExp.callee.name === unmockFunctionName;
130
+ const isMockCall = mockFunctionName && callExp.callee.name === mockFunctionName;
131
+ if (!isMockCall && !isUnmockCall) {
132
+ return this.traverse(path);
133
+ }
134
+ /**
135
+ * hoist unmock calls
136
+ */
137
+ if (isUnmockCall && callExp.arguments[0] && typeof callExp.arguments[0].value === 'string') {
138
+ mockHandler.unmock(callExp.arguments[0].value);
139
+ }
140
+ else if (isMockCall) {
141
+ /**
142
+ * if only one mock argument is set, we take the fixture from the automock directory
143
+ */
144
+ const mockCall = exp.expression;
145
+ if (mockCall.arguments.length === 1) {
146
+ /**
147
+ * enable manual mock
148
+ */
149
+ mockHandler.manualMocks.push(mockCall.arguments[0].value);
150
+ }
151
+ else {
152
+ /**
153
+ * hoist mock calls
154
+ */
155
+ mockCalls.push(exp);
156
+ }
157
+ }
158
+ path.prune();
159
+ this.traverse(path);
160
+ }
161
+ });
162
+ ast.program.body.unshift(...mockCalls.map((mc) => {
163
+ const exp = mc;
164
+ if (exp.expression && exp.expression.type === types.namedTypes.CallExpression.toString()) {
165
+ return b.expressionStatement(b.awaitExpression(exp.expression));
166
+ }
167
+ return mc;
168
+ }));
169
+ const newCode = print(ast, {
170
+ sourceMapName: id
171
+ });
172
+ return newCode;
173
+ },
174
+ configureServer(server) {
175
+ return () => {
176
+ server.middlewares.use('/', async (req, res, next) => {
177
+ if (!req.url) {
178
+ return next();
179
+ }
180
+ const urlParsed = url.parse(req.url);
181
+ const urlParamString = new URLSearchParams(urlParsed.query || '');
182
+ const specParam = urlParamString.get('spec');
183
+ if (specParam) {
184
+ mockHandler.resetMocks();
185
+ spec = os.platform() === 'win32' ? specParam.slice(1) : specParam;
186
+ }
187
+ return next();
188
+ });
189
+ };
190
+ }
191
+ }];
192
+ }
@@ -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.4.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": "5d003ce35250c9d9dcecc9fd13a97257d6b349fe"
71
74
  }