expect-webdriverio 7.0.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 (48) hide show
  1. package/README.md +7 -3
  2. package/jasmine-wdio-expect-async.d.ts +1 -1
  3. package/lib/constants.js +0 -6
  4. package/lib/index.js +1 -13
  5. package/lib/jasmineUtils.js +2 -2
  6. package/lib/matchers/asymmetrics/oneOf.js +2 -2
  7. package/lib/matchers/browser/toHaveClipboardText.js +7 -3
  8. package/lib/matchers/browser/toHaveLocalStorageItem.js +1 -11
  9. package/lib/matchers/element/toBeClickable.js +3 -2
  10. package/lib/matchers/element/toBeDisabled.js +3 -2
  11. package/lib/matchers/element/toBeDisplayed.js +3 -2
  12. package/lib/matchers/element/toBeDisplayedInViewport.js +3 -2
  13. package/lib/matchers/element/toBeEnabled.js +3 -2
  14. package/lib/matchers/element/toBeExisting.js +5 -2
  15. package/lib/matchers/element/toBeFocused.js +3 -2
  16. package/lib/matchers/element/toBeSelected.js +4 -4
  17. package/lib/matchers/element/toHaveAttribute.js +2 -14
  18. package/lib/matchers/element/toHaveChildren.js +7 -13
  19. package/lib/matchers/element/toHaveComputedLabel.js +0 -2
  20. package/lib/matchers/element/toHaveComputedRole.js +0 -2
  21. package/lib/matchers/element/toHaveElementClass.js +3 -8
  22. package/lib/matchers/element/toHaveElementProperty.js +0 -5
  23. package/lib/matchers/element/toHaveHTML.js +3 -5
  24. package/lib/matchers/element/toHaveHeight.js +4 -5
  25. package/lib/matchers/element/toHaveHref.js +6 -3
  26. package/lib/matchers/element/toHaveId.js +3 -2
  27. package/lib/matchers/element/toHaveSize.js +0 -1
  28. package/lib/matchers/element/toHaveStyle.js +0 -1
  29. package/lib/matchers/element/toHaveText.js +2 -5
  30. package/lib/matchers/element/toHaveValue.js +1 -1
  31. package/lib/matchers/element/toHaveWidth.js +4 -5
  32. package/lib/matchers/elements/toBeElementsArrayOfSize.js +9 -11
  33. package/lib/matchers/mock/toBeRequested.js +1 -1
  34. package/lib/matchers/mock/toBeRequestedTimes.js +6 -6
  35. package/lib/matchers/mock/toBeRequestedWith.js +0 -3
  36. package/lib/matchers.js +32 -32
  37. package/lib/snapshot.js +1 -8
  38. package/lib/tsconfig.build.tsbuildinfo +1 -1
  39. package/lib/util/elementsUtil.js +3 -2
  40. package/lib/util/executeBrowserCommand.js +2 -2
  41. package/lib/util/executeCommand.js +2 -41
  42. package/lib/util/formatMessage.js +4 -4
  43. package/lib/util/multiRemoteUtils.js +9 -1
  44. package/lib/util/numberOptionsUtil.js +15 -26
  45. package/lib/utils.js +10 -110
  46. package/package.json +36 -39
  47. package/types/expect-global.d.ts +1 -2
  48. package/types/expect-webdriverio.d.ts +31 -222
