@wdio/webdriver-mock-service 10.0.0-alpha.18 → 10.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.
@@ -1,20 +1,45 @@
1
- import nock from 'nock';
1
+ import { type Interceptable } from 'undici';
2
2
  export interface CommandMock {
3
- [commandName: string]: (...args: unknown[]) => nock.Interceptor;
3
+ [commandName: string]: (...args: unknown[]) => MockInterceptor;
4
+ }
5
+ export interface MockInterceptor {
6
+ times(n: number): MockInterceptor;
7
+ once(): MockInterceptor;
8
+ twice(): MockInterceptor;
9
+ reply(statusCode: number, data?: unknown | (() => unknown)): void;
10
+ }
11
+ declare class UndiciMockInterceptor implements MockInterceptor {
12
+ #private;
13
+ private readonly pool;
14
+ private readonly opts;
15
+ constructor(pool: Interceptable, opts: {
16
+ path: (path: string) => boolean;
17
+ method: string;
18
+ body?: (body: Record<string, unknown>) => boolean;
19
+ });
20
+ times(n: number): this;
21
+ once(): this;
22
+ twice(): this;
23
+ reply(statusCode: number, data?: unknown | (() => unknown)): void;
4
24
  }
5
25
  export default class WebDriverMock {
26
+ #private;
6
27
  path: string;
7
28
  command: CommandMock;
8
- scope: nock.Scope;
9
29
  constructor(host?: string, port?: number, path?: string);
10
30
  /**
11
31
  * To allow random session IDs in url paths we have to set up a custom
12
32
  * matcher that strips out the sessionID part from the expected url
13
33
  * and actual url and replaces it with a constant session id
14
34
  * @param {String} expectedPath path to match against
15
- * @returns {Function} to be called by Nock to match actual path
35
+ * @returns {Function} to be called by the mock agent to match actual path
16
36
  */
17
37
  static pathMatcher(expectedPath: string): (path: string) => boolean;
18
- get(_obj: unknown, commandName: string): (...args: unknown[]) => nock.Interceptor;
38
+ get(_obj: unknown, commandName: string): (...args: unknown[]) => UndiciMockInterceptor;
39
+ /**
40
+ * Clear all interceptors and recreate the shared MockAgent.
41
+ */
42
+ static reset(): void;
19
43
  }
44
+ export {};
20
45
  //# sourceMappingURL=WebDriverMock.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WebDriverMock.d.ts","sourceRoot":"","sources":["../src/WebDriverMock.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AAmBvB,MAAM,WAAW,WAAW;IACxB,CAAC,WAAW,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC,WAAW,CAAA;CAClE;AAUD,MAAM,CAAC,OAAO,OAAO,aAAa;IAGsC,IAAI,EAAE,MAAM;IAFhF,OAAO,EAAE,WAAW,CAAA;IACpB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAA;gBACL,IAAI,GAAE,MAAoB,EAAE,IAAI,GAAE,MAAa,EAAS,IAAI,GAAE,MAAY;IAOtF;;;;;;OAMG;IACH,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,CAAC,IAAI,EAAC,MAAM,KAAI,OAAO;IAqBjE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,IAI1B,GAAG,MAAM,OAAO,EAAE;CAoCjC"}
