expect-webdriverio 6.1.0-alpha.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/jasmineUtils.js +4 -1
- package/lib/matchers/element/toHaveAttribute.js +1 -0
- package/lib/matchers/element/toHaveComputedLabel.js +1 -0
- package/lib/matchers/element/toHaveComputedRole.js +1 -0
- package/lib/matchers/element/toHaveElementClass.js +1 -0
- package/lib/matchers/element/toHaveElementProperty.js +3 -1
- package/lib/matchers/element/toHaveHTML.js +1 -0
- package/lib/matchers/element/toHaveText.js +5 -1
- package/lib/util/elementsUtil.js +3 -2
- package/lib/util/executeCommand.js +32 -3
- package/lib/util/formatMessage.js +2 -2
- package/lib/utils.js +13 -0
- package/package.json +1 -1
- package/types/expect-webdriverio.d.ts +2 -1
package/lib/jasmineUtils.js
CHANGED
|
@@ -11,7 +11,10 @@ function asymmetricMatch(a, b) {
|
|
|
11
11
|
if (asymmetricA && asymmetricB) {
|
|
12
12
|
return undefined;
|
|
13
13
|
}
|
|
14
|
-
const matchersUtil = {
|
|
14
|
+
const matchersUtil = {
|
|
15
|
+
equals,
|
|
16
|
+
contains: (actual, expected) => actual.some(value => equals(value, expected)),
|
|
17
|
+
};
|
|
15
18
|
if (asymmetricA) {
|
|
16
19
|
return a.asymmetricMatch(b, matchersUtil);
|
|
17
20
|
}
|
|
@@ -23,6 +23,7 @@ export async function toHaveAttributeAndValue(received, attribute, expectedValue
|
|
|
23
23
|
const { success: pass, actual: attr, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
24
24
|
return await executeCommandWithStrategy({
|
|
25
25
|
unresolvedElements: received,
|
|
26
|
+
supportsArrayContaining: true,
|
|
26
27
|
expectedValues: expectedValue,
|
|
27
28
|
singleElementCompare: (element, values) => {
|
|
28
29
|
return conditionAttributeValueMatchWithExpected(element, attribute, values, options);
|
|
@@ -17,6 +17,7 @@ export async function toHaveComputedLabel(received, expectedValue, options = DEF
|
|
|
17
17
|
const { success: pass, actual: actualLabel, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
18
18
|
return await executeCommandWithStrategy({
|
|
19
19
|
unresolvedElements: received,
|
|
20
|
+
supportsArrayContaining: true,
|
|
20
21
|
expectedValues: expectedValue,
|
|
21
22
|
singleElementCompare: (element, expectedValue) => singleElementCompare(element, expectedValue, options),
|
|
22
23
|
context: { isNot, iteration },
|
|
@@ -17,6 +17,7 @@ export async function toHaveComputedRole(received, expectedValue, options = DEFA
|
|
|
17
17
|
const { success: pass, actual: actualRole, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
18
18
|
return await executeCommandWithStrategy({
|
|
19
19
|
unresolvedElements: received,
|
|
20
|
+
supportsArrayContaining: true,
|
|
20
21
|
expectedValues: expectedValue,
|
|
21
22
|
singleElementCompare: (element, expectedValue) => singleElementCompare(element, expectedValue, options),
|
|
22
23
|
context: { isNot, iteration },
|
|
@@ -35,6 +35,7 @@ export async function toHaveElementClass(received, expectedValue, options = DEFA
|
|
|
35
35
|
const { success: pass, actual: attr, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
36
36
|
return await executeCommandWithStrategy({
|
|
37
37
|
unresolvedElements: received,
|
|
38
|
+
supportsArrayContaining: true,
|
|
38
39
|
expectedValues: expectedValue,
|
|
39
40
|
singleElementCompare: (element, expectedValue) => singleElementCompare(element, attribute, expectedValue, options),
|
|
40
41
|
context: { isNot, iteration },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { equals } from '../../jasmineUtils.js';
|
|
1
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
2
3
|
import { executeCommandWithStrategy } from '../../util/executeCommand.js';
|
|
3
4
|
import { expect } from 'expect';
|
|
@@ -9,7 +10,7 @@ async function condition(el, property, expectedValue, options = DEFAULT_OPTIONS)
|
|
|
9
10
|
const propertyValue = await el.getProperty(property);
|
|
10
11
|
if (propertyValue === null || propertyValue === undefined || (!(expectedValue instanceof RegExp) && typeof propertyValue !== 'string' && !asString)) {
|
|
11
12
|
if (isAsymmetricMatcher(expectedValue)) {
|
|
12
|
-
return { success:
|
|
13
|
+
return { success: equals(propertyValue, expectedValue), actual: propertyValue };
|
|
13
14
|
}
|
|
14
15
|
return { success: propertyValue === expectedValue, actual: propertyValue };
|
|
15
16
|
}
|
|
@@ -36,6 +37,7 @@ export async function toHaveElementProperty(received, property, value, options =
|
|
|
36
37
|
const { success: pass, actual: actualProppertyValue, subject: elements, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
37
38
|
return await executeCommandWithStrategy({
|
|
38
39
|
unresolvedElements: received,
|
|
40
|
+
supportsArrayContaining: true,
|
|
39
41
|
expectedValues: value,
|
|
40
42
|
singleElementCompare: (element, expectedValue) => {
|
|
41
43
|
return condition(element, property, expectedValue, options);
|
|
@@ -17,6 +17,7 @@ export async function toHaveHTML(received, expectedValue, options = DEFAULT_OPTI
|
|
|
17
17
|
const { success: pass, actual: actualHTML, subject: elements, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
18
18
|
const result = await executeCommandWithStrategy({
|
|
19
19
|
unresolvedElements: received,
|
|
20
|
+
supportsArrayContaining: true,
|
|
20
21
|
expectedValues: expectedValue,
|
|
21
22
|
singleElementCompare: (element, expectedValue) => singleElementCompare(element, expectedValue, options),
|
|
22
23
|
context: { isNot, iteration },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
2
|
-
import { compareTextOrArray, enhanceError, getFeatureFlagValue, waitUntil, } from '../../utils.js';
|
|
2
|
+
import { compareTextOrArray, 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';
|
|
@@ -20,6 +20,7 @@ export async function toHaveText(received, expectedValue, options = DEFAULT_OPTI
|
|
|
20
20
|
return await executeCommandWithStrategy({
|
|
21
21
|
unresolvedElements: received,
|
|
22
22
|
expectedValues: expectedValue,
|
|
23
|
+
supportsArrayContaining: 'arrayOnly',
|
|
23
24
|
singleElementCompare: (element, values) => {
|
|
24
25
|
return compareElement(element, values, options);
|
|
25
26
|
},
|
|
@@ -28,6 +29,9 @@ export async function toHaveText(received, expectedValue, options = DEFAULT_OPTI
|
|
|
28
29
|
strictConfiguration: { allowArrayWithSingleElement: true }
|
|
29
30
|
});
|
|
30
31
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
32
|
+
if (isArrayContainingMatcher(expectedValue) && actualText === undefined) {
|
|
33
|
+
throw new Error('toHaveText with arrayContaining requires an array of elements');
|
|
34
|
+
}
|
|
31
35
|
const expected = fillSingleExpectedForElementArray(subject, expectedValue);
|
|
32
36
|
const message = enhanceError(subject, expected, actualText, { isNot, isSome }, verb, expectation, '', options);
|
|
33
37
|
const result = {
|
package/lib/util/elementsUtil.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { isArrayContainingMatcher } from '../utils.js';
|
|
1
2
|
export const wrapExpectedWithArray = (elements, actual, expected) => {
|
|
2
|
-
if (Array.isArray(elements) && Array.isArray(actual) && !Array.isArray(expected)) {
|
|
3
|
+
if (Array.isArray(elements) && Array.isArray(actual) && !Array.isArray(expected) && !isArrayContainingMatcher(expected)) {
|
|
3
4
|
expected = Array(actual.length).fill(expected);
|
|
4
5
|
}
|
|
5
6
|
return expected;
|
|
6
7
|
};
|
|
7
8
|
export const fillSingleExpectedForElementArray = (subject, value) => {
|
|
8
|
-
if (isElementArrayLike(subject) && !Array.isArray(value)) {
|
|
9
|
+
if (isElementArrayLike(subject) && !Array.isArray(value) && !isArrayContainingMatcher(value)) {
|
|
9
10
|
const fillerlength = subject.length > 0 ? subject.length : 1;
|
|
10
11
|
return Array(fillerlength).fill(value);
|
|
11
12
|
}
|
|
@@ -1,15 +1,44 @@
|
|
|
1
|
+
import { equals } from '../jasmineUtils.js';
|
|
2
|
+
import { isArrayContainingMatcher } from '../utils.js';
|
|
1
3
|
import { isSomeWrapper } from '../matchers/modifiers/some.js';
|
|
2
4
|
import { awaitElementOrArray, isElement, isStrictlyElementArray } from './elementsUtil.js';
|
|
3
5
|
import { refreshElementArray } from './refetchElements.js';
|
|
4
|
-
export async function executeCommandWithStrategy({ unresolvedElements, expectedValues, singleElementCompare, context: { isNot, iteration }, strategy = 'NewStrictMultipleElements', strictConfiguration = { allowEmptyElements: false, allowArrayWithSingleElement: false } }) {
|
|
6
|
+
export async function executeCommandWithStrategy({ unresolvedElements, expectedValues, singleElementCompare, context: { isNot, iteration }, strategy = 'NewStrictMultipleElements', supportsArrayContaining = false, strictConfiguration = { allowEmptyElements: false, allowArrayWithSingleElement: false } }) {
|
|
5
7
|
const isSome = isSomeWrapper(unresolvedElements);
|
|
8
|
+
const actualReceived = isSome ? unresolvedElements.elements : unresolvedElements;
|
|
9
|
+
if (supportsArrayContaining && !isSome && isArrayContainingMatcher(expectedValues)) {
|
|
10
|
+
const { selector, elements, other } = await awaitElementOrArray(unresolvedElements);
|
|
11
|
+
if (elements) {
|
|
12
|
+
if (iteration > 0 && isStrictlyElementArray(elements)) {
|
|
13
|
+
await refreshElementArray(elements);
|
|
14
|
+
}
|
|
15
|
+
const settled = await Promise.allSettled(Array.from(elements).map(async (element, index) => {
|
|
16
|
+
return singleElementCompare(element, undefined, index);
|
|
17
|
+
}));
|
|
18
|
+
const actual = settled.map((result) => {
|
|
19
|
+
if (result.status === 'rejected') {
|
|
20
|
+
throw result.reason;
|
|
21
|
+
}
|
|
22
|
+
return result.value.actual;
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
subject: elements,
|
|
26
|
+
actual,
|
|
27
|
+
success: equals(actual, expectedValues),
|
|
28
|
+
abort: elements.length === 0 && !isStrictlyElementArray(elements),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (!isElement(selector) || supportsArrayContaining === 'arrayOnly') {
|
|
32
|
+
return { subject: selector ?? other, actual: undefined, success: !!isNot, abort: true };
|
|
33
|
+
}
|
|
34
|
+
return { subject: selector, ...await singleElementCompare(selector, expectedValues) };
|
|
35
|
+
}
|
|
6
36
|
if (strategy === 'LegacyLooseMultipleElements') {
|
|
7
37
|
if (isSome) {
|
|
8
38
|
throw new Error('some(elements) works only when enabling `useToHaveTextStrictMultiElementsCompareStrategy`');
|
|
9
39
|
}
|
|
10
|
-
return legacyMultipleElementResultsStrategy(
|
|
40
|
+
return legacyMultipleElementResultsStrategy(actualReceived, expectedValues, singleElementCompare, isNot);
|
|
11
41
|
}
|
|
12
|
-
const actualReceived = isSome ? unresolvedElements.elements : unresolvedElements;
|
|
13
42
|
return multipleElementResultsStrategy(actualReceived, expectedValues, singleElementCompare, { isNot, isSome, iteration }, strictConfiguration);
|
|
14
43
|
}
|
|
15
44
|
export const legacyMultipleElementResultsStrategy = async (unresolvedElements, expectedValues, singleElementCompare, _isNot) => {
|
|
@@ -56,10 +56,10 @@ export const enhanceError = (subject, expected, actual, context, verb, expectati
|
|
|
56
56
|
};
|
|
57
57
|
let diffString = '';
|
|
58
58
|
if (isJasmineStringAsymmetricMatcher(expected)) {
|
|
59
|
-
expected = expected.jasmineToString();
|
|
59
|
+
expected = expected.jasmineToString(stringify);
|
|
60
60
|
}
|
|
61
61
|
else if (isElementOrArrayLike(subject) && Array.isArray(expected)) {
|
|
62
|
-
expected = expected.map(item => isJasmineStringAsymmetricMatcher(item) ? item.jasmineToString() : item);
|
|
62
|
+
expected = expected.map(item => isJasmineStringAsymmetricMatcher(item) ? item.jasmineToString(stringify) : item);
|
|
63
63
|
}
|
|
64
64
|
if (isNotInLabel && isElementOrArrayLike(subject) && Array.isArray(expected) && Array.isArray(actual) && expected.length === actual.length) {
|
|
65
65
|
const { expectedFormatted, receivedFormatted } = printArrayWithMatchingItemInRed(expected, actual);
|
package/lib/utils.js
CHANGED
|
@@ -15,6 +15,19 @@ export function isAsymmetricMatcher(expected) {
|
|
|
15
15
|
'asymmetricMatch' in expected &&
|
|
16
16
|
!!expected.asymmetricMatch);
|
|
17
17
|
}
|
|
18
|
+
export function isArrayContainingMatcher(expected) {
|
|
19
|
+
if (!isAsymmetricMatcher(expected) || typeof expected.asymmetricMatch !== 'function') {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (typeof expected.toString === 'function' && /^Array(Not)?Containing$/.test(expected.toString())) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if ('jasmineToString' in expected && typeof expected.jasmineToString === 'function') {
|
|
26
|
+
const description = expected.jasmineToString(() => '');
|
|
27
|
+
return description === '<jasmine.arrayContaining()>';
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
18
31
|
export function isStringContainingMatcherLike(expected) {
|
|
19
32
|
return !!expected && expected.constructor.name === 'StringContaining';
|
|
20
33
|
}
|
package/package.json
CHANGED
|
@@ -550,6 +550,7 @@ interface WdioElementOrArrayMatchers<_R, ActualT = unknown> {
|
|
|
550
550
|
* // Check if an element array contains the specified text
|
|
551
551
|
* const elem = await $$('ul > li')
|
|
552
552
|
* await expect(elem).toHaveText(['Coffee', 'Tea', 'Milk'])
|
|
553
|
+
* await expect(elem).toHaveText(expect.arrayContaining(['Tea', 'Coffee']))
|
|
553
554
|
* ```
|
|
554
555
|
*/
|
|
555
556
|
toHaveText: FnWhenElementOrArrayLike<ActualT, {
|
|
@@ -853,7 +854,7 @@ type WdioOneOfAsymmetricMatcher<R> = ExpectWebdriverIO.PartialMatcher<R> & {
|
|
|
853
854
|
}
|
|
854
855
|
|
|
855
856
|
type JasmineBaseAsymmetricMatcher = {
|
|
856
|
-
jasmineToString(): string;
|
|
857
|
+
jasmineToString(pp?: (value: unknown) => string): string;
|
|
857
858
|
asymmetricMatch(other: unknown): boolean;
|
|
858
859
|
}
|
|
859
860
|
|