expect-webdriverio 5.5.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # expect-webdriverio [![Test](https://github.com/webdriverio/expect-webdriverio/actions/workflows/test.yml/badge.svg)](https://github.com/webdriverio/expect-webdriverio/actions/workflows/test.yml)
2
2
 
3
3
 
4
- ###### [API](docs/API.md) | [TypeScript / JS Autocomplete](/docs/Types.md) | [Examples](docs/Examples.md) | [Extending Matchers](/docs/Extend.md)
4
+ ###### [API](docs/API.md) | [TypeScript / JS Autocomplete](docs/Types.md) | [Examples](docs/Examples.md) | [Extending Matchers](docs/CustomMatchers.md)
5
5
 
6
6
  > [WebdriverIO](https://webdriver.io/) Assertion library inspired by [expect](https://www.npmjs.com/package/expect)
7
7
 
@@ -0,0 +1,22 @@
1
+ /// <reference types="./types/expect-webdriverio.d.ts"/>
2
+
3
+ /**
4
+ * Overrides the default wdio `expect` for Jasmine case specifically since the `expect` is now completely asynchronous which is not the case under Jest or standalone.
5
+ */
6
+ declare namespace ExpectWebdriverIO {
7
+ interface Expect extends ExpectWebdriverIO.AsymmetricMatchers, ExpectLibInverse<ExpectWebdriverIO.InverseAsymmetricMatchers>, WdioExpect {
8
+ /**
9
+ * The `expect` function is used every time you want to test a value.
10
+ * You will rarely call `expect` by itself.
11
+ *
12
+ * expect function declaration contains two generics:
13
+ * - T: the type of the actual value, e.g. any type, not just WebdriverIO.Browser or WebdriverIO.Element
14
+ * - R: the type of the return value, e.g. Promise<void> or void
15
+ *
16
+ * Note: The function must stay here in the namespace to overwrite correctly the expect function from the expect library.
17
+ *
18
+ * @param actual The value to apply matchers against.
19
+ */
20
+ <T = unknown>(actual: T): ExpectWebdriverIO.MatchersAndInverse<Promise<void>, T>
21
+ }
22
+ }
package/jasmine.d.ts CHANGED
@@ -1,6 +1,27 @@
1
1
  /// <reference types="./types/expect-webdriverio.d.ts"/>
2
2
 
3
3
  declare namespace jasmine {
4
+
5
+ /**
6
+ * Async matchers for Jasmine to allow the typing of `expectAsync` with WebDriverIO matchers.
7
+ * T is the type of the actual value
8
+ * U is the type of the expected value
9
+ * Both T,U must stay named as they are to override the default `AsyncMatchers` type from Jasmine.
10
+ *
11
+ * We force Matchers to return a `Promise<void>` since Jasmine's `expectAsync` expects a promise in all cases (different from Jest)
12
+ */
4
13
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
- interface AsyncMatchers<T, U> extends ExpectWebdriverIO.Matchers<Promise<void>, T> {}
6
- }
14
+ interface AsyncMatchers<T, U> extends Omit<ExpectWebdriverIO.Matchers<Promise<void>, T>, 'toMatchSnapshot' | 'toMatchInlineSnapshot'> {
15
+ /**
16
+ * snapshot matcher
17
+ * @param label optional snapshot label
18
+ */
19
+ toMatchSnapshot(label?: string): Promise<void>;
20
+ /**
21
+ * inline snapshot matcher
22
+ * @param snapshot snapshot string (autogenerated if not specified)
23
+ * @param label optional snapshot label
24
+ */
25
+ toMatchInlineSnapshot(snapshot?: string, label?: string): Promise<void>;
26
+ }
27
+ }
package/jest.d.ts CHANGED
@@ -1,5 +1,37 @@
1
1
  /// <reference types="./types/expect-webdriverio.d.ts"/>
2
2
 
