ftmocks-utils 1.3.0 → 1.3.2
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/common-utils.js +20 -42
- package/src/index.js +27 -23
- package/src/json-utils.js +55 -0
- package/src/log-utils.js +90 -0
package/package.json
CHANGED
package/src/common-utils.js
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const fs = require("fs");
|
|
3
|
+
const { FtJSON } = require("./json-utils");
|
|
3
4
|
|
|
4
5
|
const charDifference = (str1, str2) => {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
if (str1 && str2) {
|
|
7
|
+
let count1 = {};
|
|
8
|
+
let count2 = {};
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
for (let ch of str1) count1[ch] = (count1[ch] || 0) + 1;
|
|
11
|
+
for (let ch of str2) count2[ch] = (count2[ch] || 0) + 1;
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
let diff = 0;
|
|
14
|
+
let chars = new Set([...Object.keys(count1), ...Object.keys(count2)]);
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
for (let ch of chars) {
|
|
17
|
+
diff += Math.abs((count1[ch] || 0) - (count2[ch] || 0));
|
|
18
|
+
}
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
return diff;
|
|
21
|
+
} else {
|
|
22
|
+
if (!str1 && str2) {
|
|
23
|
+
return str2.length;
|
|
24
|
+
} else if (str1 && !str2) {
|
|
25
|
+
return str1.length;
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
19
29
|
};
|
|
20
30
|
|
|
21
31
|
const nameToFolder = (name) => {
|
|
@@ -52,37 +62,6 @@ const capitalizeHeaders = (headers) => {
|
|
|
52
62
|
);
|
|
53
63
|
};
|
|
54
64
|
|
|
55
|
-
const areJsonEqual = (jsonObj1, jsonObj2) => {
|
|
56
|
-
// Check if both are objects and not null
|
|
57
|
-
if (
|
|
58
|
-
typeof jsonObj1 === "object" &&
|
|
59
|
-
jsonObj1 !== null &&
|
|
60
|
-
typeof jsonObj2 === "object" &&
|
|
61
|
-
jsonObj2 !== null
|
|
62
|
-
) {
|
|
63
|
-
// Get the keys of both objects
|
|
64
|
-
const keys1 = Object.keys(jsonObj1);
|
|
65
|
-
const keys2 = Object.keys(jsonObj2);
|
|
66
|
-
|
|
67
|
-
// Check if the number of keys is different
|
|
68
|
-
if (keys1.length !== keys2.length) {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Recursively check each key-value pair
|
|
73
|
-
for (let key of keys1) {
|
|
74
|
-
if (!keys2.includes(key) || !areJsonEqual(jsonObj1[key], jsonObj2[key])) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return true;
|
|
80
|
-
} else {
|
|
81
|
-
// For non-object types, use strict equality comparison
|
|
82
|
-
return jsonObj1 === jsonObj2;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
65
|
const clearNulls = (postData) => {
|
|
87
66
|
Object.keys(postData || {}).forEach((key) => {
|
|
88
67
|
if (postData[key] === null) {
|
|
@@ -162,7 +141,6 @@ module.exports = {
|
|
|
162
141
|
getFallbackDir,
|
|
163
142
|
capitalizeHeader,
|
|
164
143
|
capitalizeHeaders,
|
|
165
|
-
areJsonEqual,
|
|
166
144
|
clearNulls,
|
|
167
145
|
processURL,
|
|
168
146
|
getHeaders,
|
package/src/index.js
CHANGED
|
@@ -6,13 +6,16 @@ const {
|
|
|
6
6
|
nameToFolder,
|
|
7
7
|
getMockDir,
|
|
8
8
|
getFallbackDir,
|
|
9
|
-
areJsonEqual,
|
|
10
9
|
clearNulls,
|
|
11
10
|
processURL,
|
|
12
11
|
getHeaders,
|
|
13
12
|
countFilesInDirectory,
|
|
14
13
|
getTestByName,
|
|
15
14
|
} = require("./common-utils");
|
|
15
|
+
const { FtJSON } = require("./json-utils");
|
|
16
|
+
const Logger = require("./log-utils");
|
|
17
|
+
|
|
18
|
+
let logger = null;
|
|
16
19
|
|
|
17
20
|
const getDefaultMockDataFromConfig = (testConfig) => {
|
|
18
21
|
const defaultPath = path.join(
|
|
@@ -103,11 +106,11 @@ const isSameRequest = (req1, req2) => {
|
|
|
103
106
|
(!req1.postData && req2.postData) ||
|
|
104
107
|
(req1.postData && !req2.postData)
|
|
105
108
|
) {
|
|
106
|
-
matched = areJsonEqual(req1.postData || {}, req2.postData || {});
|
|
109
|
+
matched = FtJSON.areJsonEqual(req1.postData || {}, req2.postData || {});
|
|
107
110
|
} else if (
|
|
108
111
|
req1.postData &&
|
|
109
112
|
req2.postData &&
|
|
110
|
-
!areJsonEqual(req1.postData, req2.postData)
|
|
113
|
+
!FtJSON.areJsonEqual(req1.postData, req2.postData)
|
|
111
114
|
) {
|
|
112
115
|
matched = false;
|
|
113
116
|
}
|
|
@@ -131,8 +134,8 @@ const getSameRequestRank = (req1, req2) => {
|
|
|
131
134
|
rank = rank + queryDiff;
|
|
132
135
|
// Compare post data
|
|
133
136
|
const charDiff = charDifference(
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
FtJSON.stringify(req1.postData || {}),
|
|
138
|
+
FtJSON.stringify(req2.postData || {})
|
|
136
139
|
);
|
|
137
140
|
rank = rank + charDiff;
|
|
138
141
|
}
|
|
@@ -146,7 +149,7 @@ function compareMockToRequest(mock, req) {
|
|
|
146
149
|
);
|
|
147
150
|
const reqURL = processURL(req.originalUrl, mock.fileContent.ignoreParams);
|
|
148
151
|
const postData = mock.fileContent.request?.postData?.text
|
|
149
|
-
?
|
|
152
|
+
? FtJSON.parse(mock.fileContent.request?.postData?.text)
|
|
150
153
|
: mock.fileContent.request?.postData;
|
|
151
154
|
return isSameRequest(
|
|
152
155
|
{ url: mockURL, method: mock.fileContent.method, postData },
|
|
@@ -166,14 +169,14 @@ function compareMockToFetchRequest(mock, fetchReq) {
|
|
|
166
169
|
);
|
|
167
170
|
const reqURL = processURL(fetchReq.url, mock.fileContent.ignoreParams);
|
|
168
171
|
const postData = mock.fileContent.request?.postData?.text
|
|
169
|
-
?
|
|
172
|
+
? FtJSON.parse(mock.fileContent.request?.postData?.text)
|
|
170
173
|
: mock.fileContent.request?.postData;
|
|
171
174
|
return isSameRequest(
|
|
172
175
|
{ url: mockURL, method: mock.fileContent.method, postData },
|
|
173
176
|
{
|
|
174
177
|
method: fetchReq.options.method || "GET",
|
|
175
178
|
postData: fetchReq.options.body?.length
|
|
176
|
-
?
|
|
179
|
+
? FtJSON.parse(fetchReq.options.body)
|
|
177
180
|
: fetchReq.options.body,
|
|
178
181
|
url: reqURL,
|
|
179
182
|
}
|
|
@@ -193,14 +196,14 @@ function getCompareRankMockToFetchRequest(mock, fetchReq) {
|
|
|
193
196
|
);
|
|
194
197
|
const reqURL = processURL(fetchReq.url, mock.fileContent.ignoreParams);
|
|
195
198
|
const postData = mock.fileContent.request?.postData?.text
|
|
196
|
-
?
|
|
199
|
+
? FtJSON.parse(mock.fileContent.request?.postData?.text)
|
|
197
200
|
: mock.fileContent.request?.postData;
|
|
198
201
|
return getSameRequestRank(
|
|
199
202
|
{ url: mockURL, method: mock.fileContent.method, postData },
|
|
200
203
|
{
|
|
201
204
|
method: fetchReq.options.method || "GET",
|
|
202
205
|
postData: fetchReq.options.body?.length
|
|
203
|
-
?
|
|
206
|
+
? FtJSON.parse(fetchReq.options.body)
|
|
204
207
|
: fetchReq.options.body,
|
|
205
208
|
url: reqURL,
|
|
206
209
|
}
|
|
@@ -320,13 +323,14 @@ async function initiatePlaywrightRoutes(
|
|
|
320
323
|
mockPath = "**/*",
|
|
321
324
|
excludeMockPath = null
|
|
322
325
|
) {
|
|
326
|
+
logger = new Logger({disableLogs: ftmocksConifg.DISABLE_LOGS}, ftmocksConifg, testName);
|
|
323
327
|
const testMockData = testName
|
|
324
328
|
? loadMockDataFromConfig(ftmocksConifg, testName)
|
|
325
329
|
: [];
|
|
326
330
|
resetAllMockStats({ testMockData, testConfig: ftmocksConifg, testName });
|
|
327
331
|
const test = await getTestByName(ftmocksConifg, testName);
|
|
328
332
|
const defaultMockData = getDefaultMockDataFromConfig(ftmocksConifg);
|
|
329
|
-
|
|
333
|
+
logger.debug("\x1b[32mcalling initiatePlaywrightRoutes fetch\x1b[0m");
|
|
330
334
|
let firstUrl = null;
|
|
331
335
|
await page.route(mockPath, async (route, request) => {
|
|
332
336
|
const url = request.url();
|
|
@@ -413,7 +417,7 @@ async function initiatePlaywrightRoutes(
|
|
|
413
417
|
fallbackDir,
|
|
414
418
|
ftmocksConifg.FALLBACK_DIR_INDEX_FILE_FOR_STATUS_404 || "index.html"
|
|
415
419
|
);
|
|
416
|
-
|
|
420
|
+
logger.debug(
|
|
417
421
|
"\x1b[32mserving file for status 404\x1b[0m",
|
|
418
422
|
filePath,
|
|
419
423
|
url
|
|
@@ -476,19 +480,19 @@ async function initiatePlaywrightRoutes(
|
|
|
476
480
|
".php": "application/x-httpd-php",
|
|
477
481
|
}[ext] || "application/octet-stream";
|
|
478
482
|
|
|
479
|
-
|
|
483
|
+
logger.info("\x1b[32mserving file\x1b[0m", filePath);
|
|
480
484
|
await route.fulfill({
|
|
481
485
|
body: fileContent,
|
|
482
486
|
headers: { "Content-Type": contentType },
|
|
483
487
|
});
|
|
484
488
|
} else {
|
|
485
|
-
|
|
489
|
+
logger.debug("\x1b[31mmissing mock data, falling back\x1b[0m", url);
|
|
486
490
|
await route.fallback();
|
|
487
491
|
}
|
|
488
492
|
}
|
|
489
493
|
} catch (e) {
|
|
490
|
-
|
|
491
|
-
|
|
494
|
+
logger.error(e);
|
|
495
|
+
logger.error(
|
|
492
496
|
"\x1b[31merror at initiatePlaywrightRoutes\x1b[0m",
|
|
493
497
|
url,
|
|
494
498
|
options
|
|
@@ -530,7 +534,7 @@ async function initiateJestFetch(jest, ftmocksConifg, testName) {
|
|
|
530
534
|
return Promise.resolve({
|
|
531
535
|
status,
|
|
532
536
|
headers: new Map(Object.entries(headers)),
|
|
533
|
-
json: () => Promise.resolve(
|
|
537
|
+
json: () => Promise.resolve(FtJSON.parse(content)),
|
|
534
538
|
});
|
|
535
539
|
});
|
|
536
540
|
|
|
@@ -767,17 +771,17 @@ const isSameResponse = (req1, req2) => {
|
|
|
767
771
|
(!req1.response.content && req2.response.content) ||
|
|
768
772
|
(req1.response.content && !req2.response.content)
|
|
769
773
|
) {
|
|
770
|
-
matched = areJsonEqual(
|
|
771
|
-
|
|
772
|
-
|
|
774
|
+
matched = FtJSON.areJsonEqual(
|
|
775
|
+
FtJSON.parse(req1.response.content) || {},
|
|
776
|
+
FtJSON.parse(req2.response.content) || {}
|
|
773
777
|
);
|
|
774
778
|
// console.log('not matched at post Data 0', req1.postData, req2.postData);
|
|
775
779
|
} else if (
|
|
776
780
|
req1.response.content &&
|
|
777
781
|
req2.response.content &&
|
|
778
|
-
!areJsonEqual(
|
|
779
|
-
|
|
780
|
-
|
|
782
|
+
!FtJSON.areJsonEqual(
|
|
783
|
+
FtJSON.parse(req1.response.content) || {},
|
|
784
|
+
FtJSON.parse(req2.response.content) || {}
|
|
781
785
|
)
|
|
782
786
|
) {
|
|
783
787
|
matched = false;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class FtJSON {
|
|
2
|
+
static parse(text, reviver, reference) {
|
|
3
|
+
try {
|
|
4
|
+
return JSON.parse(text, reviver);
|
|
5
|
+
} catch (error) {
|
|
6
|
+
console.error("FtJSON parse error:", error, reference);
|
|
7
|
+
return text;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static stringify(value, replacer, space, reference) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.stringify(value, replacer, space);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error("FtJSON stringify error:", error, reference);
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static areJsonEqual(jsonObj1, jsonObj2) {
|
|
21
|
+
// Check if both are objects and not null
|
|
22
|
+
if (
|
|
23
|
+
typeof jsonObj1 === "object" &&
|
|
24
|
+
jsonObj1 !== null &&
|
|
25
|
+
typeof jsonObj2 === "object" &&
|
|
26
|
+
jsonObj2 !== null
|
|
27
|
+
) {
|
|
28
|
+
// Get the keys of both objects
|
|
29
|
+
const keys1 = Object.keys(jsonObj1);
|
|
30
|
+
const keys2 = Object.keys(jsonObj2);
|
|
31
|
+
|
|
32
|
+
// Check if the number of keys is different
|
|
33
|
+
if (keys1.length !== keys2.length) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Recursively check each key-value pair
|
|
38
|
+
for (let key of keys1) {
|
|
39
|
+
if (
|
|
40
|
+
!keys2.includes(key) ||
|
|
41
|
+
!FtJSON.areJsonEqual(jsonObj1[key], jsonObj2[key])
|
|
42
|
+
) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
} else {
|
|
49
|
+
// For non-object types, use strict equality comparison
|
|
50
|
+
return jsonObj1 === jsonObj2;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { FtJSON };
|
package/src/log-utils.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const { getMockDir, nameToFolder } = require("./common-utils");
|
|
4
|
+
|
|
5
|
+
class Logger {
|
|
6
|
+
constructor(options = {}, ftmocksConifg, testName) {
|
|
7
|
+
this.levels = ["error", "warn", "info", "debug"];
|
|
8
|
+
this.level = options.level || "info";
|
|
9
|
+
this.disableLogs = options.disableLogs || false;
|
|
10
|
+
this.logsFile = path.join(
|
|
11
|
+
getMockDir(ftmocksConifg),
|
|
12
|
+
`test_${nameToFolder(testName)}`,
|
|
13
|
+
"_logs.json"
|
|
14
|
+
);
|
|
15
|
+
this.logs = [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setLevel(level) {
|
|
19
|
+
if (this.levels.includes(level)) {
|
|
20
|
+
this.level = level;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
writeToFile(type, params) {
|
|
25
|
+
try {
|
|
26
|
+
const logMessage = params.join(" ") + "\n"; // Combine params into a string with spaces
|
|
27
|
+
this.logs.push({
|
|
28
|
+
type,
|
|
29
|
+
message: logMessage,
|
|
30
|
+
time: Date.now(),
|
|
31
|
+
source: "ftmocks-utils",
|
|
32
|
+
});
|
|
33
|
+
fs.writeFileSync(this.logsFile, JSON.stringify(this.logs, null, 2), "utf8"); // Append the log message to the file
|
|
34
|
+
} catch (error) {
|
|
35
|
+
// Ignore error
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
log(level, ...args) {
|
|
40
|
+
if (this.disableLogs) return;
|
|
41
|
+
const levelIdx = this.levels.indexOf(level);
|
|
42
|
+
const currentLevelIdx = this.levels.indexOf(this.level);
|
|
43
|
+
if (levelIdx <= currentLevelIdx) {
|
|
44
|
+
const color = this._getColor(level);
|
|
45
|
+
const prefix = `[${level.toUpperCase()}]`;
|
|
46
|
+
if (typeof args[0] === "string") {
|
|
47
|
+
// Color only the prefix
|
|
48
|
+
console.log(`${color}${prefix}\x1b[0m`, ...args);
|
|
49
|
+
} else {
|
|
50
|
+
// Non-string first arg, just print
|
|
51
|
+
console.log(`${color}${prefix}\x1b[0m`, ...args);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
this.writeToFile(level, args);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
error(...args) {
|
|
58
|
+
this.log("error", ...args);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
warn(...args) {
|
|
62
|
+
this.log("warn", ...args);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
info(...args) {
|
|
66
|
+
this.log("info", ...args);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
debug(...args) {
|
|
70
|
+
this.log("debug", ...args);
|
|
71
|
+
console.debug(...args);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
_getColor(level) {
|
|
75
|
+
switch (level) {
|
|
76
|
+
case "error":
|
|
77
|
+
return "\x1b[31m"; // Red
|
|
78
|
+
case "warn":
|
|
79
|
+
return "\x1b[33m"; // Yellow
|
|
80
|
+
case "info":
|
|
81
|
+
return "\x1b[36m"; // Cyan
|
|
82
|
+
case "debug":
|
|
83
|
+
return "\x1b[90m"; // Gray
|
|
84
|
+
default:
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = Logger;
|