ftmocks-utils 1.1.6 → 1.1.8
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 +6 -2
- package/src/index.js +617 -231
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ftmocks-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "Util functions for FtMocks",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -16,5 +16,9 @@
|
|
|
16
16
|
"bugs": {
|
|
17
17
|
"url": "https://github.com/SodhanaLibrary/ftmocks-utils/issues"
|
|
18
18
|
},
|
|
19
|
-
"homepage": "https://github.com/SodhanaLibrary/ftmocks-utils#readme"
|
|
19
|
+
"homepage": "https://github.com/SodhanaLibrary/ftmocks-utils#readme",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"uui": "^1.0.7",
|
|
22
|
+
"uuid": "^11.1.0"
|
|
23
|
+
}
|
|
20
24
|
}
|
package/src/index.js
CHANGED
|
@@ -1,135 +1,165 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
const path = require('path')
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { v4: uuidv4 } = require("uuid");
|
|
5
4
|
|
|
6
|
-
const nameToFolder = name => {
|
|
7
|
-
return name.replaceAll(
|
|
5
|
+
const nameToFolder = (name) => {
|
|
6
|
+
return name.replaceAll(" ", "_");
|
|
8
7
|
};
|
|
9
8
|
|
|
10
|
-
const getMockDir = config => {
|
|
11
|
-
if(!path.isAbsolute(config.MOCK_DIR)) {
|
|
12
|
-
return path.resolve(
|
|
9
|
+
const getMockDir = (config) => {
|
|
10
|
+
if (!path.isAbsolute(config.MOCK_DIR)) {
|
|
11
|
+
return path.resolve(process.cwd(), config.MOCK_DIR);
|
|
13
12
|
}
|
|
14
13
|
return config.MOCK_DIR;
|
|
15
|
-
}
|
|
14
|
+
};
|
|
16
15
|
|
|
17
|
-
const getFallbackDir = config => {
|
|
18
|
-
if(config.FALLBACK_DIR && !path.isAbsolute(config.FALLBACK_DIR)) {
|
|
19
|
-
return path.resolve(
|
|
16
|
+
const getFallbackDir = (config) => {
|
|
17
|
+
if (config.FALLBACK_DIR && !path.isAbsolute(config.FALLBACK_DIR)) {
|
|
18
|
+
return path.resolve(process.cwd(), config.FALLBACK_DIR);
|
|
20
19
|
}
|
|
21
20
|
return config.FALLBACK_DIR;
|
|
22
|
-
}
|
|
21
|
+
};
|
|
23
22
|
|
|
24
|
-
const capitalizeHeader = header => {
|
|
23
|
+
const capitalizeHeader = (header) => {
|
|
25
24
|
return header
|
|
26
|
-
.split(
|
|
27
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
28
|
-
.join(
|
|
29
|
-
}
|
|
25
|
+
.split("-")
|
|
26
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
27
|
+
.join("-");
|
|
28
|
+
};
|
|
30
29
|
|
|
31
|
-
const capitalizeHeaders = headers => {
|
|
30
|
+
const capitalizeHeaders = (headers) => {
|
|
32
31
|
return Object.fromEntries(
|
|
33
|
-
Object.entries(headers).map(([key, value]) => [
|
|
32
|
+
Object.entries(headers).map(([key, value]) => [
|
|
33
|
+
capitalizeHeader(key),
|
|
34
|
+
value,
|
|
35
|
+
])
|
|
34
36
|
);
|
|
35
37
|
};
|
|
36
38
|
|
|
37
|
-
const getHeaders = headers => {
|
|
39
|
+
const getHeaders = (headers) => {
|
|
38
40
|
let res = null;
|
|
39
41
|
try {
|
|
40
|
-
res = new Map([
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
res = new Map([
|
|
43
|
+
...Object.entries(headers),
|
|
44
|
+
...Object.entries(capitalizeHeaders(headers)),
|
|
45
|
+
]);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.debug("error at getHeaders", e);
|
|
48
|
+
res = new Map([
|
|
49
|
+
["Content-Type", "application/json"],
|
|
50
|
+
["content-type", "application/json"],
|
|
51
|
+
]);
|
|
44
52
|
}
|
|
45
53
|
return Object.fromEntries(res);
|
|
46
|
-
}
|
|
54
|
+
};
|
|
47
55
|
|
|
48
56
|
const areJsonEqual = (jsonObj1, jsonObj2) => {
|
|
49
57
|
// Check if both are objects and not null
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
if (
|
|
59
|
+
typeof jsonObj1 === "object" &&
|
|
60
|
+
jsonObj1 !== null &&
|
|
61
|
+
typeof jsonObj2 === "object" &&
|
|
62
|
+
jsonObj2 !== null
|
|
63
|
+
) {
|
|
53
64
|
// Get the keys of both objects
|
|
54
65
|
const keys1 = Object.keys(jsonObj1);
|
|
55
66
|
const keys2 = Object.keys(jsonObj2);
|
|
56
|
-
|
|
67
|
+
|
|
57
68
|
// Check if the number of keys is different
|
|
58
69
|
if (keys1.length !== keys2.length) {
|
|
59
70
|
return false;
|
|
60
71
|
}
|
|
61
|
-
|
|
72
|
+
|
|
62
73
|
// Recursively check each key-value pair
|
|
63
74
|
for (let key of keys1) {
|
|
64
75
|
if (!keys2.includes(key) || !areJsonEqual(jsonObj1[key], jsonObj2[key])) {
|
|
65
76
|
return false;
|
|
66
77
|
}
|
|
67
78
|
}
|
|
68
|
-
|
|
79
|
+
|
|
69
80
|
return true;
|
|
70
81
|
} else {
|
|
71
82
|
// For non-object types, use strict equality comparison
|
|
72
83
|
return jsonObj1 === jsonObj2;
|
|
73
84
|
}
|
|
74
|
-
}
|
|
85
|
+
};
|
|
75
86
|
|
|
76
87
|
const getDefaultMockDataFromConfig = (testConfig) => {
|
|
77
|
-
const defaultPath = path.join(getMockDir(testConfig),
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
88
|
+
const defaultPath = path.join(getMockDir(testConfig), "default.json");
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const defaultData = fs.readFileSync(defaultPath, "utf8");
|
|
92
|
+
let parsedData = JSON.parse(defaultData);
|
|
93
|
+
|
|
94
|
+
// Read and attach mock data for each entry in parsedData
|
|
95
|
+
parsedData.forEach((entry) => {
|
|
96
|
+
const mockFilePath = path.join(
|
|
97
|
+
getMockDir(testConfig),
|
|
98
|
+
"defaultMocks",
|
|
99
|
+
`mock_${entry.id}.json`
|
|
100
|
+
);
|
|
101
|
+
try {
|
|
102
|
+
const mockData = fs.readFileSync(mockFilePath, "utf8");
|
|
103
|
+
entry.fileContent = JSON.parse(mockData);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(`Error reading mock data for ${entry.path}:`, error);
|
|
106
|
+
return entry; // Return the original entry if there's an error
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
return parsedData;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(`Error reading or parsing default.json:`, error);
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
};
|
|
100
115
|
|
|
101
116
|
// src/index.js
|
|
102
117
|
const loadMockDataFromConfig = (testConfig, _testName) => {
|
|
103
118
|
try {
|
|
104
119
|
let testName = _testName;
|
|
105
|
-
if(!testName) {
|
|
120
|
+
if (!testName) {
|
|
106
121
|
// Read the test ID from mockServer.config.json
|
|
107
|
-
const configPath = path.join(
|
|
108
|
-
|
|
122
|
+
const configPath = path.join(
|
|
123
|
+
getMockDir(testConfig),
|
|
124
|
+
"mockServer.config.json"
|
|
125
|
+
);
|
|
126
|
+
const configData = fs.readFileSync(configPath, "utf8");
|
|
109
127
|
const config = JSON.parse(configData);
|
|
110
128
|
testName = config.testName;
|
|
111
129
|
}
|
|
112
130
|
// Read the tests from testConfig
|
|
113
|
-
const mocksPath = path.join(
|
|
114
|
-
|
|
131
|
+
const mocksPath = path.join(
|
|
132
|
+
getMockDir(testConfig),
|
|
133
|
+
`test_${nameToFolder(testName)}`,
|
|
134
|
+
"_mock_list.json"
|
|
135
|
+
);
|
|
136
|
+
const mocksData = fs.readFileSync(mocksPath, "utf8");
|
|
115
137
|
const mocks = JSON.parse(mocksData);
|
|
116
138
|
|
|
117
|
-
mocks.forEach(mock => {
|
|
118
|
-
const fileContent = JSON.parse(
|
|
139
|
+
mocks.forEach((mock) => {
|
|
140
|
+
const fileContent = JSON.parse(
|
|
141
|
+
fs.readFileSync(
|
|
142
|
+
path.join(
|
|
143
|
+
getMockDir(testConfig),
|
|
144
|
+
`test_${nameToFolder(testName)}`,
|
|
145
|
+
`mock_${mock.id}.json`
|
|
146
|
+
),
|
|
147
|
+
"utf8"
|
|
148
|
+
)
|
|
149
|
+
);
|
|
119
150
|
mock.fileContent = fileContent;
|
|
120
151
|
});
|
|
121
152
|
|
|
122
|
-
|
|
123
153
|
return mocks;
|
|
124
154
|
} catch (error) {
|
|
125
|
-
console.debug(
|
|
155
|
+
console.debug("Error loading test data:", error.message);
|
|
126
156
|
return [];
|
|
127
157
|
}
|
|
128
158
|
};
|
|
129
159
|
|
|
130
|
-
const clearNulls = postData => {
|
|
131
|
-
Object.keys(postData || {}).forEach(key => {
|
|
132
|
-
if(postData[key] === null) {
|
|
160
|
+
const clearNulls = (postData) => {
|
|
161
|
+
Object.keys(postData || {}).forEach((key) => {
|
|
162
|
+
if (postData[key] === null) {
|
|
133
163
|
delete postData[key];
|
|
134
164
|
}
|
|
135
165
|
});
|
|
@@ -139,120 +169,188 @@ const isSameRequest = (req1, req2) => {
|
|
|
139
169
|
clearNulls(req1.postData);
|
|
140
170
|
clearNulls(req2.postData);
|
|
141
171
|
let matched = true;
|
|
142
|
-
if(req1.url !== req2.url) {
|
|
172
|
+
if (req1.url !== req2.url) {
|
|
143
173
|
matched = false;
|
|
144
|
-
} else if(req1.method?.toLowerCase() !== req2.method?.toLowerCase()) {
|
|
174
|
+
} else if (req1.method?.toLowerCase() !== req2.method?.toLowerCase()) {
|
|
145
175
|
matched = false;
|
|
146
|
-
} else if
|
|
147
|
-
|
|
148
|
-
|
|
176
|
+
} else if (
|
|
177
|
+
(!req1.postData && req2.postData) ||
|
|
178
|
+
(req1.postData && !req2.postData)
|
|
179
|
+
) {
|
|
180
|
+
matched = areJsonEqual(req1.postData || {}, req2.postData || {});
|
|
181
|
+
} else if (
|
|
182
|
+
req1.postData &&
|
|
183
|
+
req2.postData &&
|
|
184
|
+
!areJsonEqual(req1.postData, req2.postData)
|
|
185
|
+
) {
|
|
149
186
|
matched = false;
|
|
150
187
|
}
|
|
151
188
|
return matched;
|
|
152
|
-
}
|
|
189
|
+
};
|
|
153
190
|
|
|
154
|
-
const processURL = (url, ignoreParams=[]) => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
191
|
+
const processURL = (url, ignoreParams = []) => {
|
|
192
|
+
// Remove the hostname from the URL
|
|
193
|
+
const urlWithoutHost = url.replace(/^(https?:\/\/)?[^\/]+/, "");
|
|
194
|
+
const processedURL = new URL(`http://domain.com${urlWithoutHost}`);
|
|
195
|
+
const params = new URLSearchParams(processedURL.search);
|
|
196
|
+
if (ignoreParams?.length > 0) {
|
|
197
|
+
ignoreParams.forEach((ip) => {
|
|
198
|
+
params.delete(ip);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
params.sort();
|
|
202
|
+
return decodeURIComponent(`${processedURL.pathname}?${params}`);
|
|
203
|
+
};
|
|
168
204
|
|
|
169
205
|
function compareMockToRequest(mock, req) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
206
|
+
const mockURL = processURL(
|
|
207
|
+
mock.fileContent.url,
|
|
208
|
+
mock.fileContent.ignoreParams
|
|
209
|
+
);
|
|
210
|
+
const reqURL = processURL(req.originalUrl, mock.fileContent.ignoreParams);
|
|
211
|
+
const postData = mock.fileContent.request?.postData?.text
|
|
212
|
+
? JSON.parse(mock.fileContent.request?.postData?.text)
|
|
213
|
+
: mock.fileContent.request?.postData;
|
|
214
|
+
return isSameRequest(
|
|
215
|
+
{ url: mockURL, method: mock.fileContent.method, postData },
|
|
216
|
+
{
|
|
174
217
|
method: req.method,
|
|
175
218
|
postData: req.body,
|
|
176
219
|
url: reqURL,
|
|
177
|
-
}
|
|
220
|
+
}
|
|
221
|
+
);
|
|
178
222
|
}
|
|
179
223
|
|
|
180
224
|
function compareMockToFetchRequest(mock, fetchReq) {
|
|
181
|
-
try{
|
|
182
|
-
const mockURL = processURL(
|
|
225
|
+
try {
|
|
226
|
+
const mockURL = processURL(
|
|
227
|
+
mock.fileContent.url,
|
|
228
|
+
mock.fileContent.ignoreParams
|
|
229
|
+
);
|
|
183
230
|
const reqURL = processURL(fetchReq.url, mock.fileContent.ignoreParams);
|
|
184
|
-
const postData = mock.fileContent.request?.postData?.text
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
url:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
231
|
+
const postData = mock.fileContent.request?.postData?.text
|
|
232
|
+
? JSON.parse(mock.fileContent.request?.postData?.text)
|
|
233
|
+
: mock.fileContent.request?.postData;
|
|
234
|
+
return isSameRequest(
|
|
235
|
+
{ url: mockURL, method: mock.fileContent.method, postData },
|
|
236
|
+
{
|
|
237
|
+
method: fetchReq.options.method || "GET",
|
|
238
|
+
postData: fetchReq.options.body?.length
|
|
239
|
+
? JSON.parse(fetchReq.options.body)
|
|
240
|
+
: fetchReq.options.body,
|
|
241
|
+
url: reqURL,
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
} catch (e) {
|
|
245
|
+
console.debug("error at compareMockToFetchRequest", mock, fetchReq);
|
|
192
246
|
console.debug(e);
|
|
193
247
|
}
|
|
194
248
|
return false;
|
|
195
249
|
}
|
|
196
250
|
|
|
197
|
-
function getMatchingMockData({
|
|
251
|
+
function getMatchingMockData({
|
|
252
|
+
testMockData,
|
|
253
|
+
defaultMockData,
|
|
254
|
+
url,
|
|
255
|
+
options,
|
|
256
|
+
testConfig,
|
|
257
|
+
testName,
|
|
258
|
+
}) {
|
|
198
259
|
let served = false;
|
|
199
|
-
let matchedMocks =
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
260
|
+
let matchedMocks =
|
|
261
|
+
testMockData?.filter((mock) => {
|
|
262
|
+
if (mock.fileContent.waitForPrevious && !served) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
served = mock.fileContent.served;
|
|
266
|
+
return compareMockToFetchRequest(mock, { url, options });
|
|
267
|
+
}) || [];
|
|
268
|
+
let foundMock = matchedMocks.find((mock) => !mock.fileContent.served)
|
|
269
|
+
? matchedMocks.find((mock) => !mock.fileContent.served)
|
|
270
|
+
: matchedMocks[matchedMocks.length - 1];
|
|
207
271
|
// updating stats to mock file
|
|
208
|
-
if(foundMock) {
|
|
209
|
-
const mockFilePath = path.join(
|
|
272
|
+
if (foundMock) {
|
|
273
|
+
const mockFilePath = path.join(
|
|
274
|
+
getMockDir(testConfig),
|
|
275
|
+
`test_${nameToFolder(testName)}`,
|
|
276
|
+
`mock_${foundMock.id}.json`
|
|
277
|
+
);
|
|
210
278
|
foundMock.fileContent.served = true;
|
|
211
|
-
fs.writeFileSync(
|
|
279
|
+
fs.writeFileSync(
|
|
280
|
+
mockFilePath,
|
|
281
|
+
JSON.stringify(foundMock.fileContent, null, 2)
|
|
282
|
+
);
|
|
212
283
|
}
|
|
213
|
-
|
|
214
|
-
if(!foundMock) {
|
|
215
|
-
foundMock = defaultMockData.find(tm =>
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
284
|
+
|
|
285
|
+
if (!foundMock) {
|
|
286
|
+
foundMock = defaultMockData.find((tm) =>
|
|
287
|
+
compareMockToFetchRequest(tm, {
|
|
288
|
+
url,
|
|
289
|
+
options,
|
|
290
|
+
})
|
|
291
|
+
);
|
|
219
292
|
}
|
|
220
293
|
return foundMock ? foundMock.fileContent : null;
|
|
221
294
|
}
|
|
222
295
|
|
|
223
|
-
async function resetAllMockStats({testMockData, testConfig, testName}) {
|
|
224
|
-
for(let i=0; i<testMockData.length; i++) {
|
|
296
|
+
async function resetAllMockStats({ testMockData, testConfig, testName }) {
|
|
297
|
+
for (let i = 0; i < testMockData.length; i++) {
|
|
225
298
|
const tmd = testMockData[i];
|
|
226
|
-
const mockFilePath = path.join(
|
|
299
|
+
const mockFilePath = path.join(
|
|
300
|
+
getMockDir(testConfig),
|
|
301
|
+
`test_${nameToFolder(testName)}`,
|
|
302
|
+
`mock_${tmd.id}.json`
|
|
303
|
+
);
|
|
227
304
|
tmd.fileContent.served = false;
|
|
228
|
-
await fs.writeFileSync(
|
|
305
|
+
await fs.writeFileSync(
|
|
306
|
+
mockFilePath,
|
|
307
|
+
JSON.stringify(tmd.fileContent, null, 2)
|
|
308
|
+
);
|
|
229
309
|
}
|
|
230
310
|
}
|
|
231
311
|
|
|
232
|
-
async function initiatePlaywrightRoutes
|
|
233
|
-
|
|
234
|
-
|
|
312
|
+
async function initiatePlaywrightRoutes(
|
|
313
|
+
page,
|
|
314
|
+
ftmocksConifg,
|
|
315
|
+
testName,
|
|
316
|
+
mockPath = "**/*",
|
|
317
|
+
excludeMockPath = null
|
|
318
|
+
) {
|
|
319
|
+
const testMockData = testName
|
|
320
|
+
? loadMockDataFromConfig(ftmocksConifg, testName)
|
|
321
|
+
: [];
|
|
322
|
+
resetAllMockStats({ testMockData, testConfig: ftmocksConifg, testName });
|
|
235
323
|
const defaultMockData = getDefaultMockDataFromConfig(ftmocksConifg);
|
|
236
|
-
console.debug(
|
|
324
|
+
console.debug("calling initiatePlaywrightRoutes fetch");
|
|
237
325
|
await page.route(mockPath, async (route, request) => {
|
|
238
326
|
const url = request.url();
|
|
239
327
|
const options = {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
}
|
|
328
|
+
url,
|
|
329
|
+
method: request.method(),
|
|
330
|
+
body: request.postData(),
|
|
331
|
+
};
|
|
246
332
|
if (excludeMockPath && new RegExp(excludeMockPath).test(url)) {
|
|
247
333
|
await route.fallback();
|
|
248
334
|
return;
|
|
249
335
|
}
|
|
250
|
-
console.debug(
|
|
251
|
-
|
|
336
|
+
console.debug(
|
|
337
|
+
"got fetch request",
|
|
338
|
+
request.method(),
|
|
339
|
+
request.url(),
|
|
340
|
+
request.postData()
|
|
341
|
+
);
|
|
342
|
+
let mockData = getMatchingMockData({
|
|
343
|
+
testMockData,
|
|
344
|
+
defaultMockData,
|
|
345
|
+
url,
|
|
346
|
+
options,
|
|
347
|
+
testConfig: ftmocksConifg,
|
|
348
|
+
testName,
|
|
349
|
+
});
|
|
252
350
|
if (mockData) {
|
|
253
|
-
console.debug(
|
|
351
|
+
console.debug("mocked", url, options);
|
|
254
352
|
const { content, headers, status } = mockData.response;
|
|
255
|
-
|
|
353
|
+
|
|
256
354
|
const json = {
|
|
257
355
|
status,
|
|
258
356
|
headers: getHeaders(headers),
|
|
@@ -261,31 +359,37 @@ async function initiatePlaywrightRoutes (page, ftmocksConifg, testName, mockPath
|
|
|
261
359
|
|
|
262
360
|
await route.fulfill(json);
|
|
263
361
|
} else {
|
|
264
|
-
console.debug(
|
|
362
|
+
console.debug("missing mock data", url, options);
|
|
265
363
|
const fallbackDir = getFallbackDir(ftmocksConifg);
|
|
266
|
-
if(!fallbackDir) {
|
|
364
|
+
if (!fallbackDir) {
|
|
267
365
|
await route.fallback();
|
|
268
366
|
return;
|
|
269
367
|
}
|
|
270
368
|
const urlObj = new URL(route.request().url());
|
|
271
|
-
const filePath = path.join(
|
|
272
|
-
|
|
369
|
+
const filePath = path.join(
|
|
370
|
+
fallbackDir,
|
|
371
|
+
urlObj.pathname === "/" || urlObj.pathname === ""
|
|
372
|
+
? ftmocksConifg.FALLBACK_DIR_INDEX_FILE || "index.html"
|
|
373
|
+
: urlObj.pathname
|
|
374
|
+
);
|
|
375
|
+
console.debug("serving file ", filePath);
|
|
273
376
|
if (fs.existsSync(filePath) && fs.lstatSync(filePath).isFile()) {
|
|
274
377
|
const fileContent = fs.readFileSync(filePath);
|
|
275
378
|
const ext = path.extname(filePath);
|
|
276
|
-
const contentType =
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
379
|
+
const contentType =
|
|
380
|
+
{
|
|
381
|
+
".html": "text/html",
|
|
382
|
+
".css": "text/css",
|
|
383
|
+
".js": "application/javascript",
|
|
384
|
+
".json": "application/json",
|
|
385
|
+
".png": "image/png",
|
|
386
|
+
".jpg": "image/jpeg",
|
|
387
|
+
}[ext] || "application/octet-stream";
|
|
388
|
+
|
|
389
|
+
console.debug("serving file", filePath);
|
|
286
390
|
await route.fulfill({
|
|
287
391
|
body: fileContent,
|
|
288
|
-
headers: {
|
|
392
|
+
headers: { "Content-Type": contentType },
|
|
289
393
|
});
|
|
290
394
|
} else {
|
|
291
395
|
await route.fallback();
|
|
@@ -294,27 +398,36 @@ async function initiatePlaywrightRoutes (page, ftmocksConifg, testName, mockPath
|
|
|
294
398
|
});
|
|
295
399
|
}
|
|
296
400
|
|
|
297
|
-
async function initiateJestFetch
|
|
298
|
-
const testMockData = testName
|
|
299
|
-
|
|
401
|
+
async function initiateJestFetch(jest, ftmocksConifg, testName) {
|
|
402
|
+
const testMockData = testName
|
|
403
|
+
? loadMockDataFromConfig(ftmocksConifg, testName)
|
|
404
|
+
: [];
|
|
405
|
+
resetAllMockStats({ testMockData, testConfig: ftmocksConifg, testName });
|
|
300
406
|
const defaultMockData = getDefaultMockDataFromConfig(ftmocksConifg);
|
|
301
|
-
console.debug(
|
|
407
|
+
console.debug("calling initiateJestFetch fetch");
|
|
302
408
|
global.fetch = jest.fn((url, options = {}) => {
|
|
303
|
-
console.debug(
|
|
304
|
-
let mockData = getMatchingMockData({
|
|
409
|
+
console.debug("got fetch request", url, options);
|
|
410
|
+
let mockData = getMatchingMockData({
|
|
411
|
+
testMockData,
|
|
412
|
+
defaultMockData,
|
|
413
|
+
url,
|
|
414
|
+
options,
|
|
415
|
+
testConfig: ftmocksConifg,
|
|
416
|
+
testName,
|
|
417
|
+
});
|
|
305
418
|
if (mockData) {
|
|
306
|
-
console.debug(
|
|
419
|
+
console.debug("mocked", url, options);
|
|
307
420
|
} else {
|
|
308
|
-
console.debug(
|
|
421
|
+
console.debug("missing mock data", url, options);
|
|
309
422
|
return Promise.resolve({
|
|
310
423
|
status: 404,
|
|
311
|
-
headers: new Map([[
|
|
312
|
-
json: () => Promise.resolve({ error:
|
|
424
|
+
headers: new Map([["Content-Type", "application/json"]]),
|
|
425
|
+
json: () => Promise.resolve({ error: "Mock data not found" }),
|
|
313
426
|
});
|
|
314
427
|
}
|
|
315
|
-
|
|
428
|
+
|
|
316
429
|
const { content, headers, status } = mockData.response;
|
|
317
|
-
|
|
430
|
+
|
|
318
431
|
return Promise.resolve({
|
|
319
432
|
status,
|
|
320
433
|
headers: new Map(Object.entries(headers)),
|
|
@@ -322,14 +435,14 @@ async function initiateJestFetch (jest, ftmocksConifg, testName) {
|
|
|
322
435
|
});
|
|
323
436
|
});
|
|
324
437
|
|
|
325
|
-
console.debug(
|
|
438
|
+
console.debug("calling XMLHttpRequest fetch");
|
|
326
439
|
global.XMLHttpRequest = jest.fn(function () {
|
|
327
440
|
const xhrMock = {
|
|
328
441
|
open: jest.fn(),
|
|
329
442
|
send: jest.fn(),
|
|
330
443
|
setRequestHeader: jest.fn(),
|
|
331
444
|
getAllResponseHeaders: jest.fn(() => {
|
|
332
|
-
return
|
|
445
|
+
return "";
|
|
333
446
|
}),
|
|
334
447
|
getResponseHeader: jest.fn((header) => {
|
|
335
448
|
return null;
|
|
@@ -337,13 +450,13 @@ async function initiateJestFetch (jest, ftmocksConifg, testName) {
|
|
|
337
450
|
readyState: 4,
|
|
338
451
|
status: 0,
|
|
339
452
|
response: null,
|
|
340
|
-
responseText:
|
|
453
|
+
responseText: "",
|
|
341
454
|
headers: new Map(Object.entries(headers)),
|
|
342
455
|
onreadystatechange: null,
|
|
343
456
|
onload: null,
|
|
344
457
|
onerror: null,
|
|
345
458
|
};
|
|
346
|
-
|
|
459
|
+
|
|
347
460
|
xhrMock.send.mockImplementation(function () {
|
|
348
461
|
const mockData = getMatchingMockData({
|
|
349
462
|
testMockData,
|
|
@@ -353,16 +466,16 @@ async function initiateJestFetch (jest, ftmocksConifg, testName) {
|
|
|
353
466
|
testConfig: ftmocksConifg,
|
|
354
467
|
testName,
|
|
355
468
|
});
|
|
356
|
-
|
|
469
|
+
|
|
357
470
|
if (mockData) {
|
|
358
|
-
console.debug(
|
|
471
|
+
console.debug("mocked", xhrMock._url, xhrMock._options);
|
|
359
472
|
const { content, headers, status } = mockData.response;
|
|
360
|
-
|
|
473
|
+
|
|
361
474
|
xhrMock.status = status;
|
|
362
475
|
xhrMock.responseText = content;
|
|
363
476
|
xhrMock.response = content;
|
|
364
477
|
xhrMock.headers = new Map(Object.entries(headers));
|
|
365
|
-
|
|
478
|
+
|
|
366
479
|
if (xhrMock.onreadystatechange) {
|
|
367
480
|
xhrMock.onreadystatechange();
|
|
368
481
|
}
|
|
@@ -370,12 +483,12 @@ async function initiateJestFetch (jest, ftmocksConifg, testName) {
|
|
|
370
483
|
xhrMock.onload();
|
|
371
484
|
}
|
|
372
485
|
} else {
|
|
373
|
-
console.debug(
|
|
374
|
-
|
|
486
|
+
console.debug("missing mock data", xhrMock._url, xhrMock._options);
|
|
487
|
+
|
|
375
488
|
xhrMock.status = 404;
|
|
376
|
-
xhrMock.responseText = JSON.stringify({ error:
|
|
489
|
+
xhrMock.responseText = JSON.stringify({ error: "Mock data not found" });
|
|
377
490
|
xhrMock.response = xhrMock.responseText;
|
|
378
|
-
|
|
491
|
+
|
|
379
492
|
if (xhrMock.onreadystatechange) {
|
|
380
493
|
xhrMock.onreadystatechange();
|
|
381
494
|
}
|
|
@@ -384,59 +497,62 @@ async function initiateJestFetch (jest, ftmocksConifg, testName) {
|
|
|
384
497
|
}
|
|
385
498
|
}
|
|
386
499
|
});
|
|
387
|
-
|
|
500
|
+
|
|
388
501
|
xhrMock.open.mockImplementation(function (method, url) {
|
|
389
502
|
xhrMock._options = { method };
|
|
390
503
|
xhrMock._url = url;
|
|
391
504
|
});
|
|
392
|
-
|
|
505
|
+
|
|
393
506
|
return xhrMock;
|
|
394
507
|
});
|
|
395
|
-
|
|
508
|
+
|
|
396
509
|
return;
|
|
397
|
-
}
|
|
510
|
+
}
|
|
398
511
|
|
|
399
512
|
function initiateConsoleLogs(jest, ftmocksConifg, testName) {
|
|
400
|
-
const logsFile = path.join(
|
|
513
|
+
const logsFile = path.join(
|
|
514
|
+
getMockDir(ftmocksConifg),
|
|
515
|
+
`test_${nameToFolder(testName)}`,
|
|
516
|
+
"_logs.json"
|
|
517
|
+
);
|
|
401
518
|
let logs = [];
|
|
402
|
-
if(!fs.existsSync(logsFile)) {
|
|
403
|
-
fs.appendFileSync(logsFile,
|
|
519
|
+
if (!fs.existsSync(logsFile)) {
|
|
520
|
+
fs.appendFileSync(logsFile, "[]", "utf8");
|
|
404
521
|
} else {
|
|
405
|
-
fs.writeFileSync(logsFile,
|
|
522
|
+
fs.writeFileSync(logsFile, "[]", "utf8");
|
|
406
523
|
}
|
|
407
524
|
|
|
408
525
|
const writeToFile = (type, params) => {
|
|
409
|
-
const logMessage = params.join(
|
|
526
|
+
const logMessage = params.join(" ") + "\n"; // Combine params into a string with spaces
|
|
410
527
|
logs.push({
|
|
411
528
|
type,
|
|
412
529
|
message: logMessage,
|
|
413
|
-
time: Date.now()
|
|
530
|
+
time: Date.now(),
|
|
414
531
|
});
|
|
415
|
-
fs.writeFileSync(logsFile, JSON.stringify(logs, null, 2),
|
|
416
|
-
}
|
|
417
|
-
|
|
532
|
+
fs.writeFileSync(logsFile, JSON.stringify(logs, null, 2), "utf8"); // Append the log message to the file
|
|
533
|
+
};
|
|
534
|
+
|
|
418
535
|
global.console = {
|
|
419
536
|
...console,
|
|
420
537
|
// uncomment to ignore a specific log level
|
|
421
538
|
log: jest.fn((...params) => {
|
|
422
|
-
writeToFile(
|
|
539
|
+
writeToFile("log", params);
|
|
423
540
|
}),
|
|
424
541
|
debug: jest.fn((...params) => {
|
|
425
|
-
writeToFile(
|
|
542
|
+
writeToFile("debug", params);
|
|
426
543
|
}),
|
|
427
544
|
info: jest.fn((...params) => {
|
|
428
|
-
writeToFile(
|
|
545
|
+
writeToFile("info", params);
|
|
429
546
|
}),
|
|
430
547
|
warn: jest.fn((...params) => {
|
|
431
|
-
writeToFile(
|
|
548
|
+
writeToFile("warn", params);
|
|
432
549
|
}),
|
|
433
550
|
error: jest.fn((...params) => {
|
|
434
|
-
writeToFile(
|
|
551
|
+
writeToFile("error", params);
|
|
435
552
|
}),
|
|
436
553
|
};
|
|
437
554
|
}
|
|
438
555
|
|
|
439
|
-
|
|
440
556
|
function countFilesInDirectory(directoryPath) {
|
|
441
557
|
return new Promise((resolve, reject) => {
|
|
442
558
|
fs.readdir(directoryPath, (err, files) => {
|
|
@@ -445,7 +561,7 @@ function countFilesInDirectory(directoryPath) {
|
|
|
445
561
|
}
|
|
446
562
|
|
|
447
563
|
// Filter out directories and only count files
|
|
448
|
-
const fileCount = files.filter(file => {
|
|
564
|
+
const fileCount = files.filter((file) => {
|
|
449
565
|
const filePath = path.join(directoryPath, file);
|
|
450
566
|
return fs.statSync(filePath).isFile();
|
|
451
567
|
}).length;
|
|
@@ -456,62 +572,332 @@ function countFilesInDirectory(directoryPath) {
|
|
|
456
572
|
}
|
|
457
573
|
|
|
458
574
|
const saveSnap = async (html, ftmocksConifg, testName) => {
|
|
459
|
-
const snapFolder = path.join(
|
|
460
|
-
|
|
461
|
-
|
|
575
|
+
const snapFolder = path.join(
|
|
576
|
+
getMockDir(ftmocksConifg),
|
|
577
|
+
`test_${nameToFolder(testName)}`,
|
|
578
|
+
"_snaps"
|
|
579
|
+
);
|
|
580
|
+
const snapTemplate = path.join(
|
|
581
|
+
getMockDir(ftmocksConifg),
|
|
582
|
+
"snap_template.html"
|
|
583
|
+
);
|
|
584
|
+
|
|
462
585
|
if (!fs.existsSync(snapFolder)) {
|
|
463
586
|
fs.mkdirSync(snapFolder);
|
|
464
587
|
}
|
|
465
|
-
const fileCount =
|
|
588
|
+
const fileCount = await countFilesInDirectory(snapFolder);
|
|
466
589
|
const snapFilePath = path.join(snapFolder, `snap_${fileCount + 1}.html`);
|
|
467
590
|
let resHtml = html;
|
|
468
591
|
if (fs.existsSync(snapFolder)) {
|
|
469
|
-
const templateHtml = fs.readFileSync(snapTemplate,
|
|
470
|
-
resHtml = templateHtml.replace(
|
|
592
|
+
const templateHtml = fs.readFileSync(snapTemplate, "utf8");
|
|
593
|
+
resHtml = templateHtml.replace(
|
|
594
|
+
"<!--FtMocks-Snap-Template-To-Be-Replaced-->",
|
|
595
|
+
html
|
|
596
|
+
);
|
|
471
597
|
}
|
|
472
|
-
fs.writeFileSync(snapFilePath, resHtml)
|
|
598
|
+
fs.writeFileSync(snapFilePath, resHtml);
|
|
473
599
|
};
|
|
474
600
|
|
|
475
601
|
const deleteAllSnaps = async (ftmocksConifg, testName) => {
|
|
476
|
-
const snapFolder = path.join(
|
|
602
|
+
const snapFolder = path.join(
|
|
603
|
+
getMockDir(ftmocksConifg),
|
|
604
|
+
`test_${nameToFolder(testName)}`,
|
|
605
|
+
"_snaps"
|
|
606
|
+
);
|
|
477
607
|
fs.rmSync(snapFolder, { recursive: true, force: true });
|
|
478
608
|
};
|
|
479
609
|
|
|
480
610
|
const deleteAllLogs = async (ftmocksConifg, testName) => {
|
|
481
|
-
const mockDir = path.join(
|
|
611
|
+
const mockDir = path.join(
|
|
612
|
+
getMockDir(ftmocksConifg),
|
|
613
|
+
`test_${nameToFolder(testName)}`
|
|
614
|
+
);
|
|
482
615
|
const logFilePath = path.join(mockDir, `_logs.json`);
|
|
483
616
|
fs.rmSync(logFilePath, { recursive: true, force: true });
|
|
484
617
|
};
|
|
485
618
|
|
|
486
619
|
function initiateJestEventSnaps(jest, ftmocksConifg, testName) {
|
|
487
|
-
const mouseEvents = ftmocksConifg.snapEvents || [
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
620
|
+
const mouseEvents = ftmocksConifg.snapEvents || [
|
|
621
|
+
"click",
|
|
622
|
+
"change",
|
|
623
|
+
"url",
|
|
624
|
+
"dblclick",
|
|
625
|
+
"contextmenu",
|
|
626
|
+
];
|
|
627
|
+
mouseEvents.forEach((event) => {
|
|
628
|
+
jest
|
|
629
|
+
.spyOn(document, "addEventListener")
|
|
630
|
+
.mockImplementation((e, callback) => {
|
|
631
|
+
if (mouseEvents.includes(e)) {
|
|
632
|
+
saveSnap(document.outerHTML, ftmocksConifg, testName);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
494
635
|
});
|
|
495
636
|
}
|
|
496
637
|
|
|
638
|
+
const createTest = async (ftmocksConifg, testName) => {
|
|
639
|
+
const testsPath = path.join(getMockDir(ftmocksConifg), "tests.json");
|
|
640
|
+
let tests = [];
|
|
641
|
+
try {
|
|
642
|
+
// Read existing tests
|
|
643
|
+
const testsData = fs.readFileSync(testsPath, "utf8");
|
|
644
|
+
tests = JSON.parse(testsData);
|
|
645
|
+
const etest = tests.find((tst) => tst.name === testName);
|
|
646
|
+
if (!etest) {
|
|
647
|
+
const newTest = {
|
|
648
|
+
id: uuidv4(),
|
|
649
|
+
name: testName,
|
|
650
|
+
};
|
|
651
|
+
tests.push(newTest);
|
|
652
|
+
fs.writeFileSync(testsPath, JSON.stringify(tests, null, 2));
|
|
653
|
+
const folderPath = path.join(
|
|
654
|
+
getMockDir(ftmocksConifg),
|
|
655
|
+
`test_${nameToFolder(testName)}`
|
|
656
|
+
);
|
|
657
|
+
const mockListFilePath = path.join(folderPath, "_mock_list.json");
|
|
658
|
+
fs.mkdir(folderPath, { recursive: true }, (err) => {
|
|
659
|
+
if (err) {
|
|
660
|
+
console.error("Error creating directory:", err);
|
|
661
|
+
} else {
|
|
662
|
+
console.log("Directory created successfully!");
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
await fs.appendFile(mockListFilePath, "[]", () => {
|
|
666
|
+
console.log("mock list file created successfully");
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
return newTest;
|
|
670
|
+
} else {
|
|
671
|
+
throw "Test already exists";
|
|
672
|
+
}
|
|
673
|
+
} catch (error) {
|
|
674
|
+
console.error(`Error reading tests.json:`, error);
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
const isSameResponse = (req1, req2) => {
|
|
680
|
+
try {
|
|
681
|
+
let matched = true;
|
|
682
|
+
if (req1.response.status !== req2.response.status) {
|
|
683
|
+
matched = false;
|
|
684
|
+
// console.log('not matched at url', req1.method, req2.method);
|
|
685
|
+
} else if (
|
|
686
|
+
(!req1.response.content && req2.response.content) ||
|
|
687
|
+
(req1.response.content && !req2.response.content)
|
|
688
|
+
) {
|
|
689
|
+
matched = areJsonEqual(
|
|
690
|
+
JSON.parse(req1.response.content) || {},
|
|
691
|
+
JSON.parse(req2.response.content) || {}
|
|
692
|
+
);
|
|
693
|
+
// console.log('not matched at post Data 0', req1.postData, req2.postData);
|
|
694
|
+
} else if (
|
|
695
|
+
req1.response.content &&
|
|
696
|
+
req2.response.content &&
|
|
697
|
+
!areJsonEqual(
|
|
698
|
+
JSON.parse(req1.response.content) || {},
|
|
699
|
+
JSON.parse(req2.response.content) || {}
|
|
700
|
+
)
|
|
701
|
+
) {
|
|
702
|
+
matched = false;
|
|
703
|
+
}
|
|
704
|
+
// if (matched) {
|
|
705
|
+
// console.log('matched responses', req1, req2);
|
|
706
|
+
// }
|
|
707
|
+
return matched;
|
|
708
|
+
} catch (error) {
|
|
709
|
+
console.error(error);
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
const compareMockToMock = (mock1, mock2, matchResponse) => {
|
|
715
|
+
try {
|
|
716
|
+
if (matchResponse) {
|
|
717
|
+
return isSameRequest(mock1, mock2) && isSameResponse(mock1, mock2);
|
|
718
|
+
} else {
|
|
719
|
+
return isSameRequest(mock1, mock2);
|
|
720
|
+
}
|
|
721
|
+
} catch (error) {
|
|
722
|
+
console.error(error);
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
const saveIfItIsFile = async (route, testName, ftmocksConifg) => {
|
|
728
|
+
const urlObj = new URL(route.request().url());
|
|
729
|
+
|
|
730
|
+
// Check if URL contains file extension like .js, .png, .css etc
|
|
731
|
+
const fileExtMatch = urlObj.pathname.match(/\.[a-zA-Z0-9]+$/);
|
|
732
|
+
if (fileExtMatch) {
|
|
733
|
+
const fileExt = fileExtMatch[0];
|
|
734
|
+
// Create directory path matching URL structure
|
|
735
|
+
const dirPath = path.join(
|
|
736
|
+
getMockDir(ftmocksConifg),
|
|
737
|
+
`test_${nameToFolder(testName)}`,
|
|
738
|
+
"_files",
|
|
739
|
+
path.dirname(urlObj.pathname)
|
|
740
|
+
);
|
|
741
|
+
|
|
742
|
+
// Create directories if they don't exist
|
|
743
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
744
|
+
|
|
745
|
+
// Save file with original name
|
|
746
|
+
const fileName = path.basename(urlObj.pathname);
|
|
747
|
+
const filePath = path.join(dirPath, fileName);
|
|
748
|
+
|
|
749
|
+
const response = await route.fetch();
|
|
750
|
+
const buffer = await response.body();
|
|
751
|
+
fs.writeFileSync(filePath, buffer);
|
|
752
|
+
|
|
753
|
+
await route.continue();
|
|
754
|
+
return true;
|
|
755
|
+
}
|
|
756
|
+
return false;
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
async function recordPlaywrightRoutes(
|
|
760
|
+
page,
|
|
761
|
+
ftmocksConifg,
|
|
762
|
+
config = {
|
|
763
|
+
testName,
|
|
764
|
+
mockPath: "**/*",
|
|
765
|
+
pattern: "^/api/.*",
|
|
766
|
+
avoidDuplicatesInTheTest: true,
|
|
767
|
+
avoidDuplicatesWithDefaultMocks: true,
|
|
768
|
+
}
|
|
769
|
+
) {
|
|
770
|
+
await page.route(config.mockPath, async (route) => {
|
|
771
|
+
try {
|
|
772
|
+
const urlObj = new URL(route.request().url());
|
|
773
|
+
if (config.pattern && config.pattern.length > 0) {
|
|
774
|
+
const patternRegex = new RegExp(config.pattern);
|
|
775
|
+
if (!patternRegex.test(urlObj.pathname)) {
|
|
776
|
+
await route.continue();
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
497
780
|
|
|
781
|
+
if (await saveIfItIsFile(route, config.testName, ftmocksConifg)) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const mockData = {
|
|
786
|
+
url: urlObj.pathname + urlObj.search,
|
|
787
|
+
time: new Date().toString(),
|
|
788
|
+
method: route.request().method(),
|
|
789
|
+
request: {
|
|
790
|
+
headers: await route.request().headers(),
|
|
791
|
+
queryString: Array.from(urlObj.searchParams.entries()).map(
|
|
792
|
+
([name, value]) => ({
|
|
793
|
+
name,
|
|
794
|
+
value,
|
|
795
|
+
})
|
|
796
|
+
),
|
|
797
|
+
postData: route.request().postData()
|
|
798
|
+
? {
|
|
799
|
+
mimeType: "application/json",
|
|
800
|
+
text: route.request().postData(),
|
|
801
|
+
}
|
|
802
|
+
: null,
|
|
803
|
+
},
|
|
804
|
+
response: {
|
|
805
|
+
status: (await route.fetch()).status(),
|
|
806
|
+
headers: (await route.fetch()).headers(),
|
|
807
|
+
content: await (await route.fetch()).text(),
|
|
808
|
+
},
|
|
809
|
+
id: crypto.randomUUID(),
|
|
810
|
+
served: false,
|
|
811
|
+
ignoreParams: ftmocksConifg.ignoreParams || [],
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
await createTest(ftmocksConifg, config.testName);
|
|
815
|
+
if (config.avoidDuplicatesInTheTest) {
|
|
816
|
+
// Check if the mock data is a duplicate of a mock data in the test
|
|
817
|
+
const testMockList = loadMockDataFromConfig(
|
|
818
|
+
ftmocksConifg,
|
|
819
|
+
config.testName
|
|
820
|
+
);
|
|
821
|
+
const matchResponse = testMockList.find((mock) =>
|
|
822
|
+
compareMockToMock(mock.fileContent, mockData, true)
|
|
823
|
+
);
|
|
824
|
+
if (matchResponse) {
|
|
825
|
+
console.log("Aborting duplicate mock data in the test");
|
|
826
|
+
await route.continue();
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (config.avoidDuplicatesWithDefaultMocks) {
|
|
832
|
+
// Check if the mock data is a duplicate of a mock data in the test
|
|
833
|
+
const defaultMockList = getDefaultMockDataFromConfig(ftmocksConifg);
|
|
834
|
+
const matchResponse = defaultMockList.find((mock) =>
|
|
835
|
+
compareMockToMock(mock.fileContent, mockData, true)
|
|
836
|
+
);
|
|
837
|
+
if (matchResponse) {
|
|
838
|
+
console.log("Aborting duplicate mock data with default mocks");
|
|
839
|
+
await route.continue();
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// Save the mock data to the test
|
|
845
|
+
const mockListPath = path.join(
|
|
846
|
+
getMockDir(ftmocksConifg),
|
|
847
|
+
`test_${nameToFolder(config.testName)}`,
|
|
848
|
+
"_mock_list.json"
|
|
849
|
+
);
|
|
850
|
+
let mockList = [];
|
|
851
|
+
if (fs.existsSync(mockListPath)) {
|
|
852
|
+
mockList = JSON.parse(fs.readFileSync(mockListPath, "utf8"));
|
|
853
|
+
}
|
|
854
|
+
mockList.push({
|
|
855
|
+
id: mockData.id,
|
|
856
|
+
url: mockData.url,
|
|
857
|
+
method: mockData.method,
|
|
858
|
+
time: mockData.time,
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
// Create test directory if it doesn't exist
|
|
862
|
+
const testDir = path.join(
|
|
863
|
+
getMockDir(ftmocksConifg),
|
|
864
|
+
`test_${nameToFolder(config.testName)}`
|
|
865
|
+
);
|
|
866
|
+
if (!fs.existsSync(testDir)) {
|
|
867
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
868
|
+
}
|
|
869
|
+
fs.writeFileSync(mockListPath, JSON.stringify(mockList, null, 2));
|
|
870
|
+
const mocDataPath = path.join(
|
|
871
|
+
getMockDir(ftmocksConifg),
|
|
872
|
+
`test_${nameToFolder(config.testName)}`,
|
|
873
|
+
`mock_${mockData.id}.json`
|
|
874
|
+
);
|
|
875
|
+
fs.writeFileSync(mocDataPath, JSON.stringify(mockData, null, 2));
|
|
876
|
+
await route.continue();
|
|
877
|
+
} catch (error) {
|
|
878
|
+
console.error(error);
|
|
879
|
+
await route.continue();
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
}
|
|
498
883
|
|
|
499
884
|
// Export functions as a module
|
|
500
885
|
module.exports = {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
886
|
+
compareMockToRequest,
|
|
887
|
+
processURL,
|
|
888
|
+
isSameRequest,
|
|
889
|
+
loadMockDataFromConfig,
|
|
890
|
+
getDefaultMockDataFromConfig,
|
|
891
|
+
nameToFolder,
|
|
892
|
+
compareMockToFetchRequest,
|
|
893
|
+
getMatchingMockData,
|
|
894
|
+
resetAllMockStats,
|
|
895
|
+
initiateJestFetch,
|
|
896
|
+
saveSnap,
|
|
897
|
+
deleteAllSnaps,
|
|
898
|
+
deleteAllLogs,
|
|
899
|
+
initiateConsoleLogs,
|
|
900
|
+
initiatePlaywrightRoutes,
|
|
901
|
+
initiateJestEventSnaps,
|
|
902
|
+
recordPlaywrightRoutes,
|
|
517
903
|
};
|