1
+ {"version":3,"file":"WebDriverMock.d.ts","sourceRoot":"","sources":["../src/WebDriverMock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuD,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAA;AAkBhG,MAAM,WAAW,WAAW;IACxB,CAAC,WAAW,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,eAAe,CAAA;CACjE;AAED,MAAM,WAAW,eAAe;IAC5B,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,eAAe,CAAA;IACjC,IAAI,IAAI,eAAe,CAAA;IACvB,KAAK,IAAI,eAAe,CAAA;IACxB,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,IAAI,CAAA;CACpE;AA8BD,cAAM,qBAAsB,YAAW,eAAe;;IAI9C,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE;QACnB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;QAC/B,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAA;KACpD;IAGL,KAAK,CAAC,CAAC,EAAE,MAAM;IAKf,IAAI;IAIJ,KAAK;IAIL,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC;CA4B7D;AAED,MAAM,CAAC,OAAO,OAAO,aAAa;;IAKsC,IAAI,EAAE,MAAM;IAJhF,OAAO,EAAE,WAAW,CAAA;gBAIR,IAAI,GAAE,MAAoB,EAAE,IAAI,GAAE,MAAa,EAAS,IAAI,GAAE,MAAY;IAOtF;;;;;;OAMG;IACH,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO;IAqBnE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,IAG1B,GAAG,MAAM,OAAO,EAAE;IAwC9B;;OAEG;IACH,MAAM,CAAC,KAAK;CAYf"}
package/build/index.d.ts CHANGED
@@ -18,13 +18,17 @@ export default class WebdriverMockService implements Services.ServiceInstance {
18
18
  waitForExistScenario(): void;
19
19
  asyncIterationScenario(): string[];
20
20
  parentNextPreviousElementChaining(): void;
21
- multiremoteFetch(): void;
21
+ multiRemoteFetch(): void;
22
22
  customCommandScenario(times?: number): void;
23
23
  customSelectorScenario(): void;
24
24
  waitForDisplayedScenario(): void;
25
25
  cucumberScenario(): void;
26
26
  executeMemLeakScenario(executeCalls: number): () => void;
27
27
  requestRetryScenario(): void;
28
+ /**
29
+ * Reset all mock interceptors and re-register the default command stubs.
30
+ * Kept as `nockReset` for compatibility with smoke tests that call it by name.
31
+ */
28
32
  nockReset(): void;
29
33
  }
30
34
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,aAAa,MAAM,oBAAoB,CAAA;AAW9C,MAAM,CAAC,OAAO,OAAO,oBAAqB,YAAW,QAAQ,CAAC,eAAe;IACzE,OAAO,CAAC,QAAQ,CAAC,CAAsD;IACvE,OAAO,CAAC,KAAK,CAAsB;;IAMnC,IAAI;IAiBJ,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,GAAG,IAAI;IAK/C,MAAM,CACF,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,kBAAkB;IA4BjE,aAAa;IAQb,kBAAkB;IASlB,qBAAqB;IAKrB,sBAAsB;IAYtB,wBAAwB;IAUxB,6BAA6B;IAU7B,2BAA2B;IA+B3B,sBAAsB;IAgBtB,oBAAoB;IAQpB,sBAAsB;IAStB,iCAAiC;IAQjC,gBAAgB;IAShB,qBAAqB,CAAC,KAAK,SAAI;IAa/B,sBAAsB;IAUtB,wBAAwB;IASxB,gBAAgB;IAShB,sBAAsB,CAAC,YAAY,EAAE,MAAM;IAa3C,oBAAoB;IAOpB,SAAS;CAKZ;AAED;;GAEG;AACH,OAAO,EAAE,aAAa,EAAE,CAAA;AAExB,eAAO,MAAM,QAAQ;;0BACC,WAAW,CAAC,MAAM;;CAQvC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,aAAa,MAAM,oBAAoB,CAAA;AAW9C,MAAM,CAAC,OAAO,OAAO,oBAAqB,YAAW,QAAQ,CAAC,eAAe;IACzE,OAAO,CAAC,QAAQ,CAAC,CAAsD;IACvE,OAAO,CAAC,KAAK,CAAsB;;IAMnC,IAAI;IAiBJ,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,GAAG,IAAI;IAK/C,MAAM,CACF,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,kBAAkB;IA4BjE,aAAa;IAQb,kBAAkB;IASlB,qBAAqB;IAKrB,sBAAsB;IAYtB,wBAAwB;IAUxB,6BAA6B;IAU7B,2BAA2B;IA+B3B,sBAAsB;IAgBtB,oBAAoB;IAQpB,sBAAsB;IAStB,iCAAiC;IAQjC,gBAAgB;IAShB,qBAAqB,CAAC,KAAK,SAAI;IAa/B,sBAAsB;IAUtB,wBAAwB;IASxB,gBAAgB;IAShB,sBAAsB,CAAC,YAAY,EAAE,MAAM;IAY3C,oBAAoB;IAOpB;;;OAGG;IACH,SAAS;CAKZ;AAED;;GAEG;AACH,OAAO,EAAE,aAAa,EAAE,CAAA;AAExB,eAAO,MAAM,QAAQ;;0BACC,WAAW,CAAC,MAAM;;CAavC,CAAA"}
package/build/index.js CHANGED
@@ -1,12 +1,7 @@
1
- // src/index.ts
2
- import nock2 from "nock";
3
- import { v4 as uuidv4 } from "uuid";
4
-
5
1
  // src/WebDriverMock.ts
6
- import nock from "nock";
2
+ import { MockAgent, setGlobalDispatcher, getGlobalDispatcher } from "undici";
7
3
  import {
8
4
  WebDriverProtocol,
9
- MJsonWProtocol,
10
5
  AppiumProtocol,
11
6
  ChromiumProtocol,
12
7
  SauceLabsProtocol,
@@ -16,7 +11,6 @@ var REGEXP_SESSION_ID = /\/[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0
16
11
  var SESSION_ID = "XXX";
17
12
  var protocols = [
18
13
  WebDriverProtocol,
19
- MJsonWProtocol,
20
14
  AppiumProtocol,
21
15
  ChromiumProtocol,
22
16
  SauceLabsProtocol,
@@ -30,23 +24,79 @@ for (const protocol of protocols) {
30
24
  }
31
25
  }
32
26
  }
27
+ var sharedAgent;
28
+ function configureNetConnect(agent) {
29
+ agent.disableNetConnect();
30
+ agent.enableNetConnect((host) => !String(host).endsWith(":4444"));
31
+ }
32
+ function getOrCreateAgent() {
33
+ if (!sharedAgent) {
34
+ sharedAgent = new MockAgent();
35
+ configureNetConnect(sharedAgent);
36
+ setGlobalDispatcher(sharedAgent);
37
+ }
38
+ return sharedAgent;
39
+ }
40
+ var UndiciMockInterceptor = class {
41
+ constructor(pool, opts) {
42
+ this.pool = pool;
43
+ this.opts = opts;
44
+ }
45
+ pool;
46
+ opts;
47
+ #times = null;
48
+ times(n) {
49
+ this.#times = n;
50
+ return this;
51
+ }
52
+ once() {
53
+ return this.times(1);
54
+ }
55
+ twice() {
56
+ return this.times(2);
57
+ }
58
+ reply(statusCode, data) {
59
+ const interceptOpts = {
60
+ path: this.opts.path,
61
+ method: this.opts.method
62
+ };
63
+ if (this.opts.body) {
64
+ interceptOpts.body = (body) => {
65
+ try {
66
+ const parsed = typeof body === "string" ? JSON.parse(body) : body;
67
+ return this.opts.body(parsed);
68
+ } catch {
69
+ return false;
70
+ }
71
+ };
72
+ }
73
+ const dataOrFn = typeof data === "function" ? () => data() : data;
74
+ const mock = this.pool.intercept(interceptOpts).reply(statusCode, dataOrFn);
75
+ if (this.#times === Infinity) {
76
+ mock.persist();
77
+ } else if (this.#times !== null && this.#times > 0) {
78
+ mock.times(this.#times);
79
+ }
80
+ }
81
+ };
33
82
  var WebDriverMock = class _WebDriverMock {
34
83
  constructor(host = "localhost", port = 4444, path = "/") {
35
84
  this.path = path;
36
- this.scope = nock(`http://${host}:${port}`, {
37
- encodedQueryParams: true
38
- });
85
+ this.#origin = `http://${host}:${port}`;
86
+ const agent = getOrCreateAgent();
87
+ this.#pool = agent.get(this.#origin);
39
88
  this.command = new Proxy({}, { get: this.get.bind(this) });
40
89
  }
41
90
  path;
42
91
  command;
43
- scope;
92
+ #origin;
93
+ #pool;
44
94
  /**
45
95
  * To allow random session IDs in url paths we have to set up a custom
46
96
  * matcher that strips out the sessionID part from the expected url
47
97
  * and actual url and replaces it with a constant session id
48
98
  * @param {String} expectedPath path to match against
49
- * @returns {Function} to be called by Nock to match actual path
99
+ * @returns {Function} to be called by the mock agent to match actual path
50
100
  */
51
101
  static pathMatcher(expectedPath) {
52
102
  return (path) => {
@@ -54,9 +104,9 @@ var WebDriverMock = class _WebDriverMock {
54
104
  if (!sessionId) {
55
105
  return path === expectedPath;
56
106
  }
57
- expectedPath = expectedPath.replace(":sessionId", SESSION_ID);
58
- path = path.replace(`${sessionId[0].slice(1)}`, SESSION_ID);
59
- return path === expectedPath;
107
+ const normalizedExpected = expectedPath.replace(":sessionId", SESSION_ID);
108
+ const normalizedPath = path.replace(`${sessionId[0].slice(1)}`, SESSION_ID);
109
+ return normalizedPath === normalizedExpected;
60
110
  };
61
111
  }
62
112
  get(_obj, commandName) {
@@ -66,24 +116,42 @@ var WebDriverMock = class _WebDriverMock {
66
116
  for (const [i, param] of Object.entries(commandData.variables || [])) {
67
117
  urlPath = urlPath.replace(`:${param.name}`, args[parseInt(i)]);
68
118
  }
119
+ const path = _WebDriverMock.pathMatcher(urlPath);
69
120
  if (method === "POST") {
70
- const reqMethod2 = method.toLowerCase();
71
- return this.scope[reqMethod2](_WebDriverMock.pathMatcher(urlPath), (body) => {
72
- for (const param of commandData.parameters) {
73
- if (!body[param.name]) {
74
- return false;
75
- }
76
- if (param.required && typeof body[param.name] === "undefined") {
77
- return false;
121
+ return new UndiciMockInterceptor(this.#pool, {
122
+ path,
123
+ method,
124
+ body: (body) => {
125
+ for (const param of commandData.parameters) {
126
+ if (!body[param.name]) {
127
+ return false;
128
+ }
129
+ if (param.required && typeof body[param.name] === "undefined") {
130
+ return false;
131
+ }
78
132
  }
133
+ return true;
79
134
  }
80
- return true;
81
135
  });
82
136
  }
83
- const reqMethod = method.toLowerCase();
84
- return this.scope[reqMethod](_WebDriverMock.pathMatcher(urlPath));
137
+ return new UndiciMockInterceptor(this.#pool, { path, method });
85
138
  };
86
139
  }
140
+ /**
141
+ * Clear all interceptors and recreate the shared MockAgent.
142
+ */
143
+ static reset() {
144
+ const previous = sharedAgent;
145
+ sharedAgent = new MockAgent();
146
+ configureNetConnect(sharedAgent);
147
+ setGlobalDispatcher(sharedAgent);
148
+ if (previous) {
149
+ void previous.close();
150
+ }
151
+ if (getGlobalDispatcher().constructor.name !== "MockAgent") {
152
+ setGlobalDispatcher(sharedAgent);
153
+ }
154
+ }
87
155
  };
88
156
 
89
157
  // src/constants.ts
@@ -138,7 +206,7 @@ var WebdriverMockService = class {
138
206
  init() {
139
207
  this._mock.command.status().times(Infinity).reply(200, { value: {} });
140
208
  this._mock.command.newSession().times(Infinity).reply(200, () => {
141
- newSession.value.sessionId = uuidv4();
209
+ newSession.value.sessionId = crypto.randomUUID();
142
210
  return newSession;
143
211
  });
144
212
  this._mock.command.deleteSession().times(2).reply(200, deleteSession);
@@ -167,7 +235,7 @@ var WebdriverMockService = class {
167
235
  this._browser.addCommand("clickScenario", this.clickScenario.bind(this));
168
236
  this._browser.addCommand("isExistingScenario", this.isExistingScenario.bind(this));
169
237
  this._browser.addCommand("isNotExistingScenario", this.isNotExistingScenario.bind(this));
170
- this._browser.addCommand("multiremoteFetch", this.multiremoteFetch.bind(this));
238
+ this._browser.addCommand("multiRemoteFetch", this.multiRemoteFetch.bind(this));
171
239
  this._browser.addCommand("asyncIterationScenario", this.asyncIterationScenario.bind(this));
172
240
  this._browser.addCommand("parentElementChaining", this.parentNextPreviousElementChaining.bind(this));
173
241
  this._browser.addCommand("refetchElementScenario", this.refetchElementScenario.bind(this));
@@ -270,7 +338,7 @@ var WebdriverMockService = class {
270
338
  this._mock.command.executeScript().reply(200, { value: elemParentResponse });
271
339
  this._mock.command.getElementText(ELEMENT_REFETCHED).reply(200, { value: "some element text" });
272
340
  }
273
- multiremoteFetch() {
341
+ multiRemoteFetch() {
274
342
  const elemResponse = { [ELEM_PROP]: ELEMENT_ID };
275
343
  const elem2Response = { [ELEM_PROP]: ELEMENT_REFETCHED };
276
344
  this._mock.command.findElement().twice().reply(200, { value: elemResponse });
@@ -321,9 +389,13 @@ var WebdriverMockService = class {
321
389
  this._mock.command.navigateTo().times(3).reply(500, {});
322
390
  this._mock.command.navigateTo().reply(200, { value: null });
323
391
  }
392
+ /**
393
+ * Reset all mock interceptors and re-register the default command stubs.
394
+ * Kept as `nockReset` for compatibility with smoke tests that call it by name.
395
+ */
324
396
  nockReset() {
325
- nock2.cleanAll();
326
- nock2.abortPendingRequests();
397
+ WebDriverMock.reset();
398
+ this._mock = new WebDriverMock();
327
399
  this.init();
328
400
  }
329
401
  };
@@ -333,7 +405,12 @@ var launcher = class WebdriverMockLauncher {
333
405
  config.port = 4444;
334
406
  config.runnerEnv = {
335
407
  ...config.runnerEnv || {},
336
- WDIO_USE_NATIVE_FETCH: "true"
408
+ /**
409
+ * Opt workers into undici + MockAgent instead of native fetch + nock.
410
+ * `WDIO_UNIT_TESTS` alone still selects the web fetch implementation for
411
+ * Vitest unit tests that stub `globalThis.fetch`.
412
+ */
413
+ WDIO_USE_UNDICI_MOCK: "true"
337
414
  };
338
415
  }
339
416
  };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@wdio/webdriver-mock-service",
3
- "version": "10.0.0-alpha.18+dbf0f4971",
3
+ "version": "10.0.0",
4
4
  "description": "A WebdriverIO service to stub all endpoints for internal testing purposes.",
5
5
  "author": "Christian Bromann <mail@bromann.dev>",
6
6
  "homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-webdriver-mock-service",
7
7
  "license": "MIT",
8
8
  "engines": {
9
- "node": ">=18.20.0"
9
+ "node": ">=22.19.0"
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
@@ -25,20 +25,18 @@
25
25
  "import": "./build/index.js"
26
26
  }
27
27
  },
28
- "typeScriptVersion": "3.8.3",
28
+ "typeScriptVersion": "6.0.3",
29
29
  "dependencies": {
30
- "@types/uuid": "^10.0.0",
31
- "@wdio/protocols": "9.31.5",
32
- "@wdio/types": "9.32.0",
33
- "nock": "14.0.15",
34
- "uuid": "^11.1.0",
35
- "webdriverio": "9.32.0"
30
+ "@wdio/protocols": "10.0.0",
31
+ "@wdio/types": "10.0.0",
32
+ "undici": "^8.11.2",
33
+ "webdriverio": "10.0.0"
36
34
  },
37
35
  "devDependencies": {
38
- "@wdio/globals": "10.0.0-alpha.18+dbf0f4971"
36
+ "@wdio/globals": "10.0.0"
39
37
  },
40
38
  "publishConfig": {
41
39
  "access": "public"
42
40
  },
43
- "gitHead": "dbf0f497130676040ce4212d9cf6459adfe16c42"
41
+ "gitHead": "38bd5ff4dccfaadccce4bd678286f00594623da2"
44
42
  }