expect-webdriverio 6.1.0 → 8.0.0-alpha.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.
Files changed (56) hide show
  1. package/README.md +22 -7
  2. package/jasmine-wdio-expect-async.d.ts +1 -1
  3. package/jest.d.ts +4 -2
  4. package/lib/api/index.js +2 -0
  5. package/lib/constants.js +0 -6
  6. package/lib/index.js +3 -13
  7. package/lib/jasmineUtils.js +2 -2
  8. package/lib/matchers/asymmetrics/asymmetricsUtils.js +10 -0
  9. package/lib/matchers/asymmetrics/multiRemote.js +36 -0
  10. package/lib/matchers/asymmetrics/oneOf.js +2 -2
  11. package/lib/matchers/browser/toHaveClipboardText.js +24 -10
  12. package/lib/matchers/browser/toHaveLocalStorageItem.js +23 -22
  13. package/lib/matchers/browser/toHaveTitle.js +16 -6
  14. package/lib/matchers/browser/toHaveUrl.js +16 -6
  15. package/lib/matchers/element/toBeClickable.js +3 -2
  16. package/lib/matchers/element/toBeDisabled.js +3 -2
  17. package/lib/matchers/element/toBeDisplayed.js +3 -2
  18. package/lib/matchers/element/toBeDisplayedInViewport.js +3 -2
  19. package/lib/matchers/element/toBeEnabled.js +3 -2
  20. package/lib/matchers/element/toBeExisting.js +5 -2
  21. package/lib/matchers/element/toBeFocused.js +3 -2
  22. package/lib/matchers/element/toBeSelected.js +4 -4
  23. package/lib/matchers/element/toHaveAttribute.js +4 -16
  24. package/lib/matchers/element/toHaveChildren.js +9 -14
  25. package/lib/matchers/element/toHaveComputedLabel.js +4 -6
  26. package/lib/matchers/element/toHaveComputedRole.js +4 -6
  27. package/lib/matchers/element/toHaveElementClass.js +5 -10
  28. package/lib/matchers/element/toHaveElementProperty.js +4 -9
  29. package/lib/matchers/element/toHaveHTML.js +6 -8
  30. package/lib/matchers/element/toHaveHeight.js +6 -7
  31. package/lib/matchers/element/toHaveHref.js +6 -3
  32. package/lib/matchers/element/toHaveId.js +3 -2
  33. package/lib/matchers/element/toHaveSize.js +3 -4
  34. package/lib/matchers/element/toHaveStyle.js +3 -4
  35. package/lib/matchers/element/toHaveText.js +5 -8
  36. package/lib/matchers/element/toHaveValue.js +1 -1
  37. package/lib/matchers/element/toHaveWidth.js +6 -7
  38. package/lib/matchers/elements/toBeElementsArrayOfSize.js +71 -6
  39. package/lib/matchers/mock/toBeRequested.js +1 -1
  40. package/lib/matchers/mock/toBeRequestedTimes.js +30 -7
  41. package/lib/matchers/mock/toBeRequestedWith.js +40 -15
  42. package/lib/matchers/snapshot.js +38 -7
  43. package/lib/matchers.js +32 -32
  44. package/lib/snapshot.js +1 -8
  45. package/lib/tsconfig.build.tsbuildinfo +1 -1
  46. package/lib/util/elementsUtil.js +35 -6
  47. package/lib/util/executeBrowserCommand.js +37 -0
  48. package/lib/util/executeCommand.js +101 -52
  49. package/lib/util/formatMessage.js +81 -7
  50. package/lib/util/multiRemoteUtils.js +93 -0
  51. package/lib/util/numberOptionsUtil.js +19 -24
  52. package/lib/util/refetchElements.js +24 -2
  53. package/lib/utils.js +16 -111
  54. package/package.json +37 -40
  55. package/types/expect-global.d.ts +1 -2
  56. package/types/expect-webdriverio.d.ts +386 -232
package/lib/utils.js CHANGED
@@ -4,8 +4,7 @@ import { wrapExpectedWithArray } from './util/elementsUtil.js';
4
4
  import { executeCommandWithStrategy } from './util/executeCommand.js';
5
5
  import { enhanceError, enhanceErrorBe } from './util/formatMessage.js';
6
6
  import { waitUntil } from './util/waitUntil.js';