package/README.md CHANGED
@@ -1,5 +1,9 @@
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
+ > [!IMPORTANT]
4
+ > This branch is v8, in development for WebdriverIO v10. Its docs are not released yet.
5
+ > For the latest release (v7, WebdriverIO v9), see the [v7 README](https://github.com/webdriverio/expect-webdriverio/blob/v7/README.md).
6
+
3
7
  ###### [API](docs/API.md) | [Multiple Elements](docs/MultipleElements.md) | [Multi-remote](docs/MultiRemote.md) | [TypeScript / JS Autocomplete](docs/Types.md) | [Examples](docs/Examples.md) | [Extending Matchers](docs/CustomMatchers.md)
4
8
 
5
9
  > [WebdriverIO](https://webdriver.io/) assertion library inspired by [expect](https://www.npmjs.com/package/expect)
@@ -31,7 +35,7 @@
31
35
  If you run your tests through the [WDIO testrunner](https://webdriver.io/docs/clioptions), no additional setup is needed. WebdriverIO initializes `expect-webdriverio` and makes `expect` available in the global scope so you can use it directly in your tests:
32
36
 
33
37
  ```js
34
- await expect($('button')).toBeDisplayed()
38
+ await expect($('#submit')).toBeDisplayed()
35
39
  await expect($$('buttons')).toBeDisplayed()
36
40
  ```
37
41
 
@@ -46,7 +50,7 @@ await expect(multiRemoteBrowser).toHaveTitle('WebdriverIO')
46
50
  await expect(multiRemoteBrowser.$('h1')).toHaveText(expect.multiRemote({ chrome: 'Welcome', firefox: 'Bienvenue' }))
47
51
  ```
48
52
 
49
- Some feature flags and WebdriverIO environment variables are required or recommended, see [Multi-remote Support](docs/MultiRemote.md#requirements--configuration).
53
+ Some WebdriverIO environment variables are recommended, see [Multi-remote Support](docs/MultiRemote.md#requirements--configuration).
50
54
 
51
55
  #### Local & Browser Runners
52
56
  - **Local Runner**: Fully compatible. You can leverage your test framework adapter as mentioned above.
@@ -69,7 +73,7 @@ import { expect } from 'expect-webdriverio'
69
73
 
70
74
  await browser.url('https://webdriver.io')
71
75
 
72
- const $button = await browser.$('button')
76
+ const $button = await browser.$('.DocSearch-Button')
73
77
  await expect($button).toBeDisplayed()
74
78
 
75
79
  await browser.deleteSession()
@@ -80,7 +80,7 @@ declare namespace ExpectWebdriverIO {
80
80
  * Under `@wdio/jasmine-framework`, the global `expect` is overridden to use Jasmine's `expectAsync`.
81
81
  * It contains custom WebdriverIO matchers as well as Jasmine's built-in async & sync matchers but not the basic Jest's expect library matchers.
82
82
  */
83
- // @ts-expect-error: IDE might flag this, but ignore it. This way the `tsc:root-types` can pass!
83
+ // @ts-expect-error: the Jasmine `expect` is always loaded with this entry, so the two declarations conflict
84
84
  declare const expect: ExpectWebdriverIO.JasmineExpect
85
85
  declare namespace NodeJS {
86
86
  interface Global {
package/lib/constants.js CHANGED
@@ -1,14 +1,10 @@
1
1
  import { getGlobalSingleton } from './util/globalSingleton.js';
2
2
  function createSharedConstants() {
3
- const DEFAULT_FEATURE_FLAGS = {
4
- useToHaveTextStrictMultiElementsCompareStrategy: false
5
- };
6
3
  const DEFAULT_OPTIONS = {
7
4
  wait: 2000,
8
5
  interval: 100,
9
6
  beforeAssertion: async () => { },
10
7
  afterAssertion: async () => { },
11
- featureFlags: DEFAULT_FEATURE_FLAGS
12
8
  };
13
9
  const DEFAULT_OPTIONS_TO_BE_DISPLAYED = {
14
10
  ...DEFAULT_OPTIONS,
@@ -18,14 +14,12 @@ function createSharedConstants() {
18
14
  visibilityProperty: true
19
15
  };
20
16
  return {
21
- DEFAULT_FEATURE_FLAGS,
22
17
  DEFAULT_OPTIONS,
23
18
  DEFAULT_OPTIONS_TO_BE_DISPLAYED,
24
19
  defaultOptionsList: [DEFAULT_OPTIONS, DEFAULT_OPTIONS_TO_BE_DISPLAYED]
25
20
  };
26
21
  }
27
22
  const shared = getGlobalSingleton('constants', createSharedConstants);
28
- export const DEFAULT_FEATURE_FLAGS = shared.DEFAULT_FEATURE_FLAGS;
29
23
  export const DEFAULT_OPTIONS = shared.DEFAULT_OPTIONS;
30
24
  export const DEFAULT_OPTIONS_TO_BE_DISPLAYED = shared.DEFAULT_OPTIONS_TO_BE_DISPLAYED;
31
25
  export const defaultOptionsList = shared.defaultOptionsList;
package/lib/index.js CHANGED
@@ -8,7 +8,6 @@ import { multiRemote } from './matchers/asymmetrics/multiRemote.js';
8
8
  import { getGlobalSingleton } from './util/globalSingleton.js';
9
9
  function createSharedExpectSetup() {
10
10
  const wdioCustomMatchers = {};
11
- const matchers = new Map();
12
11
  const extend = expectLib.extend;
13
12
  expectLib.extend = (extendedMatchers) => {
14
13
  if (!extendedMatchers || typeof extendedMatchers !== 'object') {
@@ -16,7 +15,6 @@ function createSharedExpectSetup() {
16
15
  }
17
16
  Object.entries(extendedMatchers).forEach(([name, matcher]) => {
18
17
  wdioCustomMatchers[name] = matcher;
19
- matchers.set(name, matcher);
20
18
  });
21
19
  return extend(extendedMatchers);
22
20
  };
@@ -46,11 +44,10 @@ function createSharedExpectSetup() {
46
44
  configurable: true,
47
45
  value: (testId) => SoftAssertService.getInstance().clearFailures(testId)
48
46
  });
49
- return { wdioExpect, wdioCustomMatchers, matchers };
47
+ return { wdioExpect, wdioCustomMatchers };
50
48
  }
51
49
  const sharedExpectSetup = getGlobalSingleton('expect', createSharedExpectSetup);
52
50
  export const wdioCustomMatchers = sharedExpectSetup.wdioCustomMatchers;
53
- export const matchers = sharedExpectSetup.matchers;
54
51
  export const expect = sharedExpectSetup.wdioExpect;
55
52
  export const getDefaultOptions = () => DEFAULT_OPTIONS;
56
53
  export const setDefaultOptions = (options) => {
@@ -62,15 +59,6 @@ export const setDefaultOptions = (options) => {
62
59
  });
63
60
  });
64
61
  };
65
- export const setFeatureFlags = (featureFlags) => {
66
- Object.entries(featureFlags).forEach(([ffName, ffValue]) => {
67
- defaultOptionsList.forEach((option) => {
68
- option.featureFlags[ffName] = ffValue;
69
- });
70
- });
71
- };
72
- export const setOptions = setDefaultOptions;
73
- export const getConfig = getDefaultOptions;
74
62
  export { SnapshotService } from './snapshot.js';
75
63
  export { SoftAssertService } from './softAssert.js';
76
64
  export { SoftAssertionService } from './softAssertService.js';
@@ -11,10 +11,10 @@ function asymmetricMatch(a, b) {
11
11
  if (asymmetricA && asymmetricB) {
12
12
  return undefined;
13
13
  }
14
- const matchersUtil = {
14
+ const matchersUtil = Object.assign([], {
15
15
  equals,
16
16
  contains: (actual, expected) => actual.some(value => equals(value, expected)),
17
- };
17
+ });
18
18
  if (asymmetricA) {
19
19
  return a.asymmetricMatch(b, matchersUtil);
20
20
  }
@@ -1,4 +1,4 @@
1
- import { compareTextWithArray } from '../../utils.js';
1
+ import { compareText } from '../../utils.js';
2
2
  import { WdioAsymmetricMatchers } from './asymmetricsUtils.js';
3
3
  const ONE_OF_TAG = 'expect-webdriverio.oneOf';
4
4
  const ONE_OF_SYMBOL = Symbol.for(ONE_OF_TAG);
@@ -19,7 +19,7 @@ export class OneOfMatcher extends WdioAsymmetricMatchers {
19
19
  else if (typeof actual !== 'string') {
20
20
  return false;
21
21
  }
22
- return compareTextWithArray(actual, this.sample.filter(s => s !== null), this.options).success;
22
+ return this.sample.some((expected) => expected !== null && compareText(actual, expected, this.options).success);
23
23
  }
24
24
  withOptions(options) {
25
25
  return new OneOfMatcher(...this.sample).setOptions(options);
@@ -1,9 +1,8 @@
1
- import logger from '@wdio/logger';
2
1
  import { waitUntil, enhanceError, compareTextOrOneOf } from '../../utils.js';
3
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
4
3
  import { executeBrowserCommand } from '../../util/executeBrowserCommand.js';
5
4
  import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
6
- const log = logger('expect-webdriverio');
5
+ let permissionWarningLogged = false;
7
6
  export async function toHaveClipboardText(browser, expectedValue, options = DEFAULT_OPTIONS) {
8
7
  const { expectation = 'clipboard text', verb = 'have', isNot, matcherName = 'toHaveClipboardText' } = this;
9
8
  await options.beforeAssertion?.({
@@ -35,7 +34,12 @@ export async function toHaveClipboardText(browser, expectedValue, options = DEFA
35
34
  }
36
35
  const compareClipboardText = async (browser, expectedValue, options) => {
37
36
  await browser.setPermissions({ name: 'clipboard-read' }, 'granted')
38
- .catch((err) => log.warn(`Couldn't set clipboard permissions: ${err}`));
37
+ .catch((err) => {
38
+ if (!permissionWarningLogged) {
39
+ permissionWarningLogged = true;
40
+ console.warn(`expect-webdriverio: Couldn't set clipboard permissions: ${err}`);
41
+ }
42
+ });
39
43
  const actual = await browser.execute(() => window.navigator.clipboard.readText());
40
44
  return compareTextOrOneOf(actual, expectedValue, options);
41
45
  };
@@ -10,17 +10,7 @@ export async function toHaveLocalStorageItem(browser, key, expectedValue, option
10
10
  expectedValue: expectedValue ? [key, expectedValue] : key,
11
11
  options,
12
12
  });
13
- const paramsCount = arguments.length;
14
- let expected;
15
- if (expectedValue === undefined) {
16
- if (paramsCount > 2) {
17
- console.warn('[DEPRECATION] Using toHaveLocalStorageItem with undefined is deprecated in favor of expect.anything().');
18
- }
19
- expected = expect.anything();
20
- }
21
- else {
22
- expected = expectedValue;
23
- }
13
+ const expected = expectedValue ?? expect.anything();
24
14
  const expectedWithOptions = buildWdioAsymmetricMatchersWithOptions(expected, options);
25
15
  const { actual, success: pass, subject, expected: expectedValues } = await waitUntil(async () => {
26
16
  return await executeBrowserCommand({
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toBeClickable(received, options = DEFAULT_OPTIONS) {
4
4
  this.expectation = this.expectation || 'clickable';
5
+ const { matcherName = 'toBeClickable' } = this;
5
6
  await options.beforeAssertion?.({
6
- matcherName: 'toBeClickable',
7
+ matcherName,
7
8
  options,
8
9
  });
9
10
  const result = await executeCommandBe.call(this, received, el => el?.isClickable(), options);
10
11
  await options.afterAssertion?.({
11
- matcherName: 'toBeClickable',
12
+ matcherName,
12
13
  options,
13
14
  result
14
15
  });
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toBeDisabled(received, options = DEFAULT_OPTIONS) {
4
4
  this.expectation = this.expectation || 'disabled';
5
+ const { matcherName = 'toBeDisabled' } = this;
5
6
  await options.beforeAssertion?.({
6
- matcherName: 'toBeDisabled',
7
+ matcherName,
7
8
  options,
8
9
  });
9
10
  const result = await executeCommandBe.call(this, received, async (el) => !await el.isEnabled(), options);
10
11
  await options.afterAssertion?.({
11
- matcherName: 'toBeDisabled',
12
+ matcherName,
12
13
  options,
13
14
  result
14
15
  });
@@ -2,8 +2,9 @@ import { executeCommandBe } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS_TO_BE_DISPLAYED } from '../../constants.js';
3
3
  export async function toBeDisplayed(received, options = DEFAULT_OPTIONS_TO_BE_DISPLAYED) {
4
4
  this.expectation = this.expectation || 'displayed';
5
+ const { matcherName = 'toBeDisplayed' } = this;
5
6
  await options.beforeAssertion?.({
6
- matcherName: 'toBeDisplayed',
7
+ matcherName,
7
8
  options,
8
9
  });
9
10
  const { withinViewport, contentVisibilityAuto, opacityProperty, visibilityProperty, ...commandOptions } = { ...DEFAULT_OPTIONS_TO_BE_DISPLAYED, ...options };
@@ -14,7 +15,7 @@ export async function toBeDisplayed(received, options = DEFAULT_OPTIONS_TO_BE_DI
14
15
  visibilityProperty
15
16
  }), commandOptions);
16
17
  await options.afterAssertion?.({
17
- matcherName: 'toBeDisplayed',
18
+ matcherName,
18
19
  options,
19
20
  result
20
21
  });
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toBeDisplayedInViewport(received, options = DEFAULT_OPTIONS) {
4
4
  this.expectation = this.expectation || 'displayed in viewport';
5
+ const { matcherName = 'toBeDisplayedInViewport' } = this;
5
6
  await options.beforeAssertion?.({
6
- matcherName: 'toBeDisplayedInViewport',
7
+ matcherName,
7
8
  options,
8
9
  });
9
10
  const result = await executeCommandBe.call(this, received, el => el?.isDisplayed({ withinViewport: true }), options);
10
11
  await options.afterAssertion?.({
11
- matcherName: 'toBeDisplayedInViewport',
12
+ matcherName,
12
13
  options,
13
14
  result
14
15
  });
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toBeEnabled(received, options = DEFAULT_OPTIONS) {
4
4
  this.expectation = this.expectation || 'enabled';
5
+ const { matcherName = 'toBeEnabled' } = this;
5
6
  await options.beforeAssertion?.({
6
- matcherName: 'toBeEnabled',
7
+ matcherName,
7
8
  options,
8
9
  });
9
10
  const result = await executeCommandBe.call(this, received, el => el?.isEnabled(), options);
10
11
  await options.afterAssertion?.({
11
- matcherName: 'toBeEnabled',
12
+ matcherName,
12
13
  options,
13
14
  result
14
15
  });
@@ -4,13 +4,14 @@ export async function toExist(received, options = DEFAULT_OPTIONS) {
4
4
  this.expectation = this.expectation || 'exist';
5
5
  this.verb = this.verb || '';
6
6
  this.allowEmptyElements = true;
7
+ const { matcherName = 'toExist' } = this;
7
8
  await options.beforeAssertion?.({
8
- matcherName: 'toExist',
9
+ matcherName,
9
10
  options,
10
11
  });
11
12
  const result = await executeCommandBe.call(this, received, el => el?.isExisting(), options);
12
13
  await options.afterAssertion?.({
13
- matcherName: 'toExist',
14
+ matcherName,
14
15
  options,
15
16
  result
16
17
  });
@@ -19,10 +20,12 @@ export async function toExist(received, options = DEFAULT_OPTIONS) {
19
20
  export function toBeExisting(el, options) {
20
21
  this.expectation = 'existing';
21
22
  this.verb = 'be';
23
+ this.matcherName ??= 'toBeExisting';
22
24
  return toExist.call(this, el, options);
23
25
  }
24
26
  export function toBePresent(el, options) {
25
27
  this.expectation = 'present';
26
28
  this.verb = 'be';
29
+ this.matcherName ??= 'toBePresent';
27
30
  return toExist.call(this, el, options);
28
31
  }
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toBeFocused(received, options = DEFAULT_OPTIONS) {
4
4
  this.expectation = this.expectation || 'focused';
5
+ const { matcherName = 'toBeFocused' } = this;
5
6
  await options.beforeAssertion?.({
6
- matcherName: 'toBeFocused',
7
+ matcherName,
7
8
  options,
8
9
  });
9
10
  const result = await executeCommandBe.call(this, received, el => el?.isFocused(), options);
10
11
  await options.afterAssertion?.({
11
- matcherName: 'toBeFocused',
12
+ matcherName,
12
13
  options,
13
14
  result
14
15
  });
@@ -2,14 +2,14 @@ import { executeCommandBe } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toBeSelected(received, options = DEFAULT_OPTIONS) {
4
4
  this.expectation = this.expectation || 'selected';
5
- this.matcherName = this.matcherName || 'toBeSelected';
5
+ const { matcherName = 'toBeSelected' } = this;
6
6
  await options.beforeAssertion?.({
7
- matcherName: this.matcherName,
7
+ matcherName,
8
8
  options,
9
9
  });
10
10
  const result = await executeCommandBe.call(this, received, el => el?.isSelected(), options);
11
11
  await options.afterAssertion?.({
12
- matcherName: this.matcherName,
12
+ matcherName,
13
13
  options,
14
14
  result
15
15
  });
@@ -17,7 +17,7 @@ export async function toBeSelected(received, options = DEFAULT_OPTIONS) {
17
17
  }
18
18
  export async function toBeChecked(received, options = DEFAULT_OPTIONS) {
19
19
  this.expectation = 'checked';
20
- this.matcherName = 'toBeChecked';
20
+ this.matcherName ??= 'toBeChecked';
21
21
  const result = await toBeSelected.call(this, received, options);
22
22
  return result;
23
23
  }
@@ -29,7 +29,6 @@ export async function toHaveAttributeAndValue(received, attribute, expectedValue
29
29
  return conditionAttributeValueMatchWithExpected(element, attribute, values, options);
30
30
  },
31
31
  context: { isNot, iteration },
32
- strategy: 'NewStrictMultipleElements',
33
32
  });
34
33
  }, isNot, { wait: options.wait, interval: options.interval });
35
34
  const expected = expectedValues ?? wrapExpectedWithArray(el, attr, expectedValue);
@@ -40,23 +39,13 @@ export async function toHaveAttributeAndValue(received, attribute, expectedValue
40
39
  };
41
40
  }
42
41
  export async function toHaveAttribute(received, attribute, value, options = DEFAULT_OPTIONS) {
43
- const matcherName = 'toHaveAttribute';
44
- const paramsCount = arguments.length;
42
+ const { matcherName = 'toHaveAttribute' } = this;
45
43
  await options.beforeAssertion?.({
46
44
  matcherName,
47
45
  expectedValue: [attribute, value],
48
46
  options,
49
47
  });
50
- let expectedValue;
51
- if (value === undefined) {
52
- if (paramsCount > 2) {
53
- console.warn('Using undefined or null as value for toHaveAttribute is deprecated and will be removed in v6.0.0. Please omit the third argument entirely or use toHaveAttribute(el, attribute, wdioExpect.anything(), options).');
54
- }
55
- expectedValue = expect.anything();
56
- }
57
- else {
58
- expectedValue = value;
59
- }
48
+ const expectedValue = value ?? expect.anything();
60
49
  const result = await toHaveAttributeAndValue.call(this, received, attribute, expectedValue, options);
61
50
  await options.afterAssertion?.({
62
51
  matcherName,
@@ -66,4 +55,3 @@ export async function toHaveAttribute(received, attribute, value, options = DEFA
66
55
  });
67
56
  return result;
68
57
  }
69
- export const toHaveAttr = toHaveAttribute;
@@ -1,7 +1,6 @@
1
1
  import { DEFAULT_OPTIONS } from '../../constants.js';
2
2
  import { executeCommandWithStrategy } from '../../util/executeCommand.js';
3
- import { isEmptyOrLegacyNumberOptions, validateNumberArrayAndExtractOptions } from '../../util/numberOptionsUtil.js';
4
- import { isMultiRemoteMatcher } from '../../util/multiRemoteUtils.js';
3
+ import { validateNumberMatcherArray } from '../../util/numberOptionsUtil.js';
5
4
  import { enhanceError, waitUntil, wrapExpectedWithArray } from '../../utils.js';
6
5
  async function condition(el, expectedValue) {
7
6
  const children = await el.$$('./*').getElements();
@@ -13,36 +12,31 @@ async function condition(el, expectedValue) {
13
12
  actual: children?.length
14
13
  };
15
14
  }
16
- export async function toHaveChildren(received, expectedValueOrOptions, options = DEFAULT_OPTIONS) {
15
+ export async function toHaveChildren(received, expectedValue, options = DEFAULT_OPTIONS) {
17
16
  const { expectation = 'children', verb = 'have', isNot, matcherName = 'toHaveChildren' } = this;
18
- const paramsCount = arguments.length;
19
- if (paramsCount > 1 && (expectedValueOrOptions === undefined || (isEmptyOrLegacyNumberOptions(expectedValueOrOptions) && !isMultiRemoteMatcher(expectedValueOrOptions)))) {
20
- console.warn('Passing undefined or NumberOptions as the second argument to toHaveChildren is deprecated. Use a NumberMatcher instead. For example, `expect(el).toHaveChildren({ gte: 1 }, options)`');
21
- }
22
17
  await options.beforeAssertion?.({
23
18
  matcherName,
24
- expectedValue: expectedValueOrOptions,
19
+ expectedValue,
25
20
  options,
26
21
  });
27
- const { numberMatcher: expectedNumber, commandOptions } = validateNumberArrayAndExtractOptions(expectedValueOrOptions, options, { supportDefaultAsGteThen1: true });
22
+ const expectedNumber = validateNumberMatcherArray(expectedValue, { supportDefaultAsGteThen1: true });
28
23
  const { success: pass, actual: children, subject, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
29
24
  return await executeCommandWithStrategy({
30
25
  unresolvedElements: received,
31
26
  expectedValues: expectedNumber,
32
27
  singleElementCompare: (element, expectedValue) => condition(element, expectedValue),
33
28
  context: { isNot, iteration },
34
- strategy: 'NewStrictMultipleElements',
35
29
  });
36
- }, isNot, { wait: commandOptions.wait, interval: commandOptions.interval });
30
+ }, isNot, { wait: options.wait, interval: options.interval });
37
31
  const expectedArray = expected ?? wrapExpectedWithArray(subject, children, expectedNumber);
38
- const message = enhanceError(subject, expectedArray, children, { isNot, isSome }, verb, expectation, '', commandOptions);
32
+ const message = enhanceError(subject, expectedArray, children, { isNot, isSome }, verb, expectation, '', options);
39
33
  const result = {
40
34
  pass,
41
35
  message: () => message
42
36
  };
43
37
  await options.afterAssertion?.({
44
38
  matcherName,
45
- expectedValue: expectedValueOrOptions,
39
+ expectedValue,
46
40
  options,
47
41
  result
48
42
  });
@@ -21,8 +21,6 @@ export async function toHaveComputedLabel(received, expectedValue, options = DEF
21
21
  expectedValues: expectedValue,
22
22
  singleElementCompare: (element, expectedValue) => singleElementCompare(element, expectedValue, options),
23
23
  context: { isNot, iteration },
24
- strategy: 'NewStrictMultipleElements',
25
- strictConfiguration: { allowArrayWithSingleElement: true }
26
24
  });
27
25
  }, isNot, { wait: options.wait, interval: options.interval });
28
26
  const message = enhanceError(el, expected ?? wrapExpectedWithArray(el, actualLabel, expectedValue), actualLabel, { isNot, isSome }, verb, expectation, '', options);
@@ -21,8 +21,6 @@ export async function toHaveComputedRole(received, expectedValue, options = DEFA
21
21
  expectedValues: expectedValue,
22
22
  singleElementCompare: (element, expectedValue) => singleElementCompare(element, expectedValue, options),
23
23
  context: { isNot, iteration },
24
- strategy: 'NewStrictMultipleElements',
25
- strictConfiguration: { allowArrayWithSingleElement: true }
26
24
  });
27
25
  }, isNot, { wait: options.wait, interval: options.interval });