3
+ /**
4
+ * Augment the Jest namespace to include the matchers from expect-webdriverio.
5
+ * When Jest Library is used, it specifies `expect-webdriverio/jest` for this file in the tsconfig.json's types.
6
+ */
7
+
3
8
  declare namespace jest {
4
- interface Matchers<R, T> extends ExpectWebdriverIO.Matchers<R, T> { }
5
- }
9
+
10
+ interface Matchers<R extends void | Promise<void>, T> extends ExpectWebdriverIO.Matchers<R, T> {
11
+
12
+ /**
13
+ * Below are overloaded Jest's matchers not part of `expect` but of `jest-snapshot`.
14
+ * @see https://github.com/jestjs/jest/blob/73dbef5d2d3195a1e55fb254c54cce70d3036252/packages/jest-snapshot/src/types.ts#L37
15
+ *
16
+ * Note: We need to define them below so that they are correctly typed overloaded.
17
+ * Else even when extending `WdioJestOverloadedMatchers` we have typing errors.
18
+ */
19
+
20
+ /**
21
+ * snapshot matcher
22
+ * @param label optional snapshot label
23
+ */
24
+ toMatchSnapshot(label?: string): T extends WdioPromiseLike ? Promise<void> : void;
25
+
26
+ /**
27
+ * inline snapshot matcher
28
+ * @param snapshot snapshot string (autogenerated if not specified)
29
+ * @param label optional snapshot label
30
+ */
31
+ toMatchInlineSnapshot(snapshot?: string, label?: string): T extends WdioPromiseLike ? Promise<void> : void;
32
+ }
33
+
34
+ interface Expect extends ExpectWebdriverIO.Expect {}
35
+
36
+ interface InverseAsymmetricMatchers extends ExpectWebdriverIO.AsymmetricMatchers {}
37
+ }
@@ -1,12 +1,12 @@
1
1
  import { DEFAULT_OPTIONS } from '../../constants.js';
2
- import { compareText, compareTextWithArray, enhanceError, executeCommand, isAsymmeyricMatcher, waitUntil, wrapExpectedWithArray } from '../../utils.js';
2
+ import { compareText, compareTextWithArray, enhanceError, executeCommand, isAsymmetricMatcher, waitUntil, wrapExpectedWithArray } from '../../utils.js';
3
3
  import { toHaveAttributeAndValue } from './toHaveAttribute.js';
