expect-webdriverio 5.7.0 → 6.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.
- package/README.md +16 -14
- package/lib/constants.js +4 -0
- package/lib/index.js +30 -18
- package/lib/matchers/asymmetrics/asymmetricsUtils.js +24 -0
- package/lib/matchers/asymmetrics/oneOf.js +51 -0
- package/lib/matchers/browser/toHaveClipboardText.js +8 -9
- package/lib/matchers/browser/toHaveLocalStorageItem.js +22 -14
- package/lib/matchers/browser/toHaveTitle.js +9 -10
- package/lib/matchers/browser/toHaveUrl.js +8 -9
- package/lib/matchers/element/toBeExisting.js +8 -3
- package/lib/matchers/element/toBeSelected.js +6 -13
- package/lib/matchers/element/toHaveAttribute.js +46 -45
- package/lib/matchers/element/toHaveChildren.js +30 -32
- package/lib/matchers/element/toHaveComputedLabel.js +21 -20
- package/lib/matchers/element/toHaveComputedRole.js +21 -20
- package/lib/matchers/element/toHaveElementClass.js +57 -0
- package/lib/matchers/element/toHaveElementProperty.js +40 -32
- package/lib/matchers/element/toHaveHTML.js +22 -23
- package/lib/matchers/element/toHaveHeight.js +23 -28
- package/lib/matchers/element/toHaveSize.js +16 -14
- package/lib/matchers/element/toHaveStyle.js +20 -14
- package/lib/matchers/element/toHaveText.js +26 -44
- package/lib/matchers/element/toHaveWidth.js +23 -28
- package/lib/matchers/elements/toBeElementsArrayOfSize.js +25 -30
- package/lib/matchers/mock/toBeRequestedTimes.js +12 -17
- package/lib/matchers/mock/toBeRequestedWith.js +8 -9
- package/lib/matchers/modifiers/some.js +15 -0
- package/lib/matchers.js +1 -1
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/lib/util/commandOptionsUtils.js +6 -0
- package/lib/util/elementsUtil.js +74 -4
- package/lib/util/executeCommand.js +94 -6
- package/lib/util/formatMessage.js +87 -32
- package/lib/util/numberOptionsUtil.js +92 -0
- package/lib/util/refetchElements.js +30 -5
- package/lib/util/stringUtil.js +12 -0
- package/lib/util/waitUntil.js +44 -0
- package/lib/utils.js +84 -91
- package/package.json +11 -10
- package/types/expect-webdriverio.d.ts +587 -134
- package/lib/matchers/element/toHaveClass.js +0 -61
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { AsymmetricMatcher } from 'expect';
|
|
2
|
+
import { isDefinedPlainObject } from './commandOptionsUtils.js';
|
|
3
|
+
export const isNumber = (value) => typeof value === 'number' && !isNaN(value);
|
|
4
|
+
export const isDefinedNotNumber = (value) => value !== undefined && !isNumber(value);
|
|
5
|
+
export const isDefinedNumberOrNonEmptyObject = (value) => typeof value === 'number' || (typeof value === 'object' && value !== null && !Array.isArray(value) && Object.keys(value).length > 0);
|
|
6
|
+
export function validateNumberAndExtractOptions(expectedValue, commandOptions, { supportDefaultAsGteThen1 } = {}) {
|
|
7
|
+
let defaultExpectedValue = undefined;
|
|
8
|
+
if (supportDefaultAsGteThen1 && (expectedValue === undefined || (isDefinedPlainObject(expectedValue) && expectedValue.eq === undefined && expectedValue.gte === undefined && expectedValue.lte === undefined))) {
|
|
9
|
+
defaultExpectedValue = new NumberMatcher({ gte: 1 });
|
|
10
|
+
}
|
|
11
|
+
else if (isNumber(expectedValue)) {
|
|
12
|
+
return { numberMatcher: new NumberMatcher({ eq: expectedValue }), commandOptions };
|
|
13
|
+
}
|
|
14
|
+
else if (!isDefinedNumberOrNonEmptyObject(expectedValue) || isDefinedNotNumber(expectedValue.eq) || isDefinedNotNumber(expectedValue.gte) || isDefinedNotNumber(expectedValue.lte)
|
|
15
|
+
|| (expectedValue.eq === undefined && expectedValue.gte === undefined && expectedValue.lte === undefined)) {
|
|
16
|
+
throw new Error(`Invalid NumberMatcher. Received: ${JSON.stringify(expectedValue)}`);
|
|
17
|
+
}
|
|
18
|
+
const { eq, gte, lte, ...restCommandOptions } = expectedValue ?? {};
|
|
19
|
+
if (isNumber(gte) && isNumber(lte) && gte > lte) {
|
|
20
|
+
throw new Error(`Invalid NumberMatcher range: 'gte' (${gte}) cannot be greater than 'lte' (${lte}).`);
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
numberMatcher: defaultExpectedValue ?? new NumberMatcher({ eq, gte, lte }),
|
|
24
|
+
commandOptions: { ...commandOptions, ...restCommandOptions }
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function validateNumberArrayAndExtractOptions(expectedValues, commandOptions, { supportDefaultAsGteThen1 } = {}) {
|
|
28
|
+
if (Array.isArray(expectedValues)) {
|
|
29
|
+
const allNumbers = expectedValues.map((value) => validateNumberAndExtractOptions(value, commandOptions, { supportDefaultAsGteThen1 }));
|
|
30
|
+
return { numberMatcher: allNumbers.map(({ numberMatcher }) => numberMatcher), commandOptions };
|
|
31
|
+
}
|
|
32
|
+
return validateNumberAndExtractOptions(expectedValues, commandOptions, { supportDefaultAsGteThen1 });
|
|
33
|
+
}
|
|
34
|
+
export class NumberMatcher extends AsymmetricMatcher {
|
|
35
|
+
sample;
|
|
36
|
+
constructor(sample) {
|
|
37
|
+
super(sample);
|
|
38
|
+
this.sample = sample;
|
|
39
|
+
}
|
|
40
|
+
asymmetricMatch(actual) {
|
|
41
|
+
if (actual === undefined) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (isNumber(this.sample)) {
|
|
45
|
+
return actual === this.sample;
|
|
46
|
+
}
|
|
47
|
+
if (isNumber(this.sample.eq)) {
|
|
48
|
+
return actual === this.sample.eq;
|
|
49
|
+
}
|
|
50
|
+
if (isNumber(this.sample.gte) && isNumber(this.sample.lte)) {
|
|
51
|
+
return actual >= this.sample.gte && actual <= this.sample.lte;
|
|
52
|
+
}
|
|
53
|
+
if (isNumber(this.sample.gte)) {
|
|
54
|
+
return actual >= this.sample.gte;
|
|
55
|
+
}
|
|
56
|
+
if (isNumber(this.sample.lte)) {
|
|
57
|
+
return actual <= this.sample.lte;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
toAsymmetricMatcher() {
|
|
62
|
+
if (isNumber(this.sample)) {
|
|
63
|
+
return `${this.sample}`;
|
|
64
|
+
}
|
|
65
|
+
if (isNumber(this.sample.eq)) {
|
|
66
|
+
return `${this.sample.eq}`;
|
|
67
|
+
}
|
|
68
|
+
if (isNumber(this.sample.gte) && isNumber(this.sample.lte)) {
|
|
69
|
+
return `>= ${this.sample.gte} && <= ${this.sample.lte}`;
|
|
70
|
+
}
|
|
71
|
+
if (isNumber(this.sample.gte)) {
|
|
72
|
+
return `>= ${this.sample.gte}`;
|
|
73
|
+
}
|
|
74
|
+
if (isNumber(this.sample.lte)) {
|
|
75
|
+
return `<= ${this.sample.lte}`;
|
|
76
|
+
}
|
|
77
|
+
return 'Incorrect number options provided';
|
|
78
|
+
}
|
|
79
|
+
toString() {
|
|
80
|
+
return this.toAsymmetricMatcher();
|
|
81
|
+
}
|
|
82
|
+
jasmineToString() {
|
|
83
|
+
return this.toString();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export const isEmptyOrLegacyNumberOptions = (value) => {
|
|
87
|
+
if (!isDefinedPlainObject(value)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const keys = Object.keys(value);
|
|
91
|
+
return keys.length === 0 || keys.filter((key) => !['eq', 'gte', 'lte'].includes(key)).length > 0;
|
|
92
|
+
};
|
|
@@ -1,10 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { isStrictlyElementArray } from './elementsUtil.js';
|
|
2
|
+
export const refetchElements = async (elements) => {
|
|
3
|
+
if (elements
|
|
4
|
+
&& isStrictlyElementArray(elements)
|
|
5
|
+
&& elements.parent && elements.foundWith && elements.foundWith in elements.parent) {
|
|
5
6
|
const browser = elements.parent;
|
|
6
7
|
const $$ = browser[elements.foundWith];
|
|
7
|
-
|
|
8
|
+
return await $$.call(browser, elements.selector, ...elements.props);
|
|
8
9
|
}
|
|
9
10
|
return elements;
|
|
10
11
|
};
|
|
12
|
+
export const syncronizeElements = async (subject, refetchedElements) => {
|
|
13
|
+
if (subject instanceof Promise) {
|
|
14
|
+
await syncronizeChainableElementArray(subject, refetchedElements);
|
|
15
|
+
}
|
|
16
|
+
else if (isStrictlyElementArray(subject)) {
|
|
17
|
+
synchronizeElementArray(subject, refetchedElements);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export const syncronizeChainableElementArray = async (subject, refetchedElements) => {
|
|
21
|
+
const awaitedSubject = await subject;
|
|
22
|
+
if (isStrictlyElementArray(awaitedSubject) && refetchedElements) {
|
|
23
|
+
synchronizeElementArray(await awaitedSubject.getElements(), refetchedElements);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export const synchronizeElementArray = (subject, refetchedElements) => {
|
|
27
|
+
subject.length = refetchedElements.length;
|
|
28
|
+
for (let index = 0; index < refetchedElements.length; index++) {
|
|
29
|
+
subject[index] = refetchedElements[index];
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
export const refreshElementArray = async (subject) => {
|
|
33
|
+
const refetchedElements = await refetchElements(subject);
|
|
34
|
+
synchronizeElementArray(subject, refetchedElements);
|
|
35
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DEFAULT_OPTIONS } from '../constants.js';
|
|
2
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3
|
+
export const waitUntil = async (condition, isNot = false, { wait = DEFAULT_OPTIONS.wait, interval = DEFAULT_OPTIONS.interval } = {}) => {
|
|
4
|
+
if (wait === 0) {
|
|
5
|
+
return await condition(0);
|
|
6
|
+
}
|
|
7
|
+
let error;
|
|
8
|
+
let result;
|
|
9
|
+
let iteration = 0;
|
|
10
|
+
try {
|
|
11
|
+
const start = Date.now();
|
|
12
|
+
while (true) {
|
|
13
|
+
if (Date.now() - start > wait) {
|
|
14
|
+
throw new Error('timeout');
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
result = await condition(iteration++);
|
|
18
|
+
const passed = isNot !== result?.success;
|
|
19
|
+
error = undefined;
|
|
20
|
+
if (passed) {
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
else if (result?.abort) {
|
|
24
|
+
return { ...result, success: isNot };
|
|
25
|
+
}
|
|
26
|
+
await sleep(interval);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
error = err;
|
|
30
|
+
await sleep(interval);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (error) {
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
return { ...result, success: !isNot };
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
if (error) {
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
return { ...result, subject: result?.subject, success: isNot, actual: result?.actual };
|
|
43
|
+
}
|
|
44
|
+
};
|
package/lib/utils.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import deepEql from 'deep-eql';
|
|
2
2
|
import { expect } from 'expect';
|
|
3
|
-
import { DEFAULT_OPTIONS } from './constants.js';
|
|
4
3
|
import { wrapExpectedWithArray } from './util/elementsUtil.js';
|
|
5
|
-
import {
|
|
6
|
-
import { enhanceError, enhanceErrorBe
|
|
7
|
-
|
|
4
|
+
import { executeCommandWithStrategy } from './util/executeCommand.js';
|
|
5
|
+
import { enhanceError, enhanceErrorBe } from './util/formatMessage.js';
|
|
6
|
+
import { waitUntil } from './util/waitUntil.js';
|
|
7
|
+
import { DEFAULT_FEATURE_FLAGS } from './constants.js';
|
|
8
|
+
import { OneOfMatcher } from './matchers/asymmetrics/oneOf.js';
|
|
8
9
|
export function isJasmineStringAsymmetricMatcher(expected) {
|
|
9
|
-
return isAsymmetricMatcher(expected) && 'expected' in expected;
|
|
10
|
+
return isAsymmetricMatcher(expected) && !('toAsymmetricMatcher' in expected) && 'jasmineToString' in expected && typeof expected.jasmineToString === 'function';
|
|
10
11
|
}
|
|
11
12
|
export function isAsymmetricMatcher(expected) {
|
|
12
13
|
return (typeof expected === 'object' &&
|
|
@@ -47,51 +48,21 @@ export function getAsymmetricMatcherValue(expected) {
|
|
|
47
48
|
}
|
|
48
49
|
return undefined;
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
await sleep(interval);
|
|
68
|
-
}
|
|
69
|
-
catch (err) {
|
|
70
|
-
error = err;
|
|
71
|
-
await sleep(interval);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (error) {
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
77
|
-
return !isNot;
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
if (error) {
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
83
|
-
return isNot;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
async function executeCommandBe(received, command, options) {
|
|
87
|
-
const { isNot, verb = 'be' } = this;
|
|
88
|
-
let el = await received?.getElement();
|
|
89
|
-
const pass = await waitUntil(async () => {
|
|
90
|
-
const result = await executeCommand.call(this, el, async (element) => ({ result: await command(element) }), options);
|
|
91
|
-
el = result.el;
|
|
92
|
-
return result.success;
|
|
93
|
-
}, isNot, options);
|
|
94
|
-
const message = enhanceErrorBe(el, { ...this, verb }, options);
|
|
51
|
+
async function executeCommandBe(received, command, options = {}) {
|
|
52
|
+
const { isNot, verb = 'be', allowEmptyElements = false } = this;
|
|
53
|
+
const { success: pass, actual, subject, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
54
|
+
return await executeCommandWithStrategy({
|
|
55
|
+
unresolvedElements: received,
|
|
56
|
+
expectedValues: true,
|
|
57
|
+
singleElementCompare: async (element) => {
|
|
58
|
+
const result = await command(element);
|
|
59
|
+
return { success: result, actual: result };
|
|
60
|
+
},
|
|
61
|
+
context: { isNot, iteration },
|
|
62
|
+
strictConfiguration: { allowEmptyElements },
|
|
63
|
+
});
|
|
64
|
+
}, isNot, { wait: options.wait, interval: options.interval });
|
|
65
|
+
const message = enhanceErrorBe(subject, actual, { ...this, verb, isSome }, options);
|
|
95
66
|
return {
|
|
96
67
|
pass,
|
|
97
68
|
message: () => message,
|
|
@@ -112,11 +83,30 @@ const compareNumbers = (actual, options = {}) => {
|
|
|
112
83
|
}
|
|
113
84
|
return false;
|
|
114
85
|
};
|
|
86
|
+
export const compareTextOrArray = (actualText, expectedTexts, options) => {
|
|
87
|
+
if (expectedTexts === undefined) {
|
|
88
|
+
return { actual: actualText, success: false };
|
|
89
|
+
}
|
|
90
|
+
if (Array.isArray(expectedTexts)) {
|
|
91
|
+
if (expectedTexts.some((expected) => expected instanceof OneOfMatcher)) {
|
|
92
|
+
throw new Error('OneOf is not supported in array under legacy behavior. Please enable `useToHaveTextStrictMultiElementsCompareStrategy` feature flag to use the new strict index based matching strategy with `expect.oneOf()`.');
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.warn('Array of expected values is deprecated. Please use `expect.oneOf()` asymmetric matcher to compare against multiple expected values. This will be removed in v8.0.0.');
|
|
96
|
+
expectedTexts = new OneOfMatcher(...expectedTexts).withOptions(options);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (expectedTexts instanceof OneOfMatcher) {
|
|
100
|
+
return { success: expectedTexts.asymmetricMatch(actualText), actual: actualText };
|
|
101
|
+
}
|
|
102
|
+
const compareResults = compareText(actualText, expectedTexts, options);
|
|
103
|
+
return { success: compareResults.success, actual: actualText };
|
|
104
|
+
};
|
|
115
105
|
export const compareText = (actual, expected, { ignoreCase = false, trim = true, containing = false, atStart = false, atEnd = false, atIndex, replace, }) => {
|
|
116
|
-
if (typeof actual !== 'string') {
|
|
106
|
+
if (typeof actual !== 'string' || expected === null || expected === undefined) {
|
|
117
107
|
return {
|
|
118
|
-
|
|
119
|
-
|
|
108
|
+
actual,
|
|
109
|
+
success: false,
|
|
120
110
|
};
|
|
121
111
|
}
|
|
122
112
|
if (trim) {
|
|
@@ -140,50 +130,50 @@ export const compareText = (actual, expected, { ignoreCase = false, trim = true,
|
|
|
140
130
|
if (isAsymmetricMatcher(expected)) {
|
|
141
131
|
const result = expected.asymmetricMatch(actual);
|
|
142
132
|
return {
|
|
143
|
-
|
|
144
|
-
result
|
|
133
|
+
actual,
|
|
134
|
+
success: result
|
|
145
135
|
};
|
|
146
136
|
}
|
|
147
137
|
if (expected instanceof RegExp) {
|
|
148
138
|
return {
|
|
149
|
-
|
|
150
|
-
|
|
139
|
+
actual,
|
|
140
|
+
success: !!actual.match(expected),
|
|
151
141
|
};
|
|
152
142
|
}
|
|
153
143
|
if (containing) {
|
|
154
144
|
return {
|
|
155
|
-
|
|
156
|
-
|
|
145
|
+
actual,
|
|
146
|
+
success: actual.includes(expected),
|
|
157
147
|
};
|
|
158
148
|
}
|
|
159
149
|
if (atStart) {
|
|
160
150
|
return {
|
|
161
|
-
|
|
162
|
-
|
|
151
|
+
actual,
|
|
152
|
+
success: actual.startsWith(expected),
|
|
163
153
|
};
|
|
164
154
|
}
|
|
165
155
|
if (atEnd) {
|
|
166
156
|
return {
|
|
167
|
-
|
|
168
|
-
|
|
157
|
+
actual,
|
|
158
|
+
success: actual.endsWith(expected),
|
|
169
159
|
};
|
|
170
160
|
}
|
|
171
161
|
if (atIndex) {
|
|
172
162
|
return {
|
|
173
|
-
|
|
174
|
-
|
|
163
|
+
actual,
|
|
164
|
+
success: actual.substring(atIndex, actual.length).startsWith(expected),
|
|
175
165
|
};
|
|
176
166
|
}
|
|
177
167
|
return {
|
|
178
|
-
|
|
179
|
-
|
|
168
|
+
actual,
|
|
169
|
+
success: actual === expected,
|
|
180
170
|
};
|
|
181
171
|
};
|
|
182
172
|
export const compareTextWithArray = (actual, expectedArray, { ignoreCase = false, trim = false, containing = false, atStart = false, atEnd = false, atIndex, replace, }) => {
|
|
183
173
|
if (typeof actual !== 'string') {
|
|
184
174
|
return {
|
|
185
|
-
|
|
186
|
-
|
|
175
|
+
actual,
|
|
176
|
+
success: false,
|
|
187
177
|
};
|
|
188
178
|
}
|
|
189
179
|
if (trim) {
|
|
@@ -207,7 +197,7 @@ export const compareTextWithArray = (actual, expectedArray, { ignoreCase = false
|
|
|
207
197
|
return item;
|
|
208
198
|
});
|
|
209
199
|
}
|
|
210
|
-
const
|
|
200
|
+
const hasFoundTextInArray = expectedArray.some((expected) => {
|
|
211
201
|
if (expected instanceof RegExp) {
|
|
212
202
|
return !!actual.match(expected);
|
|
213
203
|
}
|
|
@@ -229,24 +219,24 @@ export const compareTextWithArray = (actual, expectedArray, { ignoreCase = false
|
|
|
229
219
|
return actual === expected;
|
|
230
220
|
});
|
|
231
221
|
return {
|
|
232
|
-
|
|
233
|
-
|
|
222
|
+
actual,
|
|
223
|
+
success: hasFoundTextInArray,
|
|
234
224
|
};
|
|
235
225
|
};
|
|
236
226
|
export const compareObject = (actual, expected) => {
|
|
237
227
|
if (typeof actual !== 'object' || Array.isArray(actual)) {
|
|
238
228
|
return {
|
|
239
|
-
|
|
240
|
-
|
|
229
|
+
actual,
|
|
230
|
+
success: false,
|
|
241
231
|
};
|
|
242
232
|
}
|
|
243
233
|
return {
|
|
244
|
-
|
|
245
|
-
|
|
234
|
+
actual,
|
|
235
|
+
success: deepEql(actual, expected),
|
|
246
236
|
};
|
|
247
237
|
};
|
|
248
238
|
export const compareStyle = async (actualEl, style, { ignoreCase = false, trim = true, containing = false, atStart = false, atEnd = false, atIndex, replace, }) => {
|
|
249
|
-
let
|
|
239
|
+
let success = true;
|
|
250
240
|
const actual = {};
|
|
251
241
|
for (const key in style) {
|
|
252
242
|
const css = await actualEl.getCSSProperty(key);
|
|
@@ -261,42 +251,37 @@ export const compareStyle = async (actualEl, style, { ignoreCase = false, trim =
|
|
|
261
251
|
expectedVal = expectedVal.toLowerCase();
|
|
262
252
|
}
|
|
263
253
|
if (containing) {
|
|
264
|
-
|
|
254
|
+
success = actualVal.includes(expectedVal);
|
|
265
255
|
actual[key] = actualVal;
|
|
266
256
|
}
|
|
267
257
|
else if (atStart) {
|
|
268
|
-
|
|
258
|
+
success = actualVal.startsWith(expectedVal);
|
|
269
259
|
actual[key] = actualVal;
|
|
270
260
|
}
|
|
271
261
|
else if (atEnd) {
|
|
272
|
-
|
|
262
|
+
success = actualVal.endsWith(expectedVal);
|
|
273
263
|
actual[key] = actualVal;
|
|
274
264
|
}
|
|
275
265
|
else if (atIndex) {
|
|
276
|
-
|
|
266
|
+
success = actualVal.substring(atIndex, actualVal.length).startsWith(expectedVal);
|
|
277
267
|
actual[key] = actualVal;
|
|
278
268
|
}
|
|
279
269
|
else if (replace) {
|
|
280
270
|
const replacedActual = replaceActual(replace, actualVal);
|
|
281
|
-
|
|
271
|
+
success = replacedActual === expectedVal;
|
|
282
272
|
actual[key] = replacedActual;
|
|
283
273
|
}
|
|
284
274
|
else {
|
|
285
|
-
|
|
275
|
+
success = success && actualVal === expectedVal;
|
|
286
276
|
actual[key] = css.value;
|
|
287
277
|
}
|
|
288
278
|
}
|
|
289
279
|
return {
|
|
290
|
-
|
|
291
|
-
|
|
280
|
+
actual,
|
|
281
|
+
success,
|
|
292
282
|
};
|
|
293
283
|
};
|
|
294
|
-
|
|
295
|
-
this.verb = verb;
|
|
296
|
-
this.expectation = expectation;
|
|
297
|
-
return fn.apply(this, args);
|
|
298
|
-
}
|
|
299
|
-
export { aliasFn, compareNumbers, enhanceError, executeCommand, executeCommandBe, numberError, waitUntil, wrapExpectedWithArray };
|
|
284
|
+
export { compareNumbers, enhanceError, executeCommandBe, waitUntil, wrapExpectedWithArray };
|
|
300
285
|
function replaceActual(replace, actual) {
|
|
301
286
|
const hasMultipleReplacers = replace.every((r) => Array.isArray(r));
|
|
302
287
|
const replacers = hasMultipleReplacers
|
|
@@ -311,3 +296,11 @@ function replaceActual(replace, actual) {
|
|
|
311
296
|
}
|
|
312
297
|
return actual;
|
|
313
298
|
}
|
|
299
|
+
export const getFeatureFlagValue = ({ featureFlags }, featureFlag) => {
|
|
300
|
+
const providedFeatureFlagValue = featureFlags?.[featureFlag];
|
|
301
|
+
if (providedFeatureFlagValue !== undefined) {
|
|
302
|
+
return providedFeatureFlagValue;
|
|
303
|
+
}
|
|
304
|
+
return DEFAULT_FEATURE_FLAGS[featureFlag] ?? false;
|
|
305
|
+
};
|
|
306
|
+
export const toArray = (value) => value === undefined ? [] : Array.isArray(value) ? value : [value];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expect-webdriverio",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"author": "Mykola Grybyk <mykola.grybyk@gmail.com>",
|
|
5
5
|
"description": "WebdriverIO Assertion Library",
|
|
6
6
|
"license": "MIT",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"playgrounds:snapshots:update": "cd playgrounds && npm run snapshots:update"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@vitest/snapshot": "^4.1.
|
|
80
|
+
"@vitest/snapshot": "^4.1.10",
|
|
81
81
|
"deep-eql": "^5.0.2",
|
|
82
82
|
"expect": "^30.4.1",
|
|
83
83
|
"jest-matcher-utils": "^30.4.1"
|
|
@@ -88,19 +88,20 @@
|
|
|
88
88
|
"@types/jasmine": "^5.1.15",
|
|
89
89
|
"@types/jest": "^30.0.0",
|
|
90
90
|
"@types/mocha": "^10.0.10",
|
|
91
|
-
"@types/node": "^24.
|
|
92
|
-
"@vitest/coverage-v8": "^4.1.
|
|
91
|
+
"@types/node": "^24.13.3",
|
|
92
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
93
|
+
"@vitest/eslint-plugin": "^1.6.26",
|
|
93
94
|
"@wdio/eslint": "^0.1.3",
|
|
94
|
-
"@wdio/types": "^9.
|
|
95
|
-
"eslint": "^9.39.
|
|
95
|
+
"@wdio/types": "^9.30.1",
|
|
96
|
+
"eslint": "^9.39.5",
|
|
96
97
|
"husky": "^9.1.7",
|
|
97
|
-
"npm-run-all2": "^9.0.
|
|
98
|
-
"release-it": "^20.2.
|
|
98
|
+
"npm-run-all2": "^9.0.3",
|
|
99
|
+
"release-it": "^20.2.1",
|
|
99
100
|
"rimraf": "^6.1.3",
|
|
100
101
|
"strip-ansi": "^7.2.0",
|
|
101
102
|
"typescript": "^5.9.3",
|
|
102
|
-
"vitest": "^4.1.
|
|
103
|
-
"webdriverio": "^9.
|
|
103
|
+
"vitest": "^4.1.10",
|
|
104
|
+
"webdriverio": "^9.30.1"
|
|
104
105
|
},
|
|
105
106
|
"peerDependencies": {
|
|
106
107
|
"@wdio/globals": "^9.0.0",
|