expect-webdriverio 5.6.4 → 5.6.5

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,10 +1,37 @@
1
1
  /// <reference types="./types/expect-webdriverio.d.ts"/>
2
2
 
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
+ */
13
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
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
+ }
28
+
3
29
  /**
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.
30
+ * Overrides the default WDIO expect specifically for Jasmine, since `expectAsync` is forced into `expect`, making all matchers fully asynchronous. This is not the case under Jest or Mocha.
31
+ * Using `jasmine.AsyncMatchers` pull on WdioMatchers above but also allow to using Jasmine's built-in matchers and also `withContext` matcher.
5
32
  */
6
33
  declare namespace ExpectWebdriverIO {
7
- interface Expect extends ExpectWebdriverIO.AsymmetricMatchers, ExpectLibInverse<ExpectWebdriverIO.InverseAsymmetricMatchers>, WdioExpect {
34
+ interface Expect {
8
35
  /**
9
36
  * The `expect` function is used every time you want to test a value.
10
37
  * You will rarely call `expect` by itself.
@@ -17,6 +44,6 @@ declare namespace ExpectWebdriverIO {
17
44
  *
18
45
  * @param actual The value to apply matchers against.
19
46
  */
20
- <T = unknown>(actual: T): ExpectWebdriverIO.MatchersAndInverse<Promise<void>, T>
47
+ <T = unknown>(actual: T): jasmine.AsyncMatchers<T, void>
21
48
  }
22
- }
49
+ }
package/lib/constants.js CHANGED
@@ -4,3 +4,14 @@ export const DEFAULT_OPTIONS = {
4
4
  beforeAssertion: async () => { },
5
5
  afterAssertion: async () => { },
6
6
  };
7
+ export const DEFAULT_OPTIONS_TO_BE_DISPLAYED = {
8
+ ...DEFAULT_OPTIONS,
9
+ withinViewport: false,
10
+ contentVisibilityAuto: true,
11
+ opacityProperty: true,
12
+ visibilityProperty: true
13
+ };
14
+ export const defaultOptionsList = [
15
+ DEFAULT_OPTIONS,
16
+ DEFAULT_OPTIONS_TO_BE_DISPLAYED
17
+ ];
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { expect as expectLib } from 'expect';
2
2
  import * as wdioMatchers from './matchers.js';
3
- import { DEFAULT_OPTIONS } from './constants.js';
3
+ import { DEFAULT_OPTIONS, defaultOptionsList } from './constants.js';
4
4
  import createSoftExpect from './softExpect.js';
5
5
  import { SoftAssertService } from './softAssert.js';
6
6
  export const matchers = new Map();
@@ -34,11 +34,13 @@ Object.defineProperty(expectWithSoft, 'clearSoftFailures', {
34
34
  });
35
35
  export const expect = expectWithSoft;
36
36
  export const getConfig = () => DEFAULT_OPTIONS;
