expect-webdriverio 6.1.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +16 -5
  2. package/jest.d.ts +4 -2
  3. package/lib/api/index.js +2 -0
  4. package/lib/index.js +2 -0
  5. package/lib/matchers/asymmetrics/asymmetricsUtils.js +10 -0
  6. package/lib/matchers/asymmetrics/multiRemote.js +36 -0
  7. package/lib/matchers/browser/toHaveClipboardText.js +18 -8
  8. package/lib/matchers/browser/toHaveLocalStorageItem.js +22 -11
  9. package/lib/matchers/browser/toHaveTitle.js +16 -6
  10. package/lib/matchers/browser/toHaveUrl.js +16 -6
  11. package/lib/matchers/element/toHaveAttribute.js +2 -2
  12. package/lib/matchers/element/toHaveChildren.js +4 -3
  13. package/lib/matchers/element/toHaveComputedLabel.js +4 -4
  14. package/lib/matchers/element/toHaveComputedRole.js +4 -4
  15. package/lib/matchers/element/toHaveElementClass.js +4 -4
  16. package/lib/matchers/element/toHaveElementProperty.js +4 -4
  17. package/lib/matchers/element/toHaveHTML.js +4 -4
  18. package/lib/matchers/element/toHaveHeight.js +2 -2
  19. package/lib/matchers/element/toHaveSize.js +3 -3
  20. package/lib/matchers/element/toHaveStyle.js +3 -3
  21. package/lib/matchers/element/toHaveText.js +5 -5
  22. package/lib/matchers/element/toHaveValue.js +1 -1
  23. package/lib/matchers/element/toHaveWidth.js +2 -2
  24. package/lib/matchers/elements/toBeElementsArrayOfSize.js +70 -3
  25. package/lib/matchers/mock/toBeRequestedTimes.js +28 -5
  26. package/lib/matchers/mock/toBeRequestedWith.js +40 -12
  27. package/lib/matchers/snapshot.js +38 -7
  28. package/lib/tsconfig.build.tsbuildinfo +1 -1
  29. package/lib/util/elementsUtil.js +34 -6
  30. package/lib/util/executeBrowserCommand.js +37 -0
  31. package/lib/util/executeCommand.js +107 -19
  32. package/lib/util/formatMessage.js +80 -6
  33. package/lib/util/multiRemoteUtils.js +85 -0
  34. package/lib/util/numberOptionsUtil.js +6 -0
  35. package/lib/util/refetchElements.js +24 -2
  36. package/lib/utils.js +7 -2
  37. package/package.json +3 -3
  38. package/types/expect-webdriverio.d.ts +384 -39
@@ -1,11 +1,11 @@
1
1
  import { DEFAULT_OPTIONS } from '../../constants.js';
2
- import { compareTextOrArray, enhanceError, getFeatureFlagValue, isArrayContainingMatcher, waitUntil, } from '../../utils.js';
2
+ import { compareTextOrOneOf, enhanceError, getFeatureFlagValue, isArrayContainingMatcher, waitUntil, } from '../../utils.js';
3
3
  import { executeCommandWithStrategy } from '../../util/executeCommand.js';
4
4
  import { fillSingleExpectedForElementArray } from '../../util/elementsUtil.js';
5
5
  import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
6
6
  async function compareElement(el, expectedText, options) {
7
7
  const actualText = await el.getText();
8
- return compareTextOrArray(actualText, expectedText, options);
8
+ return compareTextOrOneOf(actualText, expectedText, options);
9
9
  }
10
10
  export async function toHaveText(received, expectedValue, options = DEFAULT_OPTIONS) {
11
11
  const { expectation = 'text', verb = 'have', isNot, matcherName = 'toHaveText' } = this;
@@ -16,7 +16,7 @@ export async function toHaveText(received, expectedValue, options = DEFAULT_OPTI
16
16
  });
17
17
  expectedValue = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
18
18
  const isNewStrictCompare = getFeatureFlagValue(options, 'useToHaveTextStrictMultiElementsCompareStrategy');