4
4
  async function condition(el, attribute, value, options) {
5
5
  const actualClass = await el.getAttribute(attribute);
6
6
  if (typeof actualClass !== 'string') {
7
7
  return { result: false };
8
8
  }
9
- if (isAsymmeyricMatcher(value)) {
9
+ if (isAsymmetricMatcher(value)) {
10
10
  return compareText(actualClass, value, options);
11
11
  }
12
12
  const classes = actualClass.split(' ');
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { expect } from 'expect';
3
3
  import { stripSnapshotIndentation } from '@vitest/snapshot';
4
- import { SnapshotService } from '../snapshot.js';
4
+ import { SnapshotService } from '../snapshot';
5
5
  function returnSnapshotError(snapshotError) {
6
6
  try {
7
7
  expect(snapshotError.actual).toBe(snapshotError.expected);
package/lib/snapshot.js CHANGED
@@ -1,43 +1,29 @@
1
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
- if (kind === "m") throw new TypeError("Private method is not writable");
3
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
- };
7
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
- };
12
- var _WebdriverIOSnapshotEnvironment_resolveSnapshotPath, _SnapshotService_instances, _SnapshotService_currentFilePath, _SnapshotService_currentTestName, _SnapshotService_options, _SnapshotService_snapshotResults, _SnapshotService_snapshotClient, _SnapshotService_isEqual;
13
1
  import { expect } from '@wdio/globals';
14
2
  import { SnapshotClient } from '@vitest/snapshot';
15
3
  import { NodeSnapshotEnvironment } from '@vitest/snapshot/environment';
16
4
  let service;
17
5
  class WebdriverIOSnapshotEnvironment extends NodeSnapshotEnvironment {
6
+ #resolveSnapshotPath;
18
7
  constructor(resolveSnapshotPath) {
19
8
  super({});
20
- _WebdriverIOSnapshotEnvironment_resolveSnapshotPath.set(this, void 0);
21
- __classPrivateFieldSet(this, _WebdriverIOSnapshotEnvironment_resolveSnapshotPath, resolveSnapshotPath, "f");
9
+ this.#resolveSnapshotPath = resolveSnapshotPath;
22
10
  }
23
11
  async resolvePath(filepath) {
24
- if (__classPrivateFieldGet(this, _WebdriverIOSnapshotEnvironment_resolveSnapshotPath, "f")) {
25
- return __classPrivateFieldGet(this, _WebdriverIOSnapshotEnvironment_resolveSnapshotPath, "f").call(this, filepath, '.snap');
12
+ if (this.#resolveSnapshotPath) {
13
+ return this.#resolveSnapshotPath(filepath, '.snap');
26
14
  }
27
15
  return super.resolvePath(filepath);
28
16
  }
29
17
  }
30
- _WebdriverIOSnapshotEnvironment_resolveSnapshotPath = new WeakMap();
31
18
  export class SnapshotService {
19
+ #currentFilePath;
20
+ #currentTestName;
21
+ #options;
22
+ #snapshotResults = [];
23
+ #snapshotClient = new SnapshotClient({
24
+ isEqual: this.#isEqual.bind(this),
25
+ });
32
26
  constructor(options) {
33
- _SnapshotService_instances.add(this);
34
- _SnapshotService_currentFilePath.set(this, void 0);
35
- _SnapshotService_currentTestName.set(this, void 0);
36
- _SnapshotService_options.set(this, void 0);
37
- _SnapshotService_snapshotResults.set(this, []);
38
- _SnapshotService_snapshotClient.set(this, new SnapshotClient({
39
- isEqual: __classPrivateFieldGet(this, _SnapshotService_instances, "m", _SnapshotService_isEqual).bind(this),
40
- }));
41
27
  const updateSnapshot = (Boolean(process.env.CI) && !options?.updateState)
42
28
  ? 'none'
43
29
  : options?.updateState
@@ -48,45 +34,54 @@ export class SnapshotService {
48
34
  escapeString: false,
49
35
  ...options.snapshotFormat
50
36
  } : undefined;
51
- __classPrivateFieldSet(this, _SnapshotService_options, {
37
+ this.#options = {
52
38
  updateSnapshot,
53
39
  snapshotEnvironment: new WebdriverIOSnapshotEnvironment(options?.resolveSnapshotPath),
54
40
  ...(snapshotFormatConfig && { snapshotFormat: snapshotFormatConfig })
55
- }, "f");
41
+ };
56
42
  }
57
43
  get currentFilePath() {
58
- return __classPrivateFieldGet(this, _SnapshotService_currentFilePath, "f");
44
+ return this.#currentFilePath;
59
45
  }
60
46
  get currentTestName() {
61
- return __classPrivateFieldGet(this, _SnapshotService_currentTestName, "f");
47
+ return this.#currentTestName;
62
48
  }
63
49
  get client() {
64
- return __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f");
50
+ return this.#snapshotClient;
65
51
  }
66
52
  get results() {
67
- return __classPrivateFieldGet(this, _SnapshotService_snapshotResults, "f");
53
+ return this.#snapshotResults;
68
54
  }
69
55
  async beforeTest(test) {
70
- __classPrivateFieldSet(this, _SnapshotService_currentFilePath, test.file, "f");
71
- __classPrivateFieldSet(this, _SnapshotService_currentTestName, `${test.parent} > ${test.title}`, "f");
72
- await __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f").setup(test.file, __classPrivateFieldGet(this, _SnapshotService_options, "f"));
56
+ this.#currentFilePath = test.file;
57
+ this.#currentTestName = `${test.parent} > ${test.title}`;
58
+ await this.#snapshotClient.setup(test.file, this.#options);
73
59
  }
74
60
  async beforeStep(step, scenario) {
75
61
  const file = scenario.uri;
76
62
  const testName = `${scenario.name} > ${step.text}`;
77
- __classPrivateFieldSet(this, _SnapshotService_currentFilePath, file, "f");
78
- __classPrivateFieldSet(this, _SnapshotService_currentTestName, testName, "f");
79
- await __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f").setup(file, __classPrivateFieldGet(this, _SnapshotService_options, "f"));
63
+ this.#currentFilePath = file;
64
+ this.#currentTestName = testName;
65
+ await this.#snapshotClient.setup(file, this.#options);
80
66
  }
81
67
  async after() {
82
- if (!__classPrivateFieldGet(this, _SnapshotService_currentFilePath, "f")) {
68
+ if (!this.#currentFilePath) {
83
69
  return;
84
70
  }
85
- const result = await __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f").finish(__classPrivateFieldGet(this, _SnapshotService_currentFilePath, "f"));
71
+ const result = await this.#snapshotClient.finish(this.#currentFilePath);
86
72
  if (!result) {
87
73
  return;
88
74
  }
89
- __classPrivateFieldGet(this, _SnapshotService_snapshotResults, "f").push(result);
75
+ this.#snapshotResults.push(result);
76
+ }
77
+ #isEqual(received, expected) {
78
+ try {
79
+ expect(received).toBe(expected);
80
+ return true;
81
+ }
82
+ catch {
83
+ return false;
84
+ }
90
85
  }
91
86
  static initiate(options) {
92
87
  if (!service) {
@@ -95,12 +90,3 @@ export class SnapshotService {
95
90
  return service;
96
91
  }
97
92
  }
98
- _SnapshotService_currentFilePath = new WeakMap(), _SnapshotService_currentTestName = new WeakMap(), _SnapshotService_options = new WeakMap(), _SnapshotService_snapshotResults = new WeakMap(), _SnapshotService_snapshotClient = new WeakMap(), _SnapshotService_instances = new WeakSet(), _SnapshotService_isEqual = function _SnapshotService_isEqual(received, expected) {
99
- try {
100
- expect(received).toBe(expected);
101
- return true;
102
- }
103
- catch {
104
- return false;
105
- }
106
- };
package/lib/softAssert.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export class SoftAssertService {
2
- constructor() {
3
- this.failureMap = new Map();
4
- this.currentTest = null;
5
- }
2
+ static instance;
3
+ failureMap = new Map();
4
+ currentTest = null;
5
+ constructor() { }
6
6
  static getInstance() {
7
7
  if (!SoftAssertService.instance) {
8
8
  SoftAssertService.instance = new SoftAssertService();
@@ -1,5 +1,7 @@
1
1
  import { SoftAssertService } from './softAssert.js';
2
2
  export class SoftAssertionService {
3
+ softAssertService;
4
+ options;
3
5
  constructor(serviceOptions) {
4
6
  this.softAssertService = SoftAssertService.getInstance();
5
7
  this.options = {
package/lib/utils.js CHANGED
@@ -7,8 +7,8 @@ import { enhanceError, enhanceErrorBe, numberError } from './util/formatMessage.
7
7
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8
8
  const asymmetricMatcher = typeof Symbol === 'function' && Symbol.for
9
9
  ? Symbol.for('jest.asymmetricMatcher')
10
- : 1267621;
11
- export function isAsymmeyricMatcher(expected) {
10
+ : 0x13_57_a5;
11
+ export function isAsymmetricMatcher(expected) {
12
12
  return (typeof expected === 'object' &&
13
13
  expected &&
14
14
  '$$typeof' in expected &&
@@ -17,7 +17,7 @@ export function isAsymmeyricMatcher(expected) {
17
17
  Boolean(expected.asymmetricMatch));
18
18
  }
19
19
  function isStringContainingMatcher(expected) {
20
- return isAsymmeyricMatcher(expected) && ['StringContaining', 'StringNotContaining'].includes(expected.toString());
20
+ return isAsymmetricMatcher(expected) && ['StringContaining', 'StringNotContaining'].includes(expected.toString());
21
21
  }
22
22
  const waitUntil = async (condition, isNot = false, { wait = DEFAULT_OPTIONS.wait, interval = DEFAULT_OPTIONS.interval } = {}) => {
23
23
  if (wait === 0) {
@@ -108,7 +108,7 @@ export const compareText = (actual, expected, { ignoreCase = false, trim = true,
108
108
  : expect.not.stringContaining(expected.sample?.toString().toLowerCase()));
109
109
  }
110
110
  }
111
- if (isAsymmeyricMatcher(expected)) {
111
+ if (isAsymmetricMatcher(expected)) {
112
112
  const result = expected.asymmetricMatch(actual);
113
113
  return {
114
114
  value: actual,
@@ -181,7 +181,7 @@ export const compareTextWithArray = (actual, expectedArray, { ignoreCase = false
181
181
  if (expected instanceof RegExp) {
182
182
  return !!actual.match(expected);
183
183
  }
184
- if (isAsymmeyricMatcher(expected)) {
184
+ if (isAsymmetricMatcher(expected)) {
185
185
  return expected.asymmetricMatch(actual);
186
186
  }
187
187
  if (containing) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expect-webdriverio",
3
- "version": "5.5.0",
3
+ "version": "5.6.0",
4
4
  "author": "Mykola Grybyk <mykola.grybyk@gmail.com>",
5
5
  "description": "WebdriverIO Assertion Library",
6
6
  "license": "MIT",
@@ -23,7 +23,7 @@
23
23
  "exports": {
24
24
  ".": [
25
25
  {
26
- "types": "./types/standalone.d.ts",
26
+ "types": "./types/expect-webdriverio.d.ts",
27
27
  "import": "./lib/index.js"
28
28
  },
29
29
  "./lib/index.js"
@@ -33,54 +33,64 @@
33
33
  "types": "./jest.d.ts"
34
34
  }
35
35
  ],
36
- "./types": "./types/jest-global.d.ts"
36
+ "./jasmine": [
37
+ {
38
+ "types": "./jasmine.d.ts"
39
+ }
40
+ ],
41
+ "./types": "./types/expect-global.d.ts",
42
+ "./expect-global": "./types/expect-global.d.ts"
37
43
  },
38
- "types": "./types/standalone.d.ts",
44
+ "types": "./types/expect-webdriverio.d.ts",
39
45
  "typeScriptVersion": "3.8.3",
40
46
  "engines": {
41
- "node": ">=18 || >=20 || >=22"
47
+ "node": ">=20"
42
48
  },
43
49
  "scripts": {
44
50
  "build": "run-s clean compile",
45
51
  "clean": "run-p clean:*",
46
52
  "clean:build": "rimraf ./lib",
47
- "clean:tests": "rimraf test-types/**/node_modules && rimraf test-types/**/dist",
48
53
  "compile": "tsc --build tsconfig.build.json",
49
- "tsc:root-types": "tsc jasmine.d.ts jest.d.ts",
54
+ "tsc:root-types": "node types-checks-filter-out-node_modules.js",
50
55
  "test": "run-s test:*",
51
56
  "test:tsc": "tsc --project tsconfig.json --noEmit",
52
57
  "test:lint": "eslint .",
53
58
  "test:unit": "vitest --run",
54
- "test:types": "node test-types/copy && npm run ts && npm run clean:tests && npm run tsc:root-types",
55
- "ts": "run-s ts:*",
56
- "ts:default": "cd test-types/default && tsc -p ./tsconfig.json --incremental",
57
- "ts:jest": "cd test-types/jest && tsc -p ./tsconfig.json --incremental",
58
- "ts:jasmine": "cd test-types/jasmine && tsc -p ./tsconfig.json --incremental",
59
+ "test:types": "npm run ts && npm run tsc:root-types",
60
+ "ts": "run-s ts:* ts:*:*",
61
+ "ts:jest:@jest/global": "cd test-types/jest-@jest_global && tsc --project ./tsconfig.json",
62
+ "ts:jest:@types-jest": "cd test-types/jest-@types_jest && tsc --project ./tsconfig.json",
63
+ "ts:mocha": "cd test-types/mocha && tsc --project ./tsconfig.json",
64
+ "ts:jasmine": "cd test-types/jasmine && tsc --project ./tsconfig.json",
65
+ "ts:jasmine-async": "cd test-types/jasmine-async && tsc --project ./tsconfig.json",
59
66
  "watch": "npm run compile -- --watch",
60
67
  "prepare": "husky install"
61
68
  },
62
69
  "dependencies": {
63
- "@vitest/snapshot": "^4.0.12",
70
+ "@vitest/snapshot": "^4.0.16",
64
71
  "deep-eql": "^5.0.2",
65
72
  "expect": "^30.2.0",
66
73
  "jest-matcher-utils": "^30.2.0"
67
74
  },
68
75
  "devDependencies": {
69
76
  "@types/debug": "^4.1.12",
77
+ "@types/jasmine": "^5.1.8",
70
78
  "@types/jest": "^30.0.0",
71
79
  "@types/node": "^24.10.1",
72
- "@vitest/coverage-v8": "^4.0.12",
80
+ "@types/mocha": "^10.0.10",
81
+ "@jest/globals": "^30.2.0",
82
+ "@vitest/coverage-v8": "^4.0.16",
73
83
  "@wdio/eslint": "^0.1.2",
74
84
  "@wdio/types": "^9.20.0",
75
- "eslint": "^9.39.1",
85
+ "eslint": "^9.39.2",
76
86
  "husky": "^9.1.7",
77
87
  "npm-run-all2": "^8.0.4",
78
- "release-it": "^19.0.6",
88
+ "release-it": "^19.1.0",
79
89
  "rimraf": "^6.1.2",
80
90
  "shelljs": "^0.10.0",
81
91
  "typescript": "^5.9.3",
82
- "vitest": "^4.0.12",
83
- "webdriverio": "^9.20.1"
92
+ "vitest": "^4.0.16",
93
+ "webdriverio": "^9.21.0"
84
94
  },
85
95
  "peerDependencies": {
86
96
  "@wdio/globals": "^9.0.0",
@@ -0,0 +1,15 @@
1
+ /// <reference types="./expect-webdriverio.d.ts"/>
2
+
3
+ /**
4
+ * Global declaration file for WebdriverIO's Expect library to force the expect.
5
+ * Required when used in standalone mode (mocha) or to override the one of Jasmine
6
+ */
7
+
8
+ //// @ts-expect-error: IDE might flags this one but just does be concerned by it. This way the `tsc:root-types` can pass!
9
+ declare const expect: ExpectWebdriverIO.Expect
10
+
11
+ declare namespace NodeJS {
12
+ interface Global {
13
+ expect: ExpectWebdriverIO.Expect
14
+ }
15
+ }