7
- import { DEFAULT_FEATURE_FLAGS } from './constants.js';
8
- import { isOneOfMatcher, OneOfMatcher } from './matchers/asymmetrics/oneOf.js';
7
+ import { isOneOfMatcher } from './matchers/asymmetrics/oneOf.js';
9
8
  export function isJasmineStringAsymmetricMatcher(expected) {
10
9
  return isAsymmetricMatcher(expected) && !('toAsymmetricMatcher' in expected) && 'jasmineToString' in expected && typeof expected.jasmineToString === 'function';
11
10
  }
@@ -68,8 +67,8 @@ export function getAsymmetricMatcherValue(expected) {
68
67
  return undefined;
69
68
  }
70
69
  async function executeCommandBe(received, command, options = {}) {
71
- const { isNot, verb = 'be', allowEmptyElements = false } = this;
72
- const { success: pass, actual, subject, context: { isSome } = {} } = await waitUntil(async (iteration) => {
70
+ const { isNot, verb = 'be', expectation = '', allowEmptyElements = false } = this;
71
+ const { success: pass, actual, subject, context: { isSome = false } = {} } = await waitUntil(async (iteration) => {
73
72
  return await executeCommandWithStrategy({
74
73
  unresolvedElements: received,
75
74
  expectedValues: true,
@@ -81,45 +80,21 @@ async function executeCommandBe(received, command, options = {}) {
81
80
  strictConfiguration: { allowEmptyElements },
82
81
  });
83
82
  }, isNot, { wait: options.wait, interval: options.interval });
84
- const message = enhanceErrorBe(subject, actual, { ...this, verb, isSome }, options);
83
+ const message = enhanceErrorBe(subject, actual, { ...this, verb, expectation, isSome }, options);
85
84
  return {
86
85
  pass,
87
86
  message: () => message,
88
87
  };
89
88
  }
90
- const compareNumbers = (actual, options = {}) => {
91
- if (typeof options.eq === 'number') {
92
- return actual === options.eq;
93
- }
94
- if (typeof options.gte === 'number' && typeof options.lte === 'number') {
95
- return actual >= options.gte && actual <= options.lte;
96
- }
97
- if (typeof options.gte === 'number') {
98
- return actual >= options.gte;
99
- }
100
- if (typeof options.lte === 'number') {
101
- return actual <= options.lte;
102
- }
103
- return false;
104
- };
105
- export const compareTextOrArray = (actualText, expectedTexts, options) => {
106
- if (expectedTexts === undefined) {
89
+ export const compareTextOrOneOf = (actualText, expectedText, options) => {
90
+ if (expectedText === undefined || Array.isArray(expectedText)) {
107
91
  return { actual: actualText, success: false };
108
92
  }
109
- if (Array.isArray(expectedTexts)) {
110
- if (expectedTexts.some((expected) => isOneOfMatcher(expected))) {
111
- throw new Error('OneOf is not supported in array under legacy behavior. Please enable `useToHaveTextStrictMultiElementsCompareStrategy` feature flag to use the new strict index based matching strategy with `expect.oneOf()`.');
112
- }
113
- else {
114
- console.warn('Array of expected values is deprecated. Please use `expect.oneOf()` asymmetric matcher to compare against multiple expected values. This will be removed in v8.0.0.');
115
- expectedTexts = new OneOfMatcher(...expectedTexts).withOptions(options);
116
- }
117
- }
118
- if (isOneOfMatcher(expectedTexts)) {
119
- return { success: expectedTexts.asymmetricMatch(actualText), actual: actualText };
93
+ if (isOneOfMatcher(expectedText)) {
94
+ return { success: expectedText.asymmetricMatch(actualText), actual: actualText };
120
95
  }
121
- const compareResults = compareText(actualText, expectedTexts, options);
122
- return { success: compareResults.success, actual: actualText };
96
+ const compareResults = compareText(actualText, expectedText, options);
97
+ return { ...compareResults, actual: actualText };
123
98
  };
124
99
  export const compareText = (actual, expected, { ignoreCase = false, trim = true, containing = false, atStart = false, atEnd = false, atIndex, replace, }) => {
125
100
  if (typeof actual !== 'string' || expected === null || expected === undefined) {
@@ -202,74 +177,6 @@ export const compareText = (actual, expected, { ignoreCase = false, trim = true,
202
177
  success: actual === expected,
203
178
  };
204
179
  };
205
- export const compareTextWithArray = (actual, expectedArray, { ignoreCase = false, trim = false, containing = false, atStart = false, atEnd = false, atIndex, replace, }) => {
206
- if (typeof actual !== 'string') {
207
- return {
208
- actual,
209
- success: false,
210
- };
211
- }
212
- if (trim) {
213
- actual = actual.trim();
214
- }
215
- if (Array.isArray(replace)) {
216
- actual = replaceActual(replace, actual);
217
- }
218
- const actualOriginalCase = actual;
219
- if (ignoreCase) {
220
- actual = actual.toLowerCase();
221
- expectedArray = expectedArray.map((item) => {
222
- if (typeof item === 'string') {
223
- return item.toLowerCase();
224
- }
225
- if (item instanceof RegExp) {
226
- return withIgnoreCaseFlag(item);
227
- }
228
- if (isStringContainingMatcherLike(item)) {
229
- const sample = getStringAsymmetricMatcherValue(item).toString().toLocaleLowerCase();
230
- return (isInversedStringContainingMatcher(item)
231
- ? expect.not.stringContaining(sample)
232
- : expect.stringContaining(sample));
233
- }
234
- if (isStringMatchingMatcherLike(item)) {
235
- const sample = getStringAsymmetricMatcherValue(item);
236
- const caseInsensitiveSample = withIgnoreCaseFlag(sample instanceof RegExp ? sample : new RegExp(sample));
237
- return (isInversedStringMatchingMatcher(item)
238
- ? expect.not.stringMatching(caseInsensitiveSample)
239
- : expect.stringMatching(caseInsensitiveSample));
240
- }
241
- return item;
242
- });
243
- }
244
- const hasFoundTextInArray = expectedArray.some((expected) => {
245
- if (expected instanceof RegExp) {
246
- return !!actualOriginalCase.match(expected);
247
- }
248
- if (isStringMatchingMatcherLike(expected) && getStringAsymmetricMatcherValue(expected) instanceof RegExp) {
249
- return expected.asymmetricMatch(actualOriginalCase);
250
- }
251
- if (isAsymmetricMatcher(expected)) {
252
- return expected.asymmetricMatch(actual);
253
- }
254
- if (containing) {
255
- return actual.includes(expected);
256
- }
257
- if (atStart) {
258
- return actual.startsWith(expected);
259
- }
260
- if (atEnd) {
261
- return actual.endsWith(expected);
262
- }
263
- if (atIndex !== undefined) {
264
- return actual.substring(atIndex, actual.length).startsWith(expected);
265
- }
266
- return actual === expected;
267
- });
268
- return {
269
- actual: actualOriginalCase,
270
- success: hasFoundTextInArray,
271
- };
272
- };
273
180
  export const compareObject = (actual, expected) => {
274
181
  if (typeof actual !== 'object' || Array.isArray(actual)) {
275
182
  return {
@@ -289,6 +196,11 @@ export const compareStyle = async (actualEl, style, { ignoreCase = false, trim =
289
196
  const css = await actualEl.getCSSProperty(key);
290
197
  let actualVal = String(css.value || '');
291
198
  let expectedVal = style[key];
199
+ if (typeof expectedVal !== 'string') {
200
+ actual[key] = css.value;
201
+ success = false;
202
+ continue;
203
+ }
292
204
  if (trim) {
293
205
  actualVal = actualVal.trim();
294
206
  expectedVal = expectedVal.trim();
@@ -330,7 +242,7 @@ export const compareStyle = async (actualEl, style, { ignoreCase = false, trim =
330
242
  success,
331
243
  };
332
244
  };
333
- export { compareNumbers, enhanceError, executeCommandBe, waitUntil, wrapExpectedWithArray };
245
+ export { enhanceError, executeCommandBe, waitUntil, wrapExpectedWithArray };
334
246
  function withIgnoreCaseFlag(expected) {
335
247
  const cloned = expected.ignoreCase
336
248
  ? new RegExp(expected.source, expected.flags)
@@ -352,11 +264,4 @@ function replaceActual(replace, actual) {
352
264
  }
353
265
  return actual;
354
266
  }
355
- export const getFeatureFlagValue = ({ featureFlags }, featureFlag) => {
356
- const providedFeatureFlagValue = featureFlags?.[featureFlag];
357
- if (providedFeatureFlagValue !== undefined) {
358
- return providedFeatureFlagValue;
359
- }
360
- return DEFAULT_FEATURE_FLAGS[featureFlag] ?? false;
361
- };
362
267
  export const toArray = (value) => value === undefined ? [] : Array.isArray(value) ? value : [value];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expect-webdriverio",
3
- "version": "6.1.0",
3
+ "version": "8.0.0-alpha.0",
4
4
  "author": "Mykola Grybyk <mykola.grybyk@gmail.com>",
5
5
  "description": "WebdriverIO Assertion Library",
6
6
  "license": "MIT",
@@ -19,7 +19,7 @@
19
19
  "wdio"
20
20
  ],
21
21
  "type": "module",
22
- "module": "./lib/index.js",
22
+ "main": "./lib/index.js",
23
23
  "exports": {
24
24
  ".": [
25
25
  {
@@ -43,7 +43,6 @@
43
43
  "types": "./jasmine-wdio-expect-async.d.ts"
44
44
  }
45
45
  ],
46
- "./types": "./types/expect-global.d.ts",
47
46
  "./expect-global": "./types/expect-global.d.ts",
48
47
  "./api": [
49
48
  {
@@ -56,8 +55,9 @@
56
55
  "types": "./types/expect-webdriverio.d.ts",
57
56
  "typeScriptVersion": "3.8.3",
58
57
  "engines": {
59
- "node": ">=20"
58
+ "node": ">=22.19.0"
60
59
  },
60
+ "packageManager": "pnpm@11.27.1+sha512.a81d4c21b9b09a4b0aebf90ef5e527d4262bdda322d26082978cb549023ac6d9cafc98b19fffaa5c71db438a65f70224edb78d7311187861ff96480d1cdca23c",
61
61
  "scripts": {
62
62
  "build": "run-s clean compile",
63
63
  "clean": "run-p clean:*",
@@ -65,66 +65,63 @@
65
65
  "compile": "run-s compile:*",
66
66
  "compile:lib": "tsc --build tsconfig.build.json",
67
67
  "compile:check": "if [ ! -f lib/index.js ] || [ $(find lib -type f | wc -l) -le 30 ]; then echo 'File structure under lib is broken'; exit 1; fi",
68
- "tsc:root-types": "node types-checks-filter-out-node_modules.js",
69
68
  "test": "run-s test:*",
70
69
  "test:tsc": "tsc --project tsconfig.json --noEmit --rootDir .",
71
- "test:lint": "eslint .",
70
+ "test:lint": "oxlint",
72
71
  "test:unit": "vitest --run",
73
- "test:types": "npm run ts && npm run tsc:root-types",
72
+ "test:types": "pnpm run ts",
74
73
  "ts": "run-s ts:* ts:*:*",
75
- "ts:jasmine": "cd test-types/jasmine && vitest --typecheck --typecheck.tsconfig ./tsconfig.json --run",
76
- "ts:jasmine-global-expect-async": "cd test-types/jasmine-global-expect-async && vitest --typecheck --typecheck.tsconfig ./tsconfig.json --run",
77
- "ts:jest:@types-jest": "cd test-types/jest-@types_jest && vitest --typecheck --typecheck.tsconfig ./tsconfig.json --run",
78
- "ts:mocha": "cd test-types/mocha && vitest --typecheck --typecheck.tsconfig ./tsconfig.json --run",
79
- "ts:arethetypeswrong": "npx @arethetypeswrong/cli --pack . --profile esm-only",
80
- "checks:all": "npm run build && npm run compile && npm run tsc:root-types && npm run test && npm run ts",
81
- "watch": "npm run compile -- --watch",
74
+ "ts:jasmine": "cd test-types/jasmine && vitest --typecheck --coverage.enabled=false --typecheck.tsconfig ./tsconfig.json --run",
75
+ "ts:jasmine-global-expect-async": "cd test-types/jasmine-global-expect-async && vitest --typecheck --coverage.enabled=false --typecheck.tsconfig ./tsconfig.json --run",
76
+ "ts:jest:@types-jest": "cd test-types/jest-@types_jest && vitest --typecheck --coverage.enabled=false --typecheck.tsconfig ./tsconfig.json --run",
77
+ "ts:mocha": "cd test-types/mocha && vitest --typecheck --coverage.enabled=false --typecheck.tsconfig ./tsconfig.json --run",
78
+ "ts:arethetypeswrong": "attw --pack . --profile esm-only",
79
+ "ts:declarations": "node types-checks-declarations.js",
80
+ "ts:package": "node types-checks-package.js",
81
+ "checks:all": "pnpm run build && pnpm test",
82
+ "watch": "pnpm run compile --watch",
82
83
  "prepare": "husky",
83
84
  "playgrounds:clean": "rimraf --glob '**/node_modules'",
84
- "playgrounds:setup": "npm run playgrounds:clean; npm i && cd playgrounds && npm run setup",
85
- "playgrounds:checks:all": "cd playgrounds && npm run checks:all",
86
- "playgrounds:snapshots:update": "cd playgrounds && npm run snapshots:update"
85
+ "playgrounds:setup": "pnpm run playgrounds:clean; pnpm install && cd playgrounds && pnpm run setup",
86
+ "playgrounds:checks:all": "cd playgrounds && pnpm run checks:all",
87
+ "playgrounds:snapshots:update": "cd playgrounds && pnpm run snapshots:update"
87
88
  },
88
89
  "dependencies": {
89
- "@vitest/snapshot": "^4.1.11",
90
+ "@vitest/snapshot": "^5.0.2",
91
+ "@wdio/types": "^9.31.5",
90
92
  "deep-eql": "^5.0.2",
91
- "expect": "^30.4.1",
92
- "jest-matcher-utils": "^30.4.1"
93
+ "expect": "^30.5.2",
94
+ "jest-matcher-utils": "^30.5.2"
93
95
  },
94
96
  "devDependencies": {
95
97
  "@arethetypeswrong/cli": "^0.18.5",
96
- "@jest/globals": "^30.4.1",
98
+ "@jest/globals": "^30.5.2",
99
+ "@stylistic/eslint-plugin": "^5.10.0",
97
100
  "@types/debug": "^4.1.13",
98
- "@types/jasmine": "^5.1.15",
101
+ "@types/deep-eql": "^4.0.2",
102
+ "@types/jasmine": "^6.0.0",
99
103
  "@types/jest": "^30.0.0",
100
104
  "@types/mocha": "^10.0.10",
101
- "@types/node": "^24.13.3",
102
- "@vitest/coverage-v8": "^4.1.11",
103
- "@vitest/eslint-plugin": "^1.6.27",
104
- "@wdio/eslint": "^0.1.3",
105
- "@wdio/types": "^9.32.0",
106
- "eslint": "^9.39.5",
105
+ "@types/node": "^24.19.0",
106
+ "@vitest/coverage-v8": "^5.0.2",
107
+ "@wdio/cli": "^9.32.0",
108
+ "@wdio/globals": "^9.31.3",
109
+ "@wdio/protocols": "^9.31.5",
107
110
  "husky": "^9.1.7",
108
111
  "npm-run-all2": "^9.0.3",
109
- "release-it": "^21.0.2",
112
+ "oxlint": "^1.85.0",
113
+ "release-it": "^21.1.0",
110
114
  "rimraf": "^6.1.3",
111
115
  "strip-ansi": "^7.2.0",
112
- "typescript": "^5.9.3",
113
- "vitest": "^4.1.11",
116
+ "typescript": "^7.0.2",
117
+ "vitest": "^5.0.2",
118
+ "webdriver": "^9.32.0",
114
119
  "webdriverio": "^9.32.0"
115
120
  },
116
121
  "peerDependencies": {
117
- "@wdio/globals": "^9.0.0",
118
- "@wdio/logger": "^9.0.0",
119
- "webdriverio": "^9.28.0"
122
+ "webdriverio": "^9.31.5"
120
123
  },
121
124
  "peerDependenciesMeta": {
122
- "@wdio/globals": {
123
- "optional": false
124
- },
125
- "@wdio/logger": {
126
- "optional": false
127
- },
128
125
  "webdriverio": {
129
126
  "optional": false
130
127
  }
@@ -5,12 +5,11 @@
5
5
  * Required when used in standalone mode (mocha) or to override the one of Jasmine
6
6
  */
7
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!
8
+ // @ts-ignore, not @ts-expect-error: only a conflict when the Jasmine `expect` is also loaded
9
9
  declare const expect: ExpectWebdriverIO.Expect
10
10
 
11
11
  declare namespace NodeJS {
12
12
  interface Global {
13
- // @ts-expect-error: Both wdio & Jasmine augmentation declare different global expect explaining why TS is complaining here
14
13
  expect: ExpectWebdriverIO.Expect
15
14
  }
16
15
  }