28
26
  const message = enhanceError(el, expected ?? wrapExpectedWithArray(el, actualRole, expectedValue), actualRole, { isNot, isSome }, verb, expectation, '', options);
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_OPTIONS } from '../../constants.js';
2
2
  import { executeCommandWithStrategy } from '../../util/executeCommand.js';
3
- import { compareText, compareTextOrOneOf, enhanceError, isAsymmetricMatcher, waitUntil, wrapExpectedWithArray } from '../../utils.js';
3
+ import { compareText, enhanceError, isAsymmetricMatcher, waitUntil, wrapExpectedWithArray } from '../../utils.js';
4
4
  async function singleElementCompare(el, attribute, value, options) {
5
5
  const actualClass = await el.getAttribute(attribute);
6
6
  if (value === undefined) {
@@ -13,17 +13,13 @@ async function singleElementCompare(el, attribute, value, options) {
13
13
  return compareText(actualClass, value, options);
14
14
  }
15
15
  const classes = actualClass.split(' ');
16
- const isValueInClasses = classes.some((clazz) => {
17
- return compareTextOrOneOf(clazz, value, options).success;
18
- });
16
+ const values = Array.isArray(value) ? value : [value];
17
+ const isValueInClasses = classes.some((clazz) => values.some((expected) => compareText(clazz, expected, options).success));
19
18
  return {
20
19
  success: isValueInClasses,
21
20
  actual: actualClass
22
21
  };
23
22
  }
24
- export function toHaveClass(...args) {
25
- return toHaveElementClass.call(this || {}, ...args);
26
- }
27
23
  export async function toHaveElementClass(received, expectedValue, options = DEFAULT_OPTIONS) {
28
24
  const { expectation = 'class', verb = 'have', isNot, matcherName = 'toHaveElementClass' } = this;
29
25
  await options.beforeAssertion?.({
@@ -39,7 +35,6 @@ export async function toHaveElementClass(received, expectedValue, options = DEFA
39
35
  expectedValues: expectedValue,
40
36
  singleElementCompare: (element, expectedValue) => singleElementCompare(element, attribute, expectedValue, options),
41
37
  context: { isNot, iteration },
42
- strategy: 'NewStrictMultipleElements',
43
38
  strictConfiguration: { allowArrayWithSingleElement: true }
44
39
  });
45
40
  }, isNot, { wait: options.wait, interval: options.interval });
@@ -21,11 +21,7 @@ async function condition(el, property, expectedValue, options = DEFAULT_OPTIONS)
21
21
  }
