expect-webdriverio 6.0.7 → 6.0.9
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/matchers/mock/toBeRequestedWith.js +102 -6
- package/lib/utils.js +55 -12
- package/package.json +9 -6
- package/types/expect-webdriverio.d.ts +6 -0
|
@@ -16,21 +16,41 @@ export async function toBeRequestedWith(received, expectedValue = {}, options =
|
|
|
16
16
|
expectedValue,
|
|
17
17
|
options,
|
|
18
18
|
});
|
|
19
|
+
const hasPayloadExpectation = expectedValue.postData !== undefined || expectedValue.response !== undefined;
|
|
20
|
+
const waitForPayloadOnNot = isNot && hasPayloadExpectation;
|
|
21
|
+
const parseCache = new Map();
|
|
22
|
+
let payloadNeverCollected = false;
|
|
19
23
|
const { success: pass, actual } = await waitUntil(async () => {
|
|
20
24
|
let lastCall;
|
|
25
|
+
let hasPendingPayloadCandidate = false;
|
|
21
26
|
for (const call of received.calls) {
|
|
22
27
|
lastCall = call;
|
|
23
|
-
|
|
28
|
+
const matchesNonPayloadCriteria = methodMatcher(call.request.method, expectedValue.method) &&
|
|
24
29
|
statusCodeMatcher(call.response.status, expectedValue.statusCode) &&
|
|
25
30
|
urlMatcher(call.request.url, expectedValue.url) &&
|
|
26
31
|
headersMatcher(reduceHeaders(call.request.headers), expectedValue.requestHeaders) &&
|
|
27
|
-
headersMatcher(reduceHeaders(call.response.headers), expectedValue.responseHeaders)
|
|
32
|
+
headersMatcher(reduceHeaders(call.response.headers), expectedValue.responseHeaders);
|
|
33
|
+
if (!matchesNonPayloadCriteria) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (bodyMatcher(call.postData, expectedValue.postData, parseCache) &&
|
|
37
|
+
bodyMatcher(call.body, expectedValue.response, parseCache)) {
|
|
28
38
|
return { success: true, subject: call, actual: call };
|
|
29
39
|
}
|
|
40
|
+
if ((expectedValue.postData !== undefined && call.postData === undefined) ||
|
|
41
|
+
(expectedValue.response !== undefined && call.body === undefined)) {
|
|
42
|
+
hasPendingPayloadCandidate = true;
|
|
43
|
+
}
|
|
30
44
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
payloadNeverCollected = hasPendingPayloadCandidate;
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
subject: lastCall,
|
|
49
|
+
actual: lastCall,
|
|
50
|
+
abort: waitForPayloadOnNot && !hasPendingPayloadCandidate,
|
|
51
|
+
};
|
|
52
|
+
}, 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) : '');
|
|
34
54
|
const result = {
|
|
35
55
|
pass,
|
|
36
56
|
message: () => message
|
|
@@ -43,6 +63,18 @@ export async function toBeRequestedWith(received, expectedValue = {}, options =
|
|
|
43
63
|
});
|
|
44
64
|
return result;
|
|
45
65
|
}
|
|
66
|
+
const payloadCollectionHint = (expectedValue) => {
|
|
67
|
+
const fields = [
|
|
68
|
+
expectedValue.postData !== undefined ? 'postData' : undefined,
|
|
69
|
+
expectedValue.response !== undefined ? 'response' : undefined,
|
|
70
|
+
].filter(Boolean).join(' and ');
|
|
71
|
+
return `
|
|
72
|
+
|
|
73
|
+
A request matched every other criterion, but its body was never collected, so ${fields} could not be compared.
|
|
74
|
+
This usually means either:
|
|
75
|
+
- webdriverio is older than v9.28.0, which did not populate \`mock.calls[].postData\` (upgrade to v9.28.0 or later), or
|
|
76
|
+
- body collection is turned off via \`maxSpyCollectedBodySize: 0\` in your config (it defaults to 10MB - remove the override or raise it).`;
|
|
77
|
+
};
|
|
46
78
|
const methodMatcher = (method, expected) => {
|
|
47
79
|
if (expected === undefined) {
|
|
48
80
|
return true;
|
|
@@ -87,7 +119,69 @@ const headersMatcher = (headers, expected) => {
|
|
|
87
119
|
}
|
|
88
120
|
return equals(headers, expected);
|
|
89
121
|
};
|
|
90
|
-
const
|
|
122
|
+
const bodyMatcher = (body, expected, parseCache) => {
|
|
123
|
+
if (typeof expected === 'undefined') {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
if (typeof expected === 'function') {
|
|
127
|
+
return expected(body);
|
|
128
|
+
}
|
|
129
|
+
if (typeof body === 'undefined') {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
let parsedBody = body;
|
|
133
|
+
if (body instanceof Buffer) {
|
|
134
|
+
parsedBody = body.toString();
|
|
135
|
+
}
|
|
136
|
+
if (typeof parsedBody === 'string' && isExpectedJsonLike(expected)) {
|
|
137
|
+
const parsed = parseJsonOnce(parsedBody, parseCache);
|
|
138
|
+
if (!parsed.ok) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
parsedBody = parsed.value;
|
|
142
|
+
}
|
|
143
|
+
return equals(parsedBody, expected);
|
|
144
|
+
};
|
|
145
|
+
const JSON_LIKE_ANY_CONSTRUCTORS = new Set([Number, Boolean, Array, Object]);
|
|
146
|
+
const isExpectedJsonLike = (expected) => {
|
|
147
|
+
if (typeof expected === 'undefined') {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const actualSample = isAsymmetricMatcher(expected)
|
|
151
|
+
? getAsymmetricMatcherValue(expected)
|
|
152
|
+
: expected;
|
|
153
|
+
return (actualSample === null ||
|
|
154
|
+
typeof actualSample === 'boolean' ||
|
|
155
|
+
typeof actualSample === 'number' ||
|
|
156
|
+
Array.isArray(actualSample) ||
|
|
157
|
+
JSON_LIKE_ANY_CONSTRUCTORS.has(actualSample) ||
|
|
158
|
+
(typeof actualSample === 'object' &&
|
|
159
|
+
actualSample !== null &&
|
|
160
|
+
actualSample instanceof RegExp === false));
|
|
161
|
+
};
|
|
162
|
+
const parseJsonOnce = (jsonString, cache) => {
|
|
163
|
+
const cached = cache.get(jsonString);
|
|
164
|
+
if (cached) {
|
|
165
|
+
return cached;
|
|
166
|
+
}
|
|
167
|
+
let result;
|
|
168
|
+
try {
|
|
169
|
+
result = { ok: true, value: JSON.parse(jsonString) };
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
result = { ok: false };
|
|
173
|
+
}
|
|
174
|
+
cache.set(jsonString, result);
|
|
175
|
+
return result;
|
|
176
|
+
};
|
|
177
|
+
const resolveBodyForDisplay = (raw, expectedForField, parseCache) => {
|
|
178
|
+
if (typeof raw !== 'string' || !isExpectedJsonLike(expectedForField)) {
|
|
179
|
+
return raw;
|
|
180
|
+
}
|
|
181
|
+
const parsed = parseJsonOnce(raw, parseCache);
|
|
182
|
+
return parsed.ok ? parsed.value : raw;
|
|
183
|
+
};
|
|
184
|
+
const minifyRequestMock = (requestMock, requestedWith = {}, parseCache = new Map()) => {
|
|
91
185
|
if (requestMock === undefined) {
|
|
92
186
|
return requestMock;
|
|
93
187
|
}
|
|
@@ -96,6 +190,8 @@ const minifyRequestMock = (requestMock, requestedWith) => {
|
|
|
96
190
|
method: requestMock.request.method,
|
|
97
191
|
requestHeaders: requestMock.request.headers,
|
|
98
192
|
responseHeaders: requestMock.response.headers,
|
|
193
|
+
postData: resolveBodyForDisplay(requestMock.postData, requestedWith.postData, parseCache),
|
|
194
|
+
response: resolveBodyForDisplay(requestMock.body, requestedWith.response, parseCache),
|
|
99
195
|
};
|
|
100
196
|
deleteUndefinedValues(r, requestedWith);
|
|
101
197
|
return minifyRequestedWith(r);
|
package/lib/utils.js
CHANGED
|
@@ -21,6 +21,12 @@ export function isStringContainingMatcherLike(expected) {
|
|
|
21
21
|
export function isInversedStringContainingMatcher(expected) {
|
|
22
22
|
return isStringContainingMatcherLike(expected) && expected.inverse === true;
|
|
23
23
|
}
|
|
24
|
+
export function isStringMatchingMatcherLike(expected) {
|
|
25
|
+
return !!expected && expected.constructor.name === 'StringMatching';
|
|
26
|
+
}
|
|
27
|
+
export function isInversedStringMatchingMatcher(expected) {
|
|
28
|
+
return isStringMatchingMatcherLike(expected) && expected.inverse === true;
|
|
29
|
+
}
|
|
24
30
|
export function getStringAsymmetricMatcherValue(expected) {
|
|
25
31
|
if ('expected' in expected) {
|
|
26
32
|
return expected.expected;
|
|
@@ -116,16 +122,30 @@ export const compareText = (actual, expected, { ignoreCase = false, trim = true,
|
|
|
116
122
|
actual = replaceActual(replace, actual);
|
|
117
123
|
}
|
|
118
124
|
if (ignoreCase) {
|
|
119
|
-
actual = actual.toLowerCase();
|
|
120
125
|
if (typeof expected === 'string') {
|
|
126
|
+
actual = actual.toLowerCase();
|
|
121
127
|
expected = expected.toLowerCase();
|
|
122
128
|
}
|
|
129
|
+
else if (expected instanceof RegExp) {
|
|
130
|
+
expected = withIgnoreCaseFlag(expected);
|
|
131
|
+
}
|
|
123
132
|
else if (isStringContainingMatcherLike(expected)) {
|
|
133
|
+
actual = actual.toLowerCase();
|
|
124
134
|
const sample = getStringAsymmetricMatcherValue(expected).toString().toLocaleLowerCase();
|
|
125
135
|
expected = (isInversedStringContainingMatcher(expected)
|
|
126
136
|
? expect.not.stringContaining(sample)
|
|
127
137
|
: expect.stringContaining(sample));
|
|
128
138
|
}
|
|
139
|
+
else if (isStringMatchingMatcherLike(expected)) {
|
|
140
|
+
const sample = getStringAsymmetricMatcherValue(expected);
|
|
141
|
+
const caseInsensitiveSample = withIgnoreCaseFlag(sample instanceof RegExp ? sample : new RegExp(sample));
|
|
142
|
+
expected = (isInversedStringMatchingMatcher(expected)
|
|
143
|
+
? expect.not.stringMatching(caseInsensitiveSample)
|
|
144
|
+
: expect.stringMatching(caseInsensitiveSample));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
actual = actual.toLowerCase();
|
|
148
|
+
}
|
|
129
149
|
}
|
|
130
150
|
if (isAsymmetricMatcher(expected)) {
|
|
131
151
|
const result = expected.asymmetricMatch(actual);
|
|
@@ -158,7 +178,7 @@ export const compareText = (actual, expected, { ignoreCase = false, trim = true,
|
|
|
158
178
|
success: actual.endsWith(expected),
|
|
159
179
|
};
|
|
160
180
|
}
|
|
161
|
-
if (atIndex) {
|
|
181
|
+
if (atIndex !== undefined) {
|
|
162
182
|
return {
|
|
163
183
|
actual,
|
|
164
184
|
success: actual.substring(atIndex, actual.length).startsWith(expected),
|
|
@@ -182,24 +202,38 @@ export const compareTextWithArray = (actual, expectedArray, { ignoreCase = false
|
|
|
182
202
|
if (Array.isArray(replace)) {
|
|
183
203
|
actual = replaceActual(replace, actual);
|
|
184
204
|
}
|
|
205
|
+
const actualOriginalCase = actual;
|
|
185
206
|
if (ignoreCase) {
|
|
186
207
|
actual = actual.toLowerCase();
|
|
187
208
|
expectedArray = expectedArray.map((item) => {
|
|
188
209
|
if (typeof item === 'string') {
|
|
189
210
|
return item.toLowerCase();
|
|
190
211
|
}
|
|
212
|
+
if (item instanceof RegExp) {
|
|
213
|
+
return withIgnoreCaseFlag(item);
|
|
214
|
+
}
|
|
191
215
|
if (isStringContainingMatcherLike(item)) {
|
|
192
216
|
const sample = getStringAsymmetricMatcherValue(item).toString().toLocaleLowerCase();
|
|
193
217
|
return (isInversedStringContainingMatcher(item)
|
|
194
218
|
? expect.not.stringContaining(sample)
|
|
195
219
|
: expect.stringContaining(sample));
|
|
196
220
|
}
|
|
221
|
+
if (isStringMatchingMatcherLike(item)) {
|
|
222
|
+
const sample = getStringAsymmetricMatcherValue(item);
|
|
223
|
+
const caseInsensitiveSample = withIgnoreCaseFlag(sample instanceof RegExp ? sample : new RegExp(sample));
|
|
224
|
+
return (isInversedStringMatchingMatcher(item)
|
|
225
|
+
? expect.not.stringMatching(caseInsensitiveSample)
|
|
226
|
+
: expect.stringMatching(caseInsensitiveSample));
|
|
227
|
+
}
|
|
197
228
|
return item;
|
|
198
229
|
});
|
|
199
230
|
}
|
|
200
231
|
const hasFoundTextInArray = expectedArray.some((expected) => {
|
|
201
232
|
if (expected instanceof RegExp) {
|
|
202
|
-
return !!
|
|
233
|
+
return !!actualOriginalCase.match(expected);
|
|
234
|
+
}
|
|
235
|
+
if (isStringMatchingMatcherLike(expected) && getStringAsymmetricMatcherValue(expected) instanceof RegExp) {
|
|
236
|
+
return expected.asymmetricMatch(actualOriginalCase);
|
|
203
237
|
}
|
|
204
238
|
if (isAsymmetricMatcher(expected)) {
|
|
205
239
|
return expected.asymmetricMatch(actual);
|
|
@@ -213,13 +247,13 @@ export const compareTextWithArray = (actual, expectedArray, { ignoreCase = false
|
|
|
213
247
|
if (atEnd) {
|
|
214
248
|
return actual.endsWith(expected);
|
|
215
249
|
}
|
|
216
|
-
if (atIndex) {
|
|
250
|
+
if (atIndex !== undefined) {
|
|
217
251
|
return actual.substring(atIndex, actual.length).startsWith(expected);
|
|
218
252
|
}
|
|
219
253
|
return actual === expected;
|
|
220
254
|
});
|
|
221
255
|
return {
|
|
222
|
-
actual,
|
|
256
|
+
actual: actualOriginalCase,
|
|
223
257
|
success: hasFoundTextInArray,
|
|
224
258
|
};
|
|
225
259
|
};
|
|
@@ -250,31 +284,33 @@ export const compareStyle = async (actualEl, style, { ignoreCase = false, trim =
|
|
|
250
284
|
actualVal = actualVal.toLowerCase();
|
|
251
285
|
expectedVal = expectedVal.toLowerCase();
|
|
252
286
|
}
|
|
287
|
+
let matches;
|
|
253
288
|
if (containing) {
|
|
254
|
-
|
|
289
|
+
matches = actualVal.includes(expectedVal);
|
|
255
290
|
actual[key] = actualVal;
|
|
256
291
|
}
|
|
257
292
|
else if (atStart) {
|
|
258
|
-
|
|
293
|
+
matches = actualVal.startsWith(expectedVal);
|
|
259
294
|
actual[key] = actualVal;
|
|
260
295
|
}
|
|
261
296
|
else if (atEnd) {
|
|
262
|
-
|
|
297
|
+
matches = actualVal.endsWith(expectedVal);
|
|
263
298
|
actual[key] = actualVal;
|
|
264
299
|
}
|
|
265
|
-
else if (atIndex) {
|
|
266
|
-
|
|
300
|
+
else if (atIndex !== undefined) {
|
|
301
|
+
matches = actualVal.substring(atIndex, actualVal.length).startsWith(expectedVal);
|
|
267
302
|
actual[key] = actualVal;
|
|
268
303
|
}
|
|
269
304
|
else if (replace) {
|
|
270
305
|
const replacedActual = replaceActual(replace, actualVal);
|
|
271
|
-
|
|
306
|
+
matches = replacedActual === expectedVal;
|
|
272
307
|
actual[key] = replacedActual;
|
|
273
308
|
}
|
|
274
309
|
else {
|
|
275
|
-
|
|
310
|
+
matches = actualVal === expectedVal;
|
|
276
311
|
actual[key] = css.value;
|
|
277
312
|
}
|
|
313
|
+
success = success && matches;
|
|
278
314
|
}
|
|
279
315
|
return {
|
|
280
316
|
actual,
|
|
@@ -282,6 +318,13 @@ export const compareStyle = async (actualEl, style, { ignoreCase = false, trim =
|
|
|
282
318
|
};
|
|
283
319
|
};
|
|
284
320
|
export { compareNumbers, enhanceError, executeCommandBe, waitUntil, wrapExpectedWithArray };
|
|
321
|
+
function withIgnoreCaseFlag(expected) {
|
|
322
|
+
const cloned = expected.ignoreCase
|
|
323
|
+
? new RegExp(expected.source, expected.flags)
|
|
324
|
+
: new RegExp(expected.source, `${expected.flags}i`);
|
|
325
|
+
cloned.lastIndex = expected.lastIndex;
|
|
326
|
+
return cloned;
|
|
327
|
+
}
|
|
285
328
|
function replaceActual(replace, actual) {
|
|
286
329
|
const hasMultipleReplacers = replace.every((r) => Array.isArray(r));
|
|
287
330
|
const replacers = hasMultipleReplacers
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expect-webdriverio",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.9",
|
|
4
4
|
"author": "Mykola Grybyk <mykola.grybyk@gmail.com>",
|
|
5
5
|
"description": "WebdriverIO Assertion Library",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,10 +45,13 @@
|
|
|
45
45
|
],
|
|
46
46
|
"./types": "./types/expect-global.d.ts",
|
|
47
47
|
"./expect-global": "./types/expect-global.d.ts",
|
|
48
|
-
"./api":
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
"./api": [
|
|
49
|
+
{
|
|
50
|
+
"types": "./types/expect-webdriverio.d.ts",
|
|
51
|
+
"import": "./lib/api/index.js"
|
|
52
|
+
},
|
|
53
|
+
"./lib/api/index.js"
|
|
54
|
+
]
|
|
52
55
|
},
|
|
53
56
|
"types": "./types/expect-webdriverio.d.ts",
|
|
54
57
|
"typeScriptVersion": "3.8.3",
|
|
@@ -113,7 +116,7 @@
|
|
|
113
116
|
"peerDependencies": {
|
|
114
117
|
"@wdio/globals": "^9.0.0",
|
|
115
118
|
"@wdio/logger": "^9.0.0",
|
|
116
|
-
"webdriverio": "^9.
|
|
119
|
+
"webdriverio": "^9.28.0"
|
|
117
120
|
},
|
|
118
121
|
"peerDependenciesMeta": {
|
|
119
122
|
"@wdio/globals": {
|
|
@@ -1236,11 +1236,17 @@ declare namespace ExpectWebdriverIO {
|
|
|
1236
1236
|
| ((headers: Record<string, string>) => boolean)
|
|
1237
1237
|
postData?:
|
|
1238
1238
|
| string
|
|
1239
|
+
| boolean
|
|
1240
|
+
| number
|
|
1241
|
+
| null
|
|
1239
1242
|
| ExpectWebdriverIO.JsonCompatible
|
|
1240
1243
|
| ExpectWebdriverIO.PartialMatcher<string | ExpectWebdriverIO.JsonCompatible>
|
|
1241
1244
|
| ((postData: string | undefined) => boolean)
|
|
1242
1245
|
response?:
|
|
1243
1246
|
| string
|
|
1247
|
+
| boolean
|
|
1248
|
+
| number
|
|
1249
|
+
| null
|
|
1244
1250
|
| ExpectWebdriverIO.JsonCompatible
|
|
1245
1251
|
| ExpectWebdriverIO.PartialMatcher<string | ExpectWebdriverIO.JsonCompatible>
|
|
1246
1252
|
| ((response: unknown) => boolean)
|