37
- export const setDefaultOptions = (options = {}) => {
37
+ export const setDefaultOptions = (options) => {
38
38
  Object.entries(options).forEach(([key, value]) => {
39
- if (key in DEFAULT_OPTIONS) {
40
- DEFAULT_OPTIONS[key] = value;
41
- }
39
+ defaultOptionsList.forEach((option) => {
40
+ if (key in option) {
41
+ option[key] = value;
42
+ }
43
+ });
42
44
  });
43
45
  };
44
46
  export const setOptions = setDefaultOptions;
@@ -3,6 +3,7 @@ import { waitUntil, enhanceError, compareText } from '../../utils.js';
3
3
  import { DEFAULT_OPTIONS } from '../../constants.js';
4
4
  const log = logger('expect-webdriverio');
5
5
  export async function toHaveClipboardText(browser, expectedValue, options = DEFAULT_OPTIONS) {
6
+ const isNot = this.isNot;
6
7
  const { expectation = 'clipboard text', verb = 'have' } = this;
7
8
  await options.beforeAssertion?.({
8
9
  matcherName: 'toHaveClipboardText',
@@ -15,7 +16,7 @@ export async function toHaveClipboardText(browser, expectedValue, options = DEFA
15
16
  .catch((err) => log.warn(`Couldn't set clipboard permissions: ${err}`));
16
17
  actual = await browser.execute(() => window.navigator.clipboard.readText());
17
18
  return compareText(actual, expectedValue, options).result;
18
- }, options);
19
+ }, isNot, options);
19
20
  const message = enhanceError('browser', expectedValue, actual, this, verb, expectation, '', options);
20
21
  const result = {
21
22
  pass,
@@ -0,0 +1,36 @@
1
+ import { waitUntil, enhanceError, compareText } from '../../utils.js';
2
+ import { DEFAULT_OPTIONS } from '../../constants.js';
3
+ export async function toHaveLocalStorageItem(browser, key, expectedValue, options = DEFAULT_OPTIONS) {
4
+ const isNot = this.isNot;
5
+ const { expectation = 'localStorage item', verb = 'have' } = this;
6
+ await options.beforeAssertion?.({
7
+ matcherName: 'toHaveLocalStorageItem',
8
+ expectedValue: expectedValue ? [key, expectedValue] : key,
9
+ options,
10
+ });
11
+ let actual;
12
+ const pass = await waitUntil(async () => {
13
+ actual = await browser.execute((storageKey) => {
14
+ return localStorage.getItem(storageKey);
15
+ }, key);
16
+ if (expectedValue === undefined) {
17
+ return actual !== null;
18
+ }
19
+ if (actual === null) {
20
+ return false;
21
+ }
22
+ return compareText(actual, expectedValue, options).result;
23
+ }, isNot, options);
24
+ const message = enhanceError('browser', expectedValue !== undefined ? expectedValue : `localStorage item "${key}"`, actual, this, verb, expectation, key, options);
25
+ const result = {
26
+ pass,
27
+ message: () => message
28
+ };
29
+ await options.afterAssertion?.({
30
+ matcherName: 'toHaveLocalStorageItem',
31
+ expectedValue: expectedValue ? [key, expectedValue] : key,
32
+ options,
33
+ result
34
+ });
35
+ return result;
36
+ }
@@ -1,6 +1,7 @@
1
1
  import { waitUntil, enhanceError, compareText } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toHaveTitle(browser, expectedValue, options = DEFAULT_OPTIONS) {
4
+ const isNot = this.isNot;
4
5
  const { expectation = 'title', verb = 'have' } = this;
5
6
  await options.beforeAssertion?.({
6
7
  matcherName: 'toHaveTitle',
@@ -11,7 +12,7 @@ export async function toHaveTitle(browser, expectedValue, options = DEFAULT_OPTI
11
12
  const pass = await waitUntil(async () => {
12
13
  actual = await browser.getTitle();
13
14
  return compareText(actual, expectedValue, options).result;
14
- }, options);
15
+ }, isNot, options);
15
16
  const message = enhanceError('window', expectedValue, actual, this, verb, expectation, '', options);
16
17
  const result = {
17
18
  pass,
@@ -1,6 +1,7 @@
1
1
  import { waitUntil, enhanceError, compareText } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export async function toHaveUrl(browser, expectedValue, options = DEFAULT_OPTIONS) {
4
+ const isNot = this.isNot;
4
5
  const { expectation = 'url', verb = 'have' } = this;
5
6
  await options.beforeAssertion?.({
6
7
  matcherName: 'toHaveUrl',
@@ -11,7 +12,7 @@ export async function toHaveUrl(browser, expectedValue, options = DEFAULT_OPTION
11
12
  const pass = await waitUntil(async () => {
12
13
  actual = await browser.getUrl();
13
14
  return compareText(actual, expectedValue, options).result;
14
- }, options);
15
+ }, isNot, options);
15
16
  const message = enhanceError('window', expectedValue, actual, this, verb, expectation, '', options);
16
17
  const result = {
17
18
  pass,
@@ -1,19 +1,12 @@
1
1
  import { executeCommandBe } from '../../utils.js';
2
- import { DEFAULT_OPTIONS } from '../../constants.js';
3
- const DEFAULT_OPTIONS_DISPLAYED = {
4
- ...DEFAULT_OPTIONS,
5
- withinViewport: false,
6
- contentVisibilityAuto: true,
7
- opacityProperty: true,
8
- visibilityProperty: true
9
- };
10
- export async function toBeDisplayed(received, options = DEFAULT_OPTIONS_DISPLAYED) {
2
+ import { DEFAULT_OPTIONS_TO_BE_DISPLAYED } from '../../constants.js';
3
+ export async function toBeDisplayed(received, options = DEFAULT_OPTIONS_TO_BE_DISPLAYED) {
11
4
  this.expectation = this.expectation || 'displayed';
12
5
  await options.beforeAssertion?.({
13
6
  matcherName: 'toBeDisplayed',
14
7
  options,
15
8
  });
16
- const { withinViewport, contentVisibilityAuto, opacityProperty, visibilityProperty, ...commandOptions } = { ...DEFAULT_OPTIONS_DISPLAYED, ...options };
9
+ const { withinViewport, contentVisibilityAuto, opacityProperty, visibilityProperty, ...commandOptions } = { ...DEFAULT_OPTIONS_TO_BE_DISPLAYED, ...options };
17
10
  const result = await executeCommandBe.call(this, received, el => el?.isDisplayed({
18
11
  withinViewport,
19
12
  contentVisibilityAuto,
@@ -15,6 +15,7 @@ async function conditionAttrAndValue(el, attribute, value, options) {
15
15
  return compareText(attr, value, options);
16
16
  }
17
17
  export async function toHaveAttributeAndValue(received, attribute, value, options = DEFAULT_OPTIONS) {
18
+ const isNot = this.isNot;
18
19
  const { expectation = 'attribute', verb = 'have' } = this;
19
20
  let el = await received?.getElement();
20
21
  let attr;
@@ -23,7 +24,7 @@ export async function toHaveAttributeAndValue(received, attribute, value, option
23
24
  el = result.el;
24
25
  attr = result.values;
25
26
  return result.success;
26
- }, options);
27
+ }, isNot, options);
27
28
  const expected = wrapExpectedWithArray(el, attr, value);
28
29
  const message = enhanceError(el, expected, attr, this, verb, expectation, attribute, options);
29
30
  return {
@@ -31,15 +32,16 @@ export async function toHaveAttributeAndValue(received, attribute, value, option
31
32
  message: () => message
32
33
  };
33
34
  }
34
- async function toHaveAttributeFn(received, attribute, options) {
35
- const { expectation = 'attribute', verb = 'have', isNot } = this;
35
+ async function toHaveAttributeFn(received, attribute) {
36
+ const isNot = this.isNot;
37
+ const { expectation = 'attribute', verb = 'have' } = this;
36
38
  let el = await received?.getElement();
37
39
  const pass = await waitUntil(async () => {
38
40
  const result = await executeCommand.call(this, el, conditionAttr, {}, [attribute]);
39
41
  el = result.el;
40
42
  return result.success;
41
- }, options);
42
- const message = enhanceError(el, !isNot, pass, this, verb, expectation, attribute, options);
43
+ }, isNot, {});
44
+ const message = enhanceError(el, !isNot, pass, this, verb, expectation, attribute, {});
43
45
  return {
44
46
  pass,
45
47
  message: () => message
@@ -53,7 +55,7 @@ export async function toHaveAttribute(received, attribute, value, options = DEFA
53
55
  });
54
56
  const result = value !== undefined
55
57
  ? await toHaveAttributeAndValue.call(this, received, attribute, value, options)
56
- : await toHaveAttributeFn.call(this, received, attribute, options);
58
+ : await toHaveAttributeFn.call(this, received, attribute);
57
59
  await options.afterAssertion?.({
58
60
  matcherName: 'toHaveAttribute',
59
61
  expectedValue: [attribute, value],
@@ -16,6 +16,7 @@ async function condition(el, options) {
16
16
  };
17
17
  }
18
18
  export async function toHaveChildren(received, expectedValue, options = DEFAULT_OPTIONS) {
19
+ const isNot = this.isNot;
19
20
  const { expectation = 'children', verb = 'have' } = this;
20
21
  await options.beforeAssertion?.({
21
22
  matcherName: 'toHaveChildren',
@@ -32,7 +33,7 @@ export async function toHaveChildren(received, expectedValue, options = DEFAULT_
32
33
  el = result.el;
33
34
  children = result.values;
34
35
  return result.success;
35
- }, { ...numberOptions, ...options });
36
+ }, isNot, { ...numberOptions, ...options });
36
37
  const error = numberError(numberOptions);
37
38
  const expectedArray = wrapExpectedWithArray(el, children, error);
38
39
  const message = enhanceError(el, expectedArray, children, this, verb, expectation, '', numberOptions);
@@ -24,6 +24,7 @@ export function toHaveClass(...args) {
24
24
  return toHaveElementClass.call(this || {}, ...args);
25
25
  }
26
26
  export async function toHaveElementClass(received, expectedValue, options = DEFAULT_OPTIONS) {
27
+ const isNot = this.isNot;
27
28
  const { expectation = 'class', verb = 'have' } = this;
28
29
  await options.beforeAssertion?.({
29
30
  matcherName: 'toHaveElementClass',
@@ -38,7 +39,7 @@ export async function toHaveElementClass(received, expectedValue, options = DEFA
38
39
  el = result.el;
39
40
  attr = result.values;
40
41
  return result.success;
41
- }, options);
42
+ }, isNot, options);
42
43
  const message = enhanceError(el, wrapExpectedWithArray(el, attr, expectedValue), attr, this, verb, expectation, '', options);
43
44
  const result = {
44
45
  pass,
@@ -8,6 +8,7 @@ async function condition(el, label, options) {
8
8
  return compareText(actualLabel, label, options);
9
9
  }
10
10
  export async function toHaveComputedLabel(received, expectedValue, options = DEFAULT_OPTIONS) {
11
+ const isNot = this.isNot;
11
12
  const { expectation = 'computed label', verb = 'have' } = this;
12
13
  await options.beforeAssertion?.({
13
14
  matcherName: 'toHaveComputedLabel',
@@ -21,7 +22,7 @@ export async function toHaveComputedLabel(received, expectedValue, options = DEF
21
22
  el = result.el;
22
23
  actualLabel = result.values;
23
24
  return result.success;
24
- }, options);
25
+ }, isNot, options);
25
26
  const message = enhanceError(el, wrapExpectedWithArray(el, actualLabel, expectedValue), actualLabel, this, verb, expectation, '', options);
26
27
  const result = {
27
28
  pass,
@@ -8,6 +8,7 @@ async function condition(el, role, options) {
8
8
  return compareText(actualRole, role, options);
9
9
  }
10
10
  export async function toHaveComputedRole(received, expectedValue, options = DEFAULT_OPTIONS) {
11
+ const isNot = this.isNot;
11
12
  const { expectation = 'computed role', verb = 'have' } = this;
12
13
  await options.beforeAssertion?.({
13
14
  matcherName: 'toHaveComputedRole',
@@ -21,7 +22,7 @@ export async function toHaveComputedRole(received, expectedValue, options = DEFA
21
22
  el = result.el;
22
23
  actualRole = result.values;
23
24
  return result.success;
24
- }, options);
25
+ }, isNot, options);
25
26
  const message = enhanceError(el, wrapExpectedWithArray(el, actualRole, expectedValue), actualRole, this, verb, expectation, '', options);
26
27
  const result = {
27
28
  pass,
@@ -16,7 +16,8 @@ async function condition(el, property, value, options = DEFAULT_OPTIONS) {
16
16
  return compareText(prop, value, options);
17
17
  }
18
18
  export async function toHaveElementProperty(received, property, value, options = DEFAULT_OPTIONS) {
19
- const { expectation = 'property', verb = 'have', isNot } = this;
19
+ const isNot = this.isNot;
20
+ const { expectation = 'property', verb = 'have' } = this;
20
21
  await options.beforeAssertion?.({
21
22
  matcherName: 'toHaveElementProperty',
22
23
  expectedValue: [property, value],
@@ -29,7 +30,7 @@ export async function toHaveElementProperty(received, property, value, options =
29
30
  el = result.el;
30
31
  prop = result.values;
31
32
  return result.success;
32
- }, options);
33
+ }, isNot, options);
33
34
  let message;
34
35
  if (value === undefined) {
35
36
  message = enhanceError(el, !isNot, pass, this, verb, expectation, property, options);
@@ -8,6 +8,7 @@ async function condition(el, html, options) {
8
8
  return compareText(actualHTML, html, options);
9
9
  }
10
10
  export async function toHaveHTML(received, expectedValue, options = DEFAULT_OPTIONS) {
11
+ const isNot = this.isNot;
11
12
  const { expectation = 'HTML', verb = 'have' } = this;
12
13
  await options.beforeAssertion?.({
13
14
  matcherName: 'toHaveHTML',
@@ -25,7 +26,7 @@ export async function toHaveHTML(received, expectedValue, options = DEFAULT_OPTI
25
26
  el = result.el;
26
27
  actualHTML = result.values;
27
28
  return result.success;
28
- }, options);
29
+ }, isNot, options);
29
30
  const message = enhanceError(el, wrapExpectedWithArray(el, actualHTML, expectedValue), actualHTML, this, verb, expectation, '', options);
30
31
  const result = {
31
32
  pass,
@@ -8,6 +8,7 @@ async function condition(el, height, options) {
8
8
  };
9
9
  }
10
10
  export async function toHaveHeight(received, expectedValue, options = DEFAULT_OPTIONS) {
11
+ const isNot = this.isNot;
11
12
  const { expectation = 'height', verb = 'have' } = this;
12
13
  await options.beforeAssertion?.({
13
14
  matcherName: 'toHaveHeight',
@@ -31,7 +32,7 @@ export async function toHaveHeight(received, expectedValue, options = DEFAULT_OP
31
32
  el = result.el;
32
33
  actualHeight = result.values;
33
34
  return result.success;
34
- }, { ...numberOptions, ...options });
35
+ }, isNot, { ...numberOptions, ...options });
35
36
  const error = numberError(numberOptions);
36
37
  const message = enhanceError(el, error, actualHeight, this, verb, expectation, '', options);
37
38
  const result = {
@@ -5,6 +5,7 @@ async function condition(el, size) {
5
5
  return compareObject(actualSize, size);
6
6
  }
7
7
  export async function toHaveSize(received, expectedValue, options = DEFAULT_OPTIONS) {
8
+ const isNot = this.isNot;
8
9
  const { expectation = 'size', verb = 'have' } = this;
9
10
  await options.beforeAssertion?.({
10
11
  matcherName: 'toHaveSize',
@@ -18,7 +19,7 @@ export async function toHaveSize(received, expectedValue, options = DEFAULT_OPTI
18
19
  el = result.el;
19
20
  actualSize = result.values;
20
21
  return result.success;
21
- }, options);
22
+ }, isNot, options);
22
23
  const message = enhanceError(el, wrapExpectedWithArray(el, actualSize, expectedValue), actualSize, this, verb, expectation, '', options);
23
24
  const result = {
24
25
  pass,
@@ -4,6 +4,7 @@ async function condition(el, style, options) {
4
4
  return compareStyle(el, style, options);
5
5
  }
6
6
  export async function toHaveStyle(received, expectedValue, options = DEFAULT_OPTIONS) {
7
+ const isNot = this.isNot;
7
8
  const { expectation = 'style', verb = 'have' } = this;
8
9
  await options.beforeAssertion?.({
9
10
  matcherName: 'toHaveStyle',
@@ -17,7 +18,7 @@ export async function toHaveStyle(received, expectedValue, options = DEFAULT_OPT
17
18
  el = result.el;
18
19
  actualStyle = result.values;
19
20
  return result.success;
20
- }, options);
21
+ }, isNot, options);
21
22
  const message = enhanceError(el, wrapExpectedWithArray(el, actualStyle, expectedValue), actualStyle, this, verb, expectation, '', options);
22
23
  const result = {
23
24
  pass,
@@ -28,6 +28,7 @@ async function condition(el, text, options) {
28
28
  };
29
29
  }
30
30
  export async function toHaveText(received, expectedValue, options = DEFAULT_OPTIONS) {
31
+ const isNot = this.isNot;
31
32
  const { expectation = 'text', verb = 'have' } = this;
32
33
  await options.beforeAssertion?.({
33
34
  matcherName: 'toHaveText',
@@ -45,7 +46,7 @@ export async function toHaveText(received, expectedValue, options = DEFAULT_OPTI
45
46
  el = result.el;
46
47
  actualText = result.values;
47
48
  return result.success;
48
- }, options);
49
+ }, isNot, options);
49
50
  const message = enhanceError(el, wrapExpectedWithArray(el, actualText, expectedValue), actualText, this, verb, expectation, '', options);
50
51
  const result = {
51
52
  pass,
@@ -8,6 +8,7 @@ async function condition(el, width, options) {
8
8
  };
9
9
  }
10
10
  export async function toHaveWidth(received, expectedValue, options = DEFAULT_OPTIONS) {
11
+ const isNot = this.isNot;
11
12
  const { expectation = 'width', verb = 'have' } = this;
12
13
  await options.beforeAssertion?.({
13
14
  matcherName: 'toHaveWidth',
@@ -31,7 +32,7 @@ export async function toHaveWidth(received, expectedValue, options = DEFAULT_OPT
31
32
  el = result.el;
32
33
  actualWidth = result.values;
33
34
  return result.success;
34
- }, { ...numberOptions, ...options });
35
+ }, isNot, { ...numberOptions, ...options });
35
36
  const error = numberError(numberOptions);
36
37
  const message = enhanceError(el, error, actualWidth, this, verb, expectation, '', options);
37
38
  const result = {
@@ -2,6 +2,7 @@ import { waitUntil, enhanceError, compareNumbers, numberError } from '../../util
2
2
  import { refetchElements } from '../../util/refetchElements.js';
3
3
  import { DEFAULT_OPTIONS } from '../../constants.js';
4
4
  export async function toBeElementsArrayOfSize(received, expectedValue, options = DEFAULT_OPTIONS) {
5
+ const isNot = this.isNot;
5
6
  const { expectation = 'elements array of size', verb = 'be' } = this;
6
7
  await options.beforeAssertion?.({
7
8
  matcherName: 'toBeElementsArrayOfSize',
@@ -27,7 +28,7 @@ export async function toBeElementsArrayOfSize(received, expectedValue, options =
27
28
  }
28
29
  elements = await refetchElements(elements, numberOptions.wait, true);
29
30
  return false;
30
- }, { ...numberOptions, ...options });
31
+ }, isNot, { ...numberOptions, ...options });
31
32
  if (Array.isArray(received) && pass) {
32
33
  for (let index = originalLength; index < elements.length; index++) {
33
34
  received.push(elements[index]);
@@ -2,6 +2,7 @@ import { waitUntil, enhanceError, compareNumbers } from '../../utils.js';
2
2
  import { numberError } from '../../util/formatMessage.js';
3
3
  import { DEFAULT_OPTIONS } from '../../constants.js';
4
4
  export async function toBeRequestedTimes(received, expectedValue = {}, options = DEFAULT_OPTIONS) {
5
+ const isNot = this.isNot || false;
5
6
  const { expectation = `called${typeof expectedValue === 'number' ? ' ' + expectedValue : ''} time${expectedValue !== 1 ? 's' : ''}`, verb = 'be' } = this;
6
7
  await options.beforeAssertion?.({
7
8
  matcherName: 'toBeRequestedTimes',
@@ -15,7 +16,7 @@ export async function toBeRequestedTimes(received, expectedValue = {}, options =
15
16
  const pass = await waitUntil(async () => {
16
17
  actual = received.calls.length;
17
18
  return compareNumbers(actual, numberOptions);
18
- }, { ...numberOptions, ...options });
19
+ }, isNot, { ...numberOptions, ...options });
19
20
  const error = numberError(numberOptions);
20
21
  const message = enhanceError('mock', error, actual, this, verb, expectation, '', numberOptions);
21
22
  const result = {
@@ -10,7 +10,8 @@ function reduceHeaders(headers) {
10
10
  }, {});
11
11
  }
12
12
  export async function toBeRequestedWith(received, expectedValue = {}, options = DEFAULT_OPTIONS) {
13
- const { expectation = 'called with', verb = 'be', isNot } = this;
13
+ const isNot = this.isNot || false;
14
+ const { expectation = 'called with', verb = 'be' } = this;
14
15
  await options.beforeAssertion?.({
15
16
  matcherName: 'toBeRequestedWith',
16
17
  expectedValue,
@@ -29,7 +30,7 @@ export async function toBeRequestedWith(received, expectedValue = {}, options =
29
30
  }
30
31
  }
31
32
  return false;
32
- }, { ...options, wait: isNot ? 0 : options.wait });
33
+ }, isNot, { ...options, wait: isNot ? 0 : options.wait });
33
34
  const message = enhanceError('mock', minifyRequestedWith(expectedValue), minifyRequestMock(actual, expectedValue) || 'was not called', this, verb, expectation, '', options);
34
35
  const result = {
35
36
  pass,
package/lib/matchers.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './matchers/browser/toHaveClipboardText.js';
2
+ export * from './matchers/browser/toHaveLocalStorageItem.js';
2
3
  export * from './matchers/browser/toHaveTitle.js';
3
4
  export * from './matchers/browser/toHaveUrl.js';
4
5
  export * from './matchers/element/toBeClickable.js';
@@ -1 +1 @@
1
- {"root":["../src/constants.ts","../src/index.ts","../src/jasmineUtils.ts","../src/matchers.ts","../src/snapshot.ts","../src/softAssert.ts","../src/softAssertService.ts","../src/softExpect.ts","../src/types.ts","../src/utils.ts","../src/matchers/snapshot.ts","../src/matchers/browser/toHaveClipboardText.ts","../src/matchers/browser/toHaveTitle.ts","../src/matchers/browser/toHaveUrl.ts","../src/matchers/element/toBeClickable.ts","../src/matchers/element/toBeDisabled.ts","../src/matchers/element/toBeDisplayed.ts","../src/matchers/element/toBeDisplayedInViewport.ts","../src/matchers/element/toBeEnabled.ts","../src/matchers/element/toBeExisting.ts","../src/matchers/element/toBeFocused.ts","../src/matchers/element/toBeSelected.ts","../src/matchers/element/toHaveAttribute.ts","../src/matchers/element/toHaveChildren.ts","../src/matchers/element/toHaveClass.ts","../src/matchers/element/toHaveComputedLabel.ts","../src/matchers/element/toHaveComputedRole.ts","../src/matchers/element/toHaveElementProperty.ts","../src/matchers/element/toHaveHTML.ts","../src/matchers/element/toHaveHeight.ts","../src/matchers/element/toHaveHref.ts","../src/matchers/element/toHaveId.ts","../src/matchers/element/toHaveSize.ts","../src/matchers/element/toHaveStyle.ts","../src/matchers/element/toHaveText.ts","../src/matchers/element/toHaveValue.ts","../src/matchers/element/toHaveWidth.ts","../src/matchers/elements/toBeElementsArrayOfSize.ts","../src/matchers/mock/toBeRequested.ts","../src/matchers/mock/toBeRequestedTimes.ts","../src/matchers/mock/toBeRequestedWith.ts","../src/util/elementsUtil.ts","../src/util/executeCommand.ts","../src/util/formatMessage.ts","../src/util/refetchElements.ts"],"version":"5.9.3"}
1
+ {"root":["../src/constants.ts","../src/index.ts","../src/jasmineUtils.ts","../src/matchers.ts","../src/snapshot.ts","../src/softAssert.ts","../src/softAssertService.ts","../src/softExpect.ts","../src/types.ts","../src/utils.ts","../src/matchers/snapshot.ts","../src/matchers/browser/toHaveClipboardText.ts","../src/matchers/browser/toHaveLocalStorageItem.ts","../src/matchers/browser/toHaveTitle.ts","../src/matchers/browser/toHaveUrl.ts","../src/matchers/element/toBeClickable.ts","../src/matchers/element/toBeDisabled.ts","../src/matchers/element/toBeDisplayed.ts","../src/matchers/element/toBeDisplayedInViewport.ts","../src/matchers/element/toBeEnabled.ts","../src/matchers/element/toBeExisting.ts","../src/matchers/element/toBeFocused.ts","../src/matchers/element/toBeSelected.ts","../src/matchers/element/toHaveAttribute.ts","../src/matchers/element/toHaveChildren.ts","../src/matchers/element/toHaveClass.ts","../src/matchers/element/toHaveComputedLabel.ts","../src/matchers/element/toHaveComputedRole.ts","../src/matchers/element/toHaveElementProperty.ts","../src/matchers/element/toHaveHTML.ts","../src/matchers/element/toHaveHeight.ts","../src/matchers/element/toHaveHref.ts","../src/matchers/element/toHaveId.ts","../src/matchers/element/toHaveSize.ts","../src/matchers/element/toHaveStyle.ts","../src/matchers/element/toHaveText.ts","../src/matchers/element/toHaveValue.ts","../src/matchers/element/toHaveWidth.ts","../src/matchers/elements/toBeElementsArrayOfSize.ts","../src/matchers/mock/toBeRequested.ts","../src/matchers/mock/toBeRequestedTimes.ts","../src/matchers/mock/toBeRequestedWith.ts","../src/util/elementsUtil.ts","../src/util/executeCommand.ts","../src/util/formatMessage.ts","../src/util/refetchElements.ts"],"version":"5.9.3"}
package/lib/utils.js CHANGED
@@ -19,38 +19,50 @@ export function isAsymmetricMatcher(expected) {
19
19
  function isStringContainingMatcher(expected) {
20
20
  return isAsymmetricMatcher(expected) && ['StringContaining', 'StringNotContaining'].includes(expected.toString());
21
21
  }
22
- const waitUntil = async (condition, { wait = DEFAULT_OPTIONS.wait, interval = DEFAULT_OPTIONS.interval } = {}) => {
23
- const start = Date.now();
22
+ const waitUntil = async (condition, isNot = false, { wait = DEFAULT_OPTIONS.wait, interval = DEFAULT_OPTIONS.interval } = {}) => {
23
+ if (wait === 0) {
24
+ return await condition();
25
+ }
24
26
  let error;
25
- let result = false;
26
- do {
27
- try {
28
- result = await condition();
29
- error = undefined;
30
- if (result) {
31
- break;
27
+ try {
28
+ const start = Date.now();
29
+ while (true) {
30
+ if (Date.now() - start > wait) {
31
+ throw new Error('timeout');
32
+ }
33
+ try {
34
+ const result = isNot !== (await condition());
35
+ error = undefined;
36
+ if (result) {
37
+ break;
38
+ }
39
+ await sleep(interval);
40
+ }
41
+ catch (err) {
42
+ error = err;
43
+ await sleep(interval);
32
44
  }
33
45
  }
34
- catch (err) {
35
- error = err;
46
+ if (error) {
47
+ throw error;
36
48
  }
37
- if (Date.now() - start < wait) {
38
- await sleep(interval);
49
+ return !isNot;
50
+ }
51
+ catch {
52
+ if (error) {
53
+ throw error;
39
54
  }
40
- } while (Date.now() - start < wait);
41
- if (error) {
42
- throw error;
55
+ return isNot;
43
56
  }
44
- return result;
45
57
  };
46
58
  async function executeCommandBe(received, command, options) {
47
- const { expectation, verb = 'be' } = this;
59
+ const { isNot, expectation, verb = 'be' } = this;
48
60
  let el = await received?.getElement();
49
61
  const pass = await waitUntil(async () => {
50
62
  const result = await executeCommand.call(this, el, async (element) => ({ result: await command(element) }), options);
51
63
  el = result.el;
52
64
  return result.success;
53
- }, options);
65
+ }, isNot, options);
54
66
  const message = enhanceErrorBe(el, pass, this, verb, expectation, options);
55
67
  return {
56
68
  pass,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expect-webdriverio",
3
- "version": "5.6.4",
3
+ "version": "5.6.5",
4
4
  "author": "Mykola Grybyk <mykola.grybyk@gmail.com>",
5
5
  "description": "WebdriverIO Assertion Library",
6
6
  "license": "MIT",
@@ -38,6 +38,11 @@
38
38
  "types": "./jasmine.d.ts"
39
39
  }
40
40
  ],
41
+ "./jasmine-wdio-expect-async": [
42
+ {
43
+ "types": "./jasmine-wdio-expect-async.d.ts"
44
+ }
45
+ ],
41
46
  "./types": "./types/expect-global.d.ts",
42
47
  "./expect-global": "./types/expect-global.d.ts"
43
48
  },
@@ -65,7 +70,10 @@
65
70
  "ts:jasmine-async": "cd test-types/jasmine-async && tsc --project ./tsconfig.json",
66
71
  "checks:all": "npm run build && npm run compile && npm run tsc:root-types && npm run test && npm run ts",
67
72
  "watch": "npm run compile -- --watch",
68
- "prepare": "husky install"
73
+ "prepare": "husky install",
74
+ "playgrounds:setup": "for dir in playgrounds/*/; do cd \"$dir\" && npm install && cd ../..; done",
75
+ "playgrounds:checks:all": "for dir in playgrounds/*/; do cd \"$dir\" && npm run checks:all && cd ../..; done",
76
+ "playgrounds:snapshots:update": "cd playgrounds/mocha && npm run snapshots:update && cd ../jest && npm run snapshots:update && cd ../.."
69
77
  },
70
78
  "dependencies": {
71
79
  "@vitest/snapshot": "^4.0.16",
@@ -89,6 +89,15 @@ interface WdioBrowserMatchers<_R, ActualT>{
89
89
  * `WebdriverIO.Browser` -> `execute`
90
90
  */
91
91
  toHaveClipboardText: FnWhenBrowser<ActualT, (clipboardText: string | RegExp | ExpectWebdriverIO.PartialMatcher<string>, options?: ExpectWebdriverIO.StringOptions) => Promise<void>>
92
+
93
+ /**
94
+ * `WebdriverIO.Browser` -> `execute`
95
+ */
96
+ toHaveLocalStorageItem: FnWhenBrowser<ActualT, (
97
+ key: string,
98
+ expectedValue?: string | RegExp | ExpectWebdriverIO.PartialMatcher<string>,
99
+ options?: ExpectWebdriverIO.StringOptions
100
+ ) => Promise<void>>
92
101
  }
93
102
 
94
103
  /**
@@ -728,6 +737,7 @@ declare namespace ExpectWebdriverIO {
728
737
  * `true` to check if the element is invisible due to the value of its visibility property. `true` by default.
729
738
  * @default true
730
739
  */
740
+
731
741
  visibilityProperty?: boolean
732
742
  }
733
743