ftmocks-utils 1.5.5 → 1.5.6
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/package.json +1 -1
- package/src/compare-utils.js +83 -10
- package/src/match-utils.js +41 -21
- package/src/playwright-utils.js +1 -0
- package/src/rank-compare-utils.js +10 -2
package/package.json
CHANGED
package/src/compare-utils.js
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
const { clearNulls, processURL } = require("./common-utils");
|
|
2
2
|
const { FtJSON } = require("./json-utils");
|
|
3
3
|
|
|
4
|
+
/** @param {Record<string, string>|undefined|null} headers */
|
|
5
|
+
function getHeaderValueCaseInsensitive(headers, headerName) {
|
|
6
|
+
if (!headers || !headerName) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const lower = headerName.toLowerCase();
|
|
10
|
+
for (const key of Object.keys(headers)) {
|
|
11
|
+
if (key.toLowerCase() === lower) {
|
|
12
|
+
return headers[key];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseMatchHeadersList(testConfig) {
|
|
19
|
+
const raw = testConfig?.MATCH_HEADERS;
|
|
20
|
+
if (!raw || typeof raw !== "string") {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
return raw
|
|
24
|
+
.split(",")
|
|
25
|
+
.map((h) => h.trim())
|
|
26
|
+
.filter(Boolean);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Normalize fetch `options.headers` (plain object or Headers) to a plain object. */
|
|
30
|
+
function normalizeIncomingHeaders(headers) {
|
|
31
|
+
if (!headers) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
if (typeof headers.forEach === "function") {
|
|
35
|
+
const out = {};
|
|
36
|
+
headers.forEach((value, key) => {
|
|
37
|
+
out[key] = value;
|
|
38
|
+
});
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
if (typeof headers === "object") {
|
|
42
|
+
return { ...headers };
|
|
43
|
+
}
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* When MATCH_HEADERS is set, each listed header must match between the mock
|
|
49
|
+
* recording and the incoming request (case-insensitive name, value compared as strings).
|
|
50
|
+
*/
|
|
51
|
+
function incomingHeadersMatchMock(mock, incomingHeaders, testConfig) {
|
|
52
|
+
const names = parseMatchHeadersList(testConfig);
|
|
53
|
+
if (names.length === 0) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
const mockHeaders = mock.fileContent.request?.headers || {};
|
|
57
|
+
const incoming = incomingHeaders || {};
|
|
58
|
+
for (const name of names) {
|
|
59
|
+
const mockVal = getHeaderValueCaseInsensitive(mockHeaders, name);
|
|
60
|
+
const reqVal = getHeaderValueCaseInsensitive(incoming, name);
|
|
61
|
+
if (String(mockVal ?? "") !== String(reqVal ?? "")) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
4
68
|
const isUrlAndMethodSame = (req1, req2) => {
|
|
5
69
|
const url1 = new URL(`http://domain.com${req1.url}`);
|
|
6
70
|
const url2 = new URL(`http://domain.com${req2.url}`);
|
|
@@ -45,7 +109,7 @@ const isSameResponse = (req1, req2) => {
|
|
|
45
109
|
) {
|
|
46
110
|
matched = FtJSON.areJsonEqual(
|
|
47
111
|
FtJSON.parse(req1.response.content) || {},
|
|
48
|
-
FtJSON.parse(req2.response.content) || {}
|
|
112
|
+
FtJSON.parse(req2.response.content) || {},
|
|
49
113
|
);
|
|
50
114
|
// console.log('not matched at post Data 0', req1.postData, req2.postData);
|
|
51
115
|
} else if (
|
|
@@ -53,7 +117,7 @@ const isSameResponse = (req1, req2) => {
|
|
|
53
117
|
req2.response.content &&
|
|
54
118
|
!FtJSON.areJsonEqual(
|
|
55
119
|
FtJSON.parse(req1.response.content) || {},
|
|
56
|
-
FtJSON.parse(req2.response.content) || {}
|
|
120
|
+
FtJSON.parse(req2.response.content) || {},
|
|
57
121
|
)
|
|
58
122
|
) {
|
|
59
123
|
matched = false;
|
|
@@ -71,12 +135,12 @@ const isSameResponse = (req1, req2) => {
|
|
|
71
135
|
function compareMockToRequest(mock, req) {
|
|
72
136
|
const mockURL = processURL(
|
|
73
137
|
mock.fileContent.url,
|
|
74
|
-
mock.fileContent.ignoreParams
|
|
138
|
+
mock.fileContent.ignoreParams,
|
|
75
139
|
);
|
|
76
140
|
const reqURL = processURL(req.originalUrl, mock.fileContent.ignoreParams);
|
|
77
141
|
const isSameUrlAndMethod = isUrlAndMethodSame(
|
|
78
142
|
{ url: mockURL, method: mock.fileContent.method },
|
|
79
|
-
{ url: reqURL, method: req.method }
|
|
143
|
+
{ url: reqURL, method: req.method },
|
|
80
144
|
);
|
|
81
145
|
if (!isSameUrlAndMethod) {
|
|
82
146
|
return false;
|
|
@@ -90,20 +154,20 @@ function compareMockToRequest(mock, req) {
|
|
|
90
154
|
method: req.method,
|
|
91
155
|
postData: req.body,
|
|
92
156
|
url: reqURL,
|
|
93
|
-
}
|
|
157
|
+
},
|
|
94
158
|
);
|
|
95
159
|
}
|
|
96
160
|
|
|
97
|
-
function compareMockToFetchRequest(mock, fetchReq) {
|
|
161
|
+
function compareMockToFetchRequest(mock, fetchReq, testConfig) {
|
|
98
162
|
try {
|
|
99
163
|
const mockURL = processURL(
|
|
100
164
|
mock.fileContent.url,
|
|
101
|
-
mock.fileContent.ignoreParams
|
|
165
|
+
mock.fileContent.ignoreParams,
|
|
102
166
|
);
|
|
103
167
|
const reqURL = processURL(fetchReq.url, mock.fileContent.ignoreParams);
|
|
104
168
|
const isSameUrlAndMethod = isUrlAndMethodSame(
|
|
105
169
|
{ url: mockURL, method: mock.fileContent.method },
|
|
106
|
-
{ url: reqURL, method: fetchReq.options.method || "GET" }
|
|
170
|
+
{ url: reqURL, method: fetchReq.options.method || "GET" },
|
|
107
171
|
);
|
|
108
172
|
if (!isSameUrlAndMethod) {
|
|
109
173
|
return false;
|
|
@@ -111,7 +175,7 @@ function compareMockToFetchRequest(mock, fetchReq) {
|
|
|
111
175
|
const postData = mock.fileContent.request?.postData?.text
|
|
112
176
|
? FtJSON.parse(mock.fileContent.request?.postData?.text)
|
|
113
177
|
: mock.fileContent.request?.postData;
|
|
114
|
-
|
|
178
|
+
const sameBody = isSameRequest(
|
|
115
179
|
{ url: mockURL, method: mock.fileContent.method, postData },
|
|
116
180
|
{
|
|
117
181
|
method: fetchReq.options.method || "GET",
|
|
@@ -119,8 +183,13 @@ function compareMockToFetchRequest(mock, fetchReq) {
|
|
|
119
183
|
? FtJSON.parse(fetchReq.options.body)
|
|
120
184
|
: fetchReq.options.body,
|
|
121
185
|
url: reqURL,
|
|
122
|
-
}
|
|
186
|
+
},
|
|
123
187
|
);
|
|
188
|
+
if (!sameBody) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
const incomingHeaders = normalizeIncomingHeaders(fetchReq.options?.headers);
|
|
192
|
+
return incomingHeadersMatchMock(mock, incomingHeaders, testConfig);
|
|
124
193
|
} catch (e) {
|
|
125
194
|
console.error("error at compareMockToFetchRequest", mock, fetchReq);
|
|
126
195
|
console.error(e);
|
|
@@ -148,4 +217,8 @@ module.exports = {
|
|
|
148
217
|
compareMockToRequest,
|
|
149
218
|
compareMockToFetchRequest,
|
|
150
219
|
compareMockToMock,
|
|
220
|
+
getHeaderValueCaseInsensitive,
|
|
221
|
+
parseMatchHeadersList,
|
|
222
|
+
normalizeIncomingHeaders,
|
|
223
|
+
incomingHeadersMatchMock,
|
|
151
224
|
};
|
package/src/match-utils.js
CHANGED
|
@@ -38,7 +38,7 @@ function getMatchingMockData({
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
served = mock.fileContent.served;
|
|
41
|
-
return compareMockToFetchRequest(mock, { url, options });
|
|
41
|
+
return compareMockToFetchRequest(mock, { url, options }, testConfig);
|
|
42
42
|
}) || [];
|
|
43
43
|
let foundMock = matchedMocks.find((mock) => !mock.fileContent.served)
|
|
44
44
|
? matchedMocks.find((mock) => !mock.fileContent.served)
|
|
@@ -46,29 +46,41 @@ function getMatchingMockData({
|
|
|
46
46
|
|
|
47
47
|
if (!foundMock) {
|
|
48
48
|
foundMock = defaultMockData.find((tm) =>
|
|
49
|
-
compareMockToFetchRequest(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
compareMockToFetchRequest(
|
|
50
|
+
tm,
|
|
51
|
+
{
|
|
52
|
+
url,
|
|
53
|
+
options,
|
|
54
|
+
},
|
|
55
|
+
testConfig
|
|
56
|
+
)
|
|
53
57
|
);
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
if (!foundMock && mode !== "strict") {
|
|
57
61
|
const mockRanks = {};
|
|
58
62
|
testMockData.forEach((tm) => {
|
|
59
|
-
const rank = getCompareRankMockToFetchRequest(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
const rank = getCompareRankMockToFetchRequest(
|
|
64
|
+
tm,
|
|
65
|
+
{
|
|
66
|
+
url,
|
|
67
|
+
options,
|
|
68
|
+
},
|
|
69
|
+
testConfig
|
|
70
|
+
);
|
|
63
71
|
if (rank > 0) {
|
|
64
72
|
mockRanks[tm.id] = rank;
|
|
65
73
|
}
|
|
66
74
|
});
|
|
67
75
|
defaultMockData.forEach((tm) => {
|
|
68
|
-
const rank = getCompareRankMockToFetchRequest(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
const rank = getCompareRankMockToFetchRequest(
|
|
77
|
+
tm,
|
|
78
|
+
{
|
|
79
|
+
url,
|
|
80
|
+
options,
|
|
81
|
+
},
|
|
82
|
+
testConfig
|
|
83
|
+
);
|
|
72
84
|
if (rank > 0) {
|
|
73
85
|
mockRanks[tm.id] = rank;
|
|
74
86
|
}
|
|
@@ -157,20 +169,28 @@ function getMatchingMockDataV2({
|
|
|
157
169
|
const mockRanks = {};
|
|
158
170
|
matchedMocks.forEach((mockId) => {
|
|
159
171
|
const mock = testMockIdMap[mockId];
|
|
160
|
-
const rank = getCompareRankMockToFetchRequest(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
172
|
+
const rank = getCompareRankMockToFetchRequest(
|
|
173
|
+
mock,
|
|
174
|
+
{
|
|
175
|
+
url,
|
|
176
|
+
options,
|
|
177
|
+
},
|
|
178
|
+
testConfig
|
|
179
|
+
);
|
|
164
180
|
if (rank > 0) {
|
|
165
181
|
mockRanks[mock.id] = rank;
|
|
166
182
|
}
|
|
167
183
|
});
|
|
168
184
|
defaultMatchedMocks.forEach((mockId) => {
|
|
169
185
|
const mock = defaultMockIdMap[mockId];
|
|
170
|
-
const rank = getCompareRankMockToFetchRequest(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
186
|
+
const rank = getCompareRankMockToFetchRequest(
|
|
187
|
+
mock,
|
|
188
|
+
{
|
|
189
|
+
url,
|
|
190
|
+
options,
|
|
191
|
+
},
|
|
192
|
+
testConfig
|
|
193
|
+
);
|
|
174
194
|
if (rank > 0) {
|
|
175
195
|
mockRanks[mock.id] = rank;
|
|
176
196
|
}
|
package/src/playwright-utils.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const { clearNulls, charDifference, processURL } = require("./common-utils");
|
|
2
2
|
const { FtJSON } = require("./json-utils");
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
isUrlAndMethodSame,
|
|
5
|
+
incomingHeadersMatchMock,
|
|
6
|
+
normalizeIncomingHeaders,
|
|
7
|
+
} = require("./compare-utils");
|
|
4
8
|
|
|
5
9
|
const getSameRequestRank = (req1, req2) => {
|
|
6
10
|
let rank = 1;
|
|
@@ -21,7 +25,7 @@ const getSameRequestRank = (req1, req2) => {
|
|
|
21
25
|
return rank;
|
|
22
26
|
};
|
|
23
27
|
|
|
24
|
-
function getCompareRankMockToFetchRequest(mock, fetchReq) {
|
|
28
|
+
function getCompareRankMockToFetchRequest(mock, fetchReq, testConfig) {
|
|
25
29
|
try {
|
|
26
30
|
const mockURL = processURL(
|
|
27
31
|
mock.fileContent.url,
|
|
@@ -36,6 +40,10 @@ function getCompareRankMockToFetchRequest(mock, fetchReq) {
|
|
|
36
40
|
) {
|
|
37
41
|
return 0;
|
|
38
42
|
}
|
|
43
|
+
const incomingHeaders = normalizeIncomingHeaders(fetchReq.options?.headers);
|
|
44
|
+
if (!incomingHeadersMatchMock(mock, incomingHeaders, testConfig)) {
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
39
47
|
const postData = mock.fileContent.request?.postData?.text
|
|
40
48
|
? FtJSON.parse(mock.fileContent.request?.postData?.text)
|
|
41
49
|
: mock.fileContent.request?.postData;
|