22
22
  export async function toHaveElementProperty(received, property, value, options = DEFAULT_OPTIONS) {
23
23
  const { expectation = 'property', verb = 'have', isNot, matcherName = 'toHaveElementProperty', allowObjectExpectedValue = true } = this;
24
- const paramsCount = arguments.length;
25
24
  if (value === undefined || value === null) {
26
- if (paramsCount > 2) {
27
- console.warn('Using undefined or null as value for toHaveElementProperty is deprecated and will be removed in v6.0.0. Please omit the third argument entirely or use toHaveElementProperty(el, property, object.anything(), options).');
28
- }
29
25
  value = expect.anything();
30
26
  }
31
27
  await options.beforeAssertion?.({
@@ -43,7 +39,6 @@ export async function toHaveElementProperty(received, property, value, options =
43
39
  return condition(element, property, expectedValue, options);
44
40
  },
45
41
  context: { isNot, iteration },
46
- strategy: 'NewStrictMultipleElements',
47
42
  strictConfiguration: { allowArrayWithSingleElement: false, allowObjectExpectedValue }
48
43
  });
49
44
  }, isNot, { wait: options.wait, interval: options.interval });
@@ -8,25 +8,23 @@ async function singleElementCompare(el, html, options) {
8
8
  }
9
9
  export async function toHaveHTML(received, expectedValue, options = DEFAULT_OPTIONS) {
10
10
  const { expectation = 'HTML', verb = 'have', isNot, matcherName = 'toHaveHTML' } = this;
11
- expectedValue = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
12
11
  await options.beforeAssertion?.({
13
12
  matcherName,
14
13
  expectedValue,
15
14
  options,
16
15
  });
16
+ const expectedWithOptions = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
17
17
  const { success: pass, actual: actualHTML, subject: elements, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
18
18
  const result = await executeCommandWithStrategy({
19
19
  unresolvedElements: received,
20
20
  supportsArrayContaining: true,
21
- expectedValues: expectedValue,
21
+ expectedValues: expectedWithOptions,
22
22
  singleElementCompare: (element, expectedValue) => singleElementCompare(element, expectedValue, options),
23
23
  context: { isNot, iteration },
24
- strategy: 'NewStrictMultipleElements',
25
- strictConfiguration: { allowArrayWithSingleElement: true }
26
24
  });
27
25
  return result;
28
26
  }, isNot, { wait: options.wait, interval: options.interval });