19
- const { success: pass, actual: actualText, subject: subject, context: { isSome } = {} } = await waitUntil(async (iteration) => {
19
+ const { success: pass, actual: actualText, subject: subject, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
20
20
  return await executeCommandWithStrategy({
21
21
  unresolvedElements: received,
22
22
  expectedValues: expectedValue,
@@ -32,8 +32,8 @@ export async function toHaveText(received, expectedValue, options = DEFAULT_OPTI
32
32
  if (isArrayContainingMatcher(expectedValue) && actualText === undefined) {
33
33
  throw new Error('toHaveText with arrayContaining requires an array of elements');
34
34
  }
35
- const expected = fillSingleExpectedForElementArray(subject, expectedValue);
36
- const message = enhanceError(subject, expected, actualText, { isNot, isSome }, verb, expectation, '', options);
35
+ const finalExpected = expected ?? fillSingleExpectedForElementArray(subject, expectedValue);
36
+ const message = enhanceError(subject, finalExpected, actualText, { isNot, isSome }, verb, expectation, '', options);
37
37
  const result = {
38
38
  pass,
39
39
  message: () => message
@@ -1,5 +1,5 @@
1
1
  import { toHaveElementProperty } from './toHaveElementProperty.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  export function toHaveValue(el, value, options = DEFAULT_OPTIONS) {
4
- return toHaveElementProperty.call(this, el, 'value', value, options);
4
+ return toHaveElementProperty.call({ ...this, allowObjectExpectedValue: false }, el, 'value', value, options);
5
5
  }
@@ -18,7 +18,7 @@ export async function toHaveWidth(received, expectedValue, options = DEFAULT_OPT
18
18
  options,
19
19
  });
20
20
  const { numberMatcher: expectedNumber, commandOptions } = validateNumberArrayAndExtractOptions(expectedValue, options);
21
- const { success: pass, actual: actualWidth, subject: elements, context: { isSome } = {} } = await waitUntil(async (iteration) => {
21
+ const { success: pass, actual: actualWidth, subject: elements, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
22
22
  return await executeCommandWithStrategy({
23
23
  unresolvedElements: received,
24
24
  expectedValues: expectedNumber,
@@ -28,7 +28,7 @@ export async function toHaveWidth(received, expectedValue, options = DEFAULT_OPT
28
28
  strictConfiguration: { allowArrayWithSingleElement: false }
29
29
  });
30
30
  }, isNot, { wait: commandOptions.wait, interval: commandOptions.interval });
31
- const expectedValues = wrapExpectedWithArray(elements, actualWidth, expectedNumber);
31
+ const expectedValues = expected ?? wrapExpectedWithArray(elements, actualWidth, expectedNumber);
32
32
  const message = enhanceError(elements, expectedValues, actualWidth, { isNot, isSome }, verb, expectation, '', commandOptions);
33
33
  const result = {
34
34
  pass,
@@ -1,8 +1,9 @@
1
1
  import { waitUntil, enhanceError, } from '../../utils.js';
2
- import { refetchElements, syncronizeElements } from '../../util/refetchElements.js';
2
+ import { refetchElements, synchronizeElementArray, syncronizeElements } from '../../util/refetchElements.js';
3
3
  import { DEFAULT_OPTIONS } from '../../constants.js';
4
4
  import { validateNumberAndExtractOptions } from '../../util/numberOptionsUtil.js';
5
- import { awaitElementArray, isStrictlyElementArray } from '../../util/elementsUtil.js';
5
+ import { awaitElementArray, isMultiRemoteElementArray, isMultiRemoteElements, isMultiRemoteElementsLike, isStrictlyElementArray } from '../../util/elementsUtil.js';
6
+ import { getElementsPerInstance, getGlobalMultiRemoteInstanceNames, hasSameInstanceNames, isGlobalBrowserSingleRemote, isMultiRemoteMatcher } from '../../util/multiRemoteUtils.js';
6
7
  export async function toBeElementsArrayOfSize(received, expectedValue, options = DEFAULT_OPTIONS) {
7
8
  const { expectation = 'elements array of size', verb = 'be', isNot, matcherName = 'toBeElementsArrayOfSize' } = this;
8
9
  await options.beforeAssertion?.({
@@ -10,8 +11,17 @@ export async function toBeElementsArrayOfSize(received, expectedValue, options =
10
11
  expectedValue,
11
12
  options,
12
13
  });
13
- const { numberMatcher: expectedNumber, commandOptions } = validateNumberAndExtractOptions(expectedValue, options);
14
14
  let { elements, other } = await awaitElementArray(received);
15
+ const awaitedMultiRemote = other ?? elements;
16
+ const isEmptyWithPerInstanceSizes = Array.isArray(awaitedMultiRemote) && awaitedMultiRemote.length === 0
17
+ && !isStrictlyElementArray(awaitedMultiRemote) && !isGlobalBrowserSingleRemote()
18
+ && getPerInstanceSizes(expectedValue) !== undefined;
19
+ if (isMultiRemoteElementsLike(awaitedMultiRemote) || isEmptyWithPerInstanceSizes) {
20
+ const result = await multiRemoteElementsArrayOfSize(awaitedMultiRemote, expectedValue, options, { context: this, verb, expectation });
21
+ await options.afterAssertion?.({ matcherName, expectedValue, options, result });
22
+ return result;
23
+ }
24
+ const { numberMatcher: expectedNumber, commandOptions } = validateNumberAndExtractOptions(expectedValue, options);
15
25
  const originalLength = elements ? elements.length : undefined;
16
26
  const { success: pass } = await waitUntil(async (iteration) => {
17
27
  if (!elements) {
@@ -43,3 +53,60 @@ export async function toBeElementsArrayOfSize(received, expectedValue, options =
43
53
  });
44
54
  return result;
45
55
  }
56
+ const multiRemoteElementsArrayOfSize = async (received, expectedValue, options, { context, verb, expectation }) => {
57
+ const { isNot } = context;
58
+ const perInstanceSizes = getPerInstanceSizes(expectedValue);
59
+ const instances = getMultiRemoteInstanceNames(received)
60
+ ?? getGlobalMultiRemoteInstanceNames()
61
+ ?? (perInstanceSizes ? Object.keys(perInstanceSizes) : []);
62
+ let commandOptions = options;
63
+ let expected;
64
+ let instanceMismatch = false;
65
+ if (perInstanceSizes) {
66
+ instanceMismatch = !hasSameInstanceNames(perInstanceSizes, instances);
67
+ expected = Object.fromEntries(Object.entries(perInstanceSizes).map(([name, value]) => [name, validateNumberAndExtractOptions(value, options).numberMatcher]));
68
+ }
69
+ else {
70
+ const validated = validateNumberAndExtractOptions(expectedValue, options);
71
+ commandOptions = validated.commandOptions;
72
+ expected = Object.fromEntries(instances.map((name) => [name, validated.numberMatcher]));
73
+ }
74
+ let refetchSource = received;
75
+ let elements = received;
76
+ let actual = countElementsPerInstance(elements, instances);
77
+ const { success: pass } = await waitUntil(async (iteration) => {
78
+ if (iteration > 0) {
79
+ elements = await refetchElements(refetchSource);
80
+ if (elements.length > 0 || !isMultiRemoteElements(refetchSource)) {
81
+ refetchSource = elements;
82
+ }
83
+ actual = countElementsPerInstance(elements, instances);
84
+ }
85
+ if (instanceMismatch) {
86
+ return { success: !!isNot, subject: refetchSource, actual, abort: true };
87
+ }
88
+ const matches = (name) => expected[name].asymmetricMatch(actual[name]);
89
+ const success = isNot ? instances.some(matches) : instances.every(matches);
90
+ return { success, subject: refetchSource, actual };
91
+ }, isNot, { wait: commandOptions.wait, interval: commandOptions.interval });
92
+ if (pass && elements !== received) {
93
+ synchronizeElementArray(received, elements);
94
+ }
95
+ const message = enhanceError(refetchSource, expected, actual, { isNot }, verb, expectation, '', commandOptions);
96
+ return { pass, message: () => message };
97
+ };
98
+ const getPerInstanceSizes = (value) => {
99
+ return isMultiRemoteMatcher(value) ? value.sample : undefined;
100
+ };
101
+ const getMultiRemoteInstanceNames = (elements) => {
102
+ const first = elements[0];
103
+ if (first) {
104
+ return first.instances;
105
+ }
106
+ const parent = isMultiRemoteElementArray(elements) ? elements.parent : undefined;
107
+ return parent?.instances;
108
+ };
109
+ const countElementsPerInstance = (elements, instances) => {
110
+ const elementsPerInstance = getElementsPerInstance(elements, instances);
111
+ return Object.fromEntries(instances.map((name) => [name, elementsPerInstance[name].length]));
112
+ };
@@ -1,6 +1,8 @@
1
1
  import { waitUntil, enhanceError } from '../../utils.js';
2
2
  import { DEFAULT_OPTIONS } from '../../constants.js';
3
3
  import { validateNumberAndExtractOptions } from '../../util/numberOptionsUtil.js';
4
+ import { awaitMocks, getMockInstanceNames, isMockArray } from '../../util/multiRemoteUtils.js';
5
+ import { formatMultiRemoteMocks, labelMultiRemoteValues } from '../../util/formatMessage.js';
4
6
  export async function toBeRequestedTimes(received, expectedValue, options = DEFAULT_OPTIONS) {
5
7
  const { verb = 'be', isNot, matcherName = 'toBeRequestedTimes', expectation = `called${typeof expectedValue === 'number' ? ' ' + expectedValue : ''} time${expectedValue !== 1 ? 's' : ''}`, } = this;
6
8
  await options.beforeAssertion?.({
@@ -9,11 +11,32 @@ export async function toBeRequestedTimes(received, expectedValue, options = DEFA
9
11
  options,
10
12
  });
11
13
  const { numberMatcher: expectedNumber, commandOptions } = validateNumberAndExtractOptions(expectedValue, options);
12
- const { success: pass, actual } = await waitUntil(async () => {
13
- const actual = received.calls.length;
14
- return { success: expectedNumber.asymmetricMatch(actual), subject: received, actual };
15
- }, isNot, { wait: commandOptions.wait, interval: commandOptions.interval });
16
- const message = enhanceError('mock', expectedNumber, actual, this, verb, expectation, '', commandOptions);
14
+ const mocks = await awaitMocks(received);
15
+ let message;
16
+ let pass;
17
+ if (isMockArray(mocks)) {
18
+ const instanceNames = getMockInstanceNames(mocks);
19
+ const { names } = instanceNames;
20
+ const label = (value) => instanceNames.isNamedByInstance ? labelMultiRemoteValues(value) : value;
21
+ const result = await waitUntil(async () => {
22
+ const actual = Object.fromEntries(mocks.map((mock, index) => [names[index], mock.calls.length]));
23
+ const matches = (name) => expectedNumber.asymmetricMatch(actual[name]);
24
+ const success = isNot ? names.some(matches) : names.every(matches);
25
+ return { success, subject: mocks, actual };
26
+ }, isNot, { wait: commandOptions.wait, interval: commandOptions.interval });
27
+ pass = result.success;
28
+ const expected = Object.fromEntries(names.map((name) => [name, expectedNumber]));
29
+ message = enhanceError(formatMultiRemoteMocks(instanceNames), label(expected), label(result.actual), this, verb, expectation, '', commandOptions);
30
+ }
31
+ else {
32
+ const mock = mocks;
33
+ const result = await waitUntil(async () => {
34
+ const actual = mock.calls.length;
35
+ return { success: expectedNumber.asymmetricMatch(actual), subject: mock, actual };
36
+ }, isNot, { wait: commandOptions.wait, interval: commandOptions.interval });
37
+ pass = result.success;
38
+ message = enhanceError('mock', expectedNumber, result.actual, this, verb, expectation, '', commandOptions);
39
+ }
17
40
  const result = {
18
41
  pass,
19
42
  message: () => message
@@ -1,6 +1,8 @@
1
1
  import { waitUntil, enhanceError, isAsymmetricMatcher, getAsymmetricMatcherValue } from '../../utils.js';
2
2
  import { equals } from '../../jasmineUtils.js';
3
3
  import { DEFAULT_OPTIONS } from '../../constants.js';
4
+ import { awaitMocks, getMockInstanceNames, isMockArray } from '../../util/multiRemoteUtils.js';
5
+ import { formatMultiRemoteMocks, labelMultiRemoteValues } from '../../util/formatMessage.js';
4
6
  const STR_LIMIT = 80;
5
7
  const KEY_LIMIT = 12;
6
8
  function reduceHeaders(headers) {
@@ -16,9 +18,42 @@ export async function toBeRequestedWith(received, expectedValue = {}, options =
16
18
  expectedValue,
17
19
  options,
18
20
  });
21
+ const parseCache = new Map();
22
+ const mocks = await awaitMocks(received);
23
+ let pass;
24
+ let message;
25
+ if (isMockArray(mocks)) {
26
+ const instanceNames = getMockInstanceNames(mocks);
27
+ const { names } = instanceNames;
28
+ const label = (value) => instanceNames.isNamedByInstance ? labelMultiRemoteValues(value) : value;
29
+ const results = await Promise.all(mocks.map((mock) => checkRequestedWith(mock, expectedValue, options, !!isNot, parseCache)));
30
+ pass = isNot ? results.some((result) => result.pass) : results.every((result) => result.pass);
31
+ const expected = Object.fromEntries(names.map((name) => [name, minifyRequestedWith(expectedValue)]));
32
+ const actual = Object.fromEntries(results.map((result, index) => [names[index], result.actual]));
33
+ const payloadNeverCollected = results.some((result) => !result.pass && result.payloadNeverCollected);
34
+ message = enhanceError(formatMultiRemoteMocks(instanceNames), label(expected), label(actual), this, verb, expectation, '', options)
35
+ + (!pass && !isNot && payloadNeverCollected ? payloadCollectionHint(expectedValue) : '');
36
+ }
37
+ else {
38
+ const result = await checkRequestedWith(mocks, expectedValue, options, !!isNot, parseCache);
39
+ pass = result.pass;
40
+ message = enhanceError('mock', minifyRequestedWith(expectedValue), result.actual, this, verb, expectation, '', options) + (!pass && !isNot && result.payloadNeverCollected ? payloadCollectionHint(expectedValue) : '');
41
+ }
42
+ const result = {
43
+ pass,
44
+ message: () => message
45
+ };
46
+ await options.afterAssertion?.({
47
+ matcherName,
48
+ expectedValue,
49
+ options,
50
+ result
51
+ });
52
+ return result;
53
+ }
54
+ const checkRequestedWith = async (received, expectedValue, options, isNot, parseCache) => {
19
55
  const hasPayloadExpectation = expectedValue.postData !== undefined || expectedValue.response !== undefined;
20
56
  const waitForPayloadOnNot = isNot && hasPayloadExpectation;
21
- const parseCache = new Map();
22
57
  let payloadNeverCollected = false;
23
58
  const { success: pass, actual } = await waitUntil(async () => {
24
59
  let lastCall;
@@ -50,19 +85,12 @@ export async function toBeRequestedWith(received, expectedValue = {}, options =
50
85
  abort: waitForPayloadOnNot && !hasPendingPayloadCandidate,
51
86
  };
52
87
  }, waitForPayloadOnNot ? false : isNot, { ...options, wait: (isNot && !waitForPayloadOnNot) ? 0 : options.wait });
53
- const message = enhanceError('mock', minifyRequestedWith(expectedValue), minifyRequestMock(actual, expectedValue, parseCache) || 'was not called', this, verb, expectation, '', options) + (!pass && !isNot && payloadNeverCollected ? payloadCollectionHint(expectedValue) : '');
54
- const result = {
88
+ return {
55
89
  pass,
56
- message: () => message
90
+ actual: minifyRequestMock(actual, expectedValue, parseCache) || 'was not called',
91
+ payloadNeverCollected,
57
92
  };
58
- await options.afterAssertion?.({
59
- matcherName,
60
- expectedValue,
61
- options,
62
- result
63
- });
64
- return result;
65
- }
93
+ };
66
94
  const payloadCollectionHint = (expectedValue) => {
67
95
  const fields = [
68
96
  expectedValue.postData !== undefined ? 'postData' : undefined,
@@ -2,6 +2,8 @@ import path from 'node:path';
2
2
  import { expect } from 'expect';
3
3
  import { stripSnapshotIndentation } from '@vitest/snapshot';
4
4
  import { SnapshotService } from '../snapshot.js';
5
+ import { awaitElementOrArray, isElementOrArrayOrMultiRemoteElementLike, isMultiRemoteElement, isMultiRemoteElementArray, isMultiRemoteElementsLike, isStrictlyElementArray } from '../util/elementsUtil.js';
6
+ import { getElementsPerInstance } from '../util/multiRemoteUtils.js';
5
7
  function returnSnapshotError(snapshotError) {
6
8
  try {
7
9
  expect(snapshotError.actual).toBe(snapshotError.expected);
@@ -38,12 +40,41 @@ function toMatchSnapshotAssert(received, message, inlineOptions) {
38
40
  return returnSnapshotError(e);
39
41
  }
40
42
  }
43
+ const isElementsToSnapshot = (received) => {
44
+ const isEmptyPlainArray = Array.isArray(received) && received.length === 0 && !isStrictlyElementArray(received) && !isMultiRemoteElementArray(received);
45
+ return isElementOrArrayOrMultiRemoteElementLike(received) && !isEmptyPlainArray;
46
+ };
47
+ const getOuterHTML = (element) => element.getHTML({ includeSelectorTag: true });
48
+ const getMultiRemoteOuterHTML = async (multiRemoteElements) => {
49
+ const isSingleElement = isMultiRemoteElement(multiRemoteElements);
50
+ if (!isSingleElement && multiRemoteElements.length === 0) {
51
+ return [];
52
+ }
53
+ const instances = [...(isSingleElement ? multiRemoteElements : multiRemoteElements[0]).instances].sort();
54
+ const elementsPerInstance = isSingleElement ? undefined : getElementsPerInstance(multiRemoteElements, instances);
55
+ const htmlPerInstance = Object.fromEntries(await Promise.all(instances.map(async (instance) => [
56
+ instance,
57
+ elementsPerInstance
58
+ ? await Promise.all(elementsPerInstance[instance].map(getOuterHTML))
59
+ : await getOuterHTML(multiRemoteElements.getInstance(instance))
60
+ ])));
61
+ const htmls = Object.values(htmlPerInstance);
62
+ return htmls.every((html) => JSON.stringify(html) === JSON.stringify(htmls[0])) ? htmls[0] : htmlPerInstance;
63
+ };
41
64
  async function toMatchSnapshotAsync(asyncReceived, message, inlineOptions) {
42
- let received = await asyncReceived;
43
- if (received && typeof received === 'object' && 'elementId' in received) {
44
- received = await received.getHTML({
45
- includeSelectorTag: true
46
- });
65
+ const { element, elements, multiRemoteSelector, other } = await awaitElementOrArray(await asyncReceived);
66
+ let received = other;
67
+ if (multiRemoteSelector) {
68
+ received = await getMultiRemoteOuterHTML(multiRemoteSelector);
69
+ }
70
+ else if (elements && isMultiRemoteElementsLike(elements)) {
71
+ received = await getMultiRemoteOuterHTML(elements);
72
+ }
73
+ else if (elements) {
74
+ received = await Promise.all(Array.from(elements).map(getOuterHTML));
75
+ }
76
+ else if (element) {
77
+ received = await getOuterHTML(element);
47
78
  }
48
79
  return toMatchSnapshotAssert(received, message, inlineOptions);
49
80
  }
@@ -53,8 +84,8 @@ function toMatchSnapshotHelper(received, message, inlineOptions) {
53
84
  throw new Error('Snapshot service is not initialized');
54
85
  }
55
86
  if (received && typeof received === 'object' &&
56
- ('elementId' in received ||
57
- 'then' in received)) {
87
+ ('then' in received ||
88
+ isElementsToSnapshot(received))) {
58
89
  return toMatchSnapshotAsync(received, message, inlineOptions);
59
90
  }
60
91
  return toMatchSnapshotAssert(received, message, inlineOptions);
@@ -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/api/index.ts","../src/matchers/snapshot.ts","../src/matchers/asymmetrics/asymmetricsUtils.ts","../src/matchers/asymmetrics/oneOf.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/toHaveComputedLabel.ts","../src/matchers/element/toHaveComputedRole.ts","../src/matchers/element/toHaveElementClass.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/matchers/modifiers/some.ts","../src/util/commandOptionsUtils.ts","../src/util/elementsUtil.ts","../src/util/executeCommand.ts","../src/util/formatMessage.ts","../src/util/globalSingleton.ts","../src/util/numberOptionsUtil.ts","../src/util/refetchElements.ts","../src/util/stringUtil.ts","../src/util/waitUntil.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/api/index.ts","../src/matchers/snapshot.ts","../src/matchers/asymmetrics/asymmetricsUtils.ts","../src/matchers/asymmetrics/multiRemote.ts","../src/matchers/asymmetrics/oneOf.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/toHaveComputedLabel.ts","../src/matchers/element/toHaveComputedRole.ts","../src/matchers/element/toHaveElementClass.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/matchers/modifiers/some.ts","../src/util/commandOptionsUtils.ts","../src/util/elementsUtil.ts","../src/util/executeBrowserCommand.ts","../src/util/executeCommand.ts","../src/util/formatMessage.ts","../src/util/globalSingleton.ts","../src/util/multiRemoteUtils.ts","../src/util/numberOptionsUtil.ts","../src/util/refetchElements.ts","../src/util/stringUtil.ts","../src/util/waitUntil.ts"],"version":"5.9.3"}
@@ -22,27 +22,33 @@ const isSelector = (obj) => {
22
22
  };
23
23
  export const isElementArray = (obj) => {
24
24
  return isSelector(obj)
25
- && 'foundWith' in obj;
25
+ && 'foundWith' in obj
26
+ && !isMultiRemote(obj);
26
27
  };
27
28
  export const isStrictlyElementArray = (obj) => {
28
29
  return isElementArray(obj)
29
30
  && Array.isArray(obj)
30
- && 'getElements' in obj;
31
+ && 'getElements' in obj
32
+ && !isMultiRemote(obj);
31
33
  };
32
34
  export const isElement = (obj) => {
33
35
  return isSelector(obj)
34
36
  && !Array.isArray(obj)
35
- && 'getElement' in obj;
37
+ && 'getElement' in obj
38
+ && !isMultiRemote(obj);
36
39
  };
37
40
  export const isElementArrayLike = (obj) => {
38
- return !!obj && (isStrictlyElementArray(obj) || (Array.isArray(obj) && obj.every(isElement)));
41
+ return !!obj && (isStrictlyElementArray(obj) || (Array.isArray(obj) && Array.prototype.every.call(obj, isElement)));
39
42
  };
40
43
  export const isArrayOfElement = (obj) => {
41
- return Array.isArray(obj) && obj.every(isElement);
44
+ return Array.isArray(obj) && !isMultiRemote(obj) && Array.prototype.every.call(obj, isElement);
42
45
  };
43
46
  export const isElementOrArrayLike = (obj) => {
44
47
  return !!obj && (isElement(obj) || isElementArrayLike(obj));
45
48
  };
49
+ export const isElementOrArrayOrMultiRemoteElementLike = (obj) => {
50
+ return !!obj && (isElement(obj) || isElementArrayLike(obj) || isMultiRemoteElementLike(obj));
51
+ };
46
52
  export const awaitElementOrArray = async (received) => {
47
53
  if (!received || typeof received !== 'object') {
48
54
  return { other: received };
@@ -51,9 +57,12 @@ export const awaitElementOrArray = async (received) => {
51
57
  if (awaitedElements instanceof Promise) {
52
58
  awaitedElements = await awaitedElements;
53
59
  }
54
- if (!isElementOrArrayLike(awaitedElements)) {
60
+ if (!isElementOrArrayOrMultiRemoteElementLike(awaitedElements)) {
55
61
  return { other: awaitedElements };
56
62
  }
63
+ if (isMultiRemoteElement(awaitedElements)) {
64
+ return { selector: awaitedElements, multiRemoteSelector: awaitedElements };
65
+ }
57
66
  if ('getElement' in awaitedElements) {
58
67
  const element = await awaitedElements.getElement();
59
68
  return { selector: element, element };
@@ -77,3 +86,22 @@ export const awaitElementArray = async (received) => {
77
86
  }
78
87
  return { elements: awaitedElements };
79
88
  };
89
+ const isMultiRemote = (obj) => {
90
+ return !!obj && typeof obj === 'object' && ('isMultiremote' in obj && obj.isMultiremote === true || 'isMultiRemote' in obj && obj.isMultiRemote === true);
91
+ };
92
+ export const isMultiRemoteElement = (obj) => {
93
+ return isMultiRemote(obj) && !Array.isArray(obj) && 'selector' in obj;
94
+ };
95
+ export const isMultiRemoteElements = (obj) => {
96
+ return Array.isArray(obj) && obj.length > 0 && !isMultiRemoteElementArray(obj)
97
+ && Array.prototype.every.call(obj, isMultiRemoteElement);
98
+ };
99
+ export const isMultiRemoteElementArray = (obj) => {
100
+ return !!obj && typeof obj === 'object' && 'isMultiremote' in obj && obj.isMultiremote === true && 'parent' in obj && 'foundWith' in obj && 'selector' in obj;
101
+ };
102
+ export const isMultiRemoteElementLike = (obj) => {
103
+ return isMultiRemoteElement(obj) || isMultiRemoteElementsLike(obj);
104
+ };
105
+ export const isMultiRemoteElementsLike = (obj) => {
106
+ return isMultiRemoteElementArray(obj) || isMultiRemoteElements(obj);
107
+ };
@@ -0,0 +1,37 @@
1
+ import { getPerInstanceValues, hasSameInstanceNames } from './multiRemoteUtils.js';
2
+ export async function executeBrowserCommand({ browser, expectedValue, compare, isNot = false, }) {
3
+ if (browser.isMultiremote) {
4
+ const { instances } = browser;
5
+ const perInstanceValues = getPerInstanceValues(expectedValue);
6
+ const expected = perInstanceValues ?? Object.fromEntries(instances.map((name) => [name, expectedValue]));
7
+ const forceFailure = (!!perInstanceValues && !hasSameInstanceNames(perInstanceValues, instances))
8
+ || Object.values(expected).some(Array.isArray);
9
+ const arrayResults = await Promise.all(instances.map(async (name) => {
10
+ let singleBrowser;
11
+ try {
12
+ singleBrowser = browser.getInstance(name);
13
+ }
14
+ catch {
15
+ return { success: false, actual: undefined, multiRemoteBrowserName: name };
16
+ }
17
+ const isExpected = name in expected;
18
+ const results = await compare(singleBrowser, forceFailure || !isExpected ? undefined : expected[name]);
19
+ return { ...results, multiRemoteBrowserName: name };
20
+ }));
21
+ const actual = arrayResults.reduce((acc, result) => {
22
+ acc[result.multiRemoteBrowserName] = result.actual;
23
+ return acc;
24
+ }, {});
25
+ if (forceFailure) {
26
+ return { actual, success: isNot, abort: true, subject: browser, expected };
27
+ }
28
+ const success = isNot ? arrayResults.some(result => result.success) : arrayResults.every(result => result.success);
29
+ return { actual, success, subject: browser, expected };
30
+ }
31
+ const forceFailure = getPerInstanceValues(expectedValue) !== undefined || Array.isArray(expectedValue);
32
+ const results = await compare(browser, forceFailure ? undefined : expectedValue);
33
+ if (forceFailure) {
34
+ return { ...results, success: isNot, abort: true, subject: browser, expected: expectedValue };
35
+ }
36
+ return { ...results, subject: browser, expected: expectedValue };
37
+ }