29
- const expectedValues = expected ?? wrapExpectedWithArray(elements, actualHTML, expectedValue);
27
+ const expectedValues = expected ?? wrapExpectedWithArray(elements, actualHTML, expectedWithOptions);
30
28
  const message = enhanceError(elements, expectedValues, actualHTML, { isNot, isSome }, verb, expectation, '', options);
31
29
  const result = {
32
30
  pass,
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_OPTIONS } from '../../constants.js';
2
2
  import { wrapExpectedWithArray } from '../../util/elementsUtil.js';
3
3
  import { executeCommandWithStrategy } from '../../util/executeCommand.js';
4
- import { validateNumberArrayAndExtractOptions } from '../../util/numberOptionsUtil.js';
4
+ import { validateNumberMatcherArray } from '../../util/numberOptionsUtil.js';
5
5
  import { enhanceError, waitUntil, } from '../../utils.js';
6
6
  async function condition(el, expectedNumber) {
7
7
  const actualHeight = await el.getSize('height');
@@ -17,19 +17,18 @@ export async function toHaveHeight(received, expectedValue, options = DEFAULT_OP
17
17
  expectedValue,
18
18
  options,
19
19
  });
20
- const { numberMatcher: expectedNumber, commandOptions } = validateNumberArrayAndExtractOptions(expectedValue, options);
20
+ const expectedNumber = validateNumberMatcherArray(expectedValue);
21
21
  const { success: pass, actual: actualHeight, subject: elements, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
22
22
  return await executeCommandWithStrategy({
23
23
  unresolvedElements: received,
24
24
  expectedValues: expectedNumber,
25
25
  singleElementCompare: (element, expectedNumber) => condition(element, expectedNumber),
26
26
  context: { isNot, iteration },
27
- strategy: 'NewStrictMultipleElements',
28
27
  strictConfiguration: { allowArrayWithSingleElement: false }
29
28
  });
30
- }, isNot, { wait: commandOptions.wait, interval: commandOptions.interval });
29
+ }, isNot, { wait: options.wait, interval: options.interval });
31
30
  const expectedValues = expected ?? wrapExpectedWithArray(elements, actualHeight, expectedNumber);
32
- const message = enhanceError(elements, expectedValues, actualHeight, { isNot, isSome }, verb, expectation, '', commandOptions);
31
+ const message = enhanceError(elements, expectedValues, actualHeight, { isNot, isSome }, verb, expectation, '', options);
33
32
  const result = {
34
33
  pass,
35
34
  message: () => message
@@ -1,18 +1,21 @@
1
1
  import { toHaveAttributeAndValue } from './toHaveAttribute.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toHaveHref(el, expectedValue, options = DEFAULT_OPTIONS) {
4
+ const { matcherName = 'toHaveHref' } = this;
4
5
  await options.beforeAssertion?.({
5
- matcherName: 'toHaveHref',
6
+ matcherName,
6
7
  expectedValue,
7
8
  options,
8
9
  });
9
10
  const result = await toHaveAttributeAndValue.call(this, el, 'href', expectedValue, options);
10
11
  await options.afterAssertion?.({
11
- matcherName: 'toHaveHref',
12
+ matcherName,
12
13
  expectedValue,
13
14
  options,
14
15
  result
15
16
  });
16
17
  return result;
17
18
  }
18
- export const toHaveLink = toHaveHref;
19
+ export const toHaveLink = function toHaveLink(...args) {
20
+ return toHaveHref.call({ matcherName: 'toHaveLink', ...this }, ...args);
21
+ };
@@ -1,14 +1,15 @@
1
1
  import { toHaveAttributeAndValue } from './toHaveAttribute.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toHaveId(el, expectedValue, options = DEFAULT_OPTIONS) {
4
+ const { matcherName = 'toHaveId' } = this;
4
5
  await options.beforeAssertion?.({
5
- matcherName: 'toHaveId',
6
+ matcherName,
6
7
  expectedValue,
7
8
  options,
8
9
  });
9
10
  const result = await toHaveAttributeAndValue.call(this, el, 'id', expectedValue, options);
10
11
  await options.afterAssertion?.({
11
- matcherName: 'toHaveId',
12
+ matcherName,
12
13
  expectedValue,
13
14
  options,
14
15
  result