ftmocks-utils 1.3.2 → 1.3.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ftmocks-utils",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "Util functions for FtMocks",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -0,0 +1,151 @@
1
+ const { clearNulls, processURL } = require("./common-utils");
2
+ const { FtJSON } = require("./json-utils");
3
+
4
+ const isUrlAndMethodSame = (req1, req2) => {
5
+ const url1 = new URL(`http://domain.com${req1.url}`);
6
+ const url2 = new URL(`http://domain.com${req2.url}`);
7
+ return (
8
+ url1.pathname === url2.pathname &&
9
+ url1.method?.toLowerCase() === url2.method?.toLowerCase()
10
+ );
11
+ };
12
+
13
+ const isSameRequest = (req1, req2) => {
14
+ clearNulls(req1.postData);
15
+ clearNulls(req2.postData);
16
+ let matched = true;
17
+ if (req1.url !== req2.url) {
18
+ matched = false;
19
+ } else if (req1.method?.toLowerCase() !== req2.method?.toLowerCase()) {
20
+ matched = false;
21
+ } else if (
22
+ (!req1.postData && req2.postData) ||
23
+ (req1.postData && !req2.postData)
24
+ ) {
25
+ matched = FtJSON.areJsonEqual(req1.postData || {}, req2.postData || {});
26
+ } else if (
27
+ req1.postData &&
28
+ req2.postData &&
29
+ !FtJSON.areJsonEqual(req1.postData, req2.postData)
30
+ ) {
31
+ matched = false;
32
+ }
33
+ return matched;
34
+ };
35
+
36
+ const isSameResponse = (req1, req2) => {
37
+ try {
38
+ let matched = true;
39
+ if (req1.response.status !== req2.response.status) {
40
+ matched = false;
41
+ // console.log('not matched at url', req1.method, req2.method);
42
+ } else if (
43
+ (!req1.response.content && req2.response.content) ||
44
+ (req1.response.content && !req2.response.content)
45
+ ) {
46
+ matched = FtJSON.areJsonEqual(
47
+ FtJSON.parse(req1.response.content) || {},
48
+ FtJSON.parse(req2.response.content) || {}
49
+ );
50
+ // console.log('not matched at post Data 0', req1.postData, req2.postData);
51
+ } else if (
52
+ req1.response.content &&
53
+ req2.response.content &&
54
+ !FtJSON.areJsonEqual(
55
+ FtJSON.parse(req1.response.content) || {},
56
+ FtJSON.parse(req2.response.content) || {}
57
+ )
58
+ ) {
59
+ matched = false;
60
+ }
61
+ // if (matched) {
62
+ // console.log('matched responses', req1, req2);
63
+ // }
64
+ return matched;
65
+ } catch (error) {
66
+ console.error(error);
67
+ return false;
68
+ }
69
+ };
70
+
71
+ function compareMockToRequest(mock, req) {
72
+ const mockURL = processURL(
73
+ mock.fileContent.url,
74
+ mock.fileContent.ignoreParams
75
+ );
76
+ const reqURL = processURL(req.originalUrl, mock.fileContent.ignoreParams);
77
+ const isSameUrlAndMethod = isUrlAndMethodSame(
78
+ { url: mockURL, method: mock.fileContent.method },
79
+ { url: reqURL, method: req.method }
80
+ );
81
+ if (!isSameUrlAndMethod) {
82
+ return false;
83
+ }
84
+ const postData = mock.fileContent.request?.postData?.text
85
+ ? FtJSON.parse(mock.fileContent.request?.postData?.text)
86
+ : mock.fileContent.request?.postData;
87
+ return isSameRequest(
88
+ { url: mockURL, method: mock.fileContent.method, postData },
89
+ {
90
+ method: req.method,
91
+ postData: req.body,
92
+ url: reqURL,
93
+ }
94
+ );
95
+ }
96
+
97
+ function compareMockToFetchRequest(mock, fetchReq) {
98
+ try {
99
+ const mockURL = processURL(
100
+ mock.fileContent.url,
101
+ mock.fileContent.ignoreParams
102
+ );
103
+ const reqURL = processURL(fetchReq.url, mock.fileContent.ignoreParams);
104
+ const isSameUrlAndMethod = isUrlAndMethodSame(
105
+ { url: mockURL, method: mock.fileContent.method },
106
+ { url: reqURL, method: fetchReq.options.method || "GET" }
107
+ );
108
+ if (!isSameUrlAndMethod) {
109
+ return false;
110
+ }
111
+ const postData = mock.fileContent.request?.postData?.text
112
+ ? FtJSON.parse(mock.fileContent.request?.postData?.text)
113
+ : mock.fileContent.request?.postData;
114
+ return isSameRequest(
115
+ { url: mockURL, method: mock.fileContent.method, postData },
116
+ {
117
+ method: fetchReq.options.method || "GET",
118
+ postData: fetchReq.options.body?.length
119
+ ? FtJSON.parse(fetchReq.options.body)
120
+ : fetchReq.options.body,
121
+ url: reqURL,
122
+ }
123
+ );
124
+ } catch (e) {
125
+ console.error("error at compareMockToFetchRequest", mock, fetchReq);
126
+ console.error(e);
127
+ }
128
+ return false;
129
+ }
130
+
131
+ const compareMockToMock = (mock1, mock2, matchResponse) => {
132
+ try {
133
+ if (matchResponse) {
134
+ return isSameRequest(mock1, mock2) && isSameResponse(mock1, mock2);
135
+ } else {
136
+ return isSameRequest(mock1, mock2);
137
+ }
138
+ } catch (error) {
139
+ console.error(error);
140
+ return false;
141
+ }
142
+ };
143
+
144
+ module.exports = {
145
+ isUrlAndMethodSame,
146
+ isSameRequest,
147
+ isSameResponse,
148
+ compareMockToRequest,
149
+ compareMockToFetchRequest,
150
+ compareMockToMock,
151
+ };
package/src/index.js CHANGED
@@ -12,209 +12,25 @@ const {
12
12
  countFilesInDirectory,
13
13
  getTestByName,
14
14
  } = require("./common-utils");
15
+ const {
16
+ getDefaultMockDataFromConfig,
17
+ loadMockDataFromConfig,
18
+ resetAllMockStats,
19
+ } = require("./mock-utils");
20
+ const { createTest } = require("./test-utils");
15
21
  const { FtJSON } = require("./json-utils");
16
- const Logger = require("./log-utils");
22
+ const { Logger, deleteAllLogs } = require("./log-utils");
23
+ const {
24
+ isSameRequest,
25
+ compareMockToRequest,
26
+ compareMockToFetchRequest,
27
+ compareMockToMock,
28
+ } = require("./compare-utils");
29
+ const { getCompareRankMockToFetchRequest } = require("./rank-compare-utils");
17
30
 
18
31
  let logger = null;
19
32
 
20
- const getDefaultMockDataFromConfig = (testConfig) => {
21
- const defaultPath = path.join(
22
- getMockDir(testConfig),
23
- "defaultMocks",
24
- "_mock_list.json"
25
- );
26
-
27
- try {
28
- const defaultData = fs.readFileSync(defaultPath, "utf8");
29
- let parsedData = JSON.parse(defaultData);
30
-
31
- // Read and attach mock data for each entry in parsedData
32
- parsedData.forEach((entry) => {
33
- const mockFilePath = path.join(
34
- getMockDir(testConfig),
35
- "defaultMocks",
36
- `mock_${entry.id}.json`
37
- );
38
- try {
39
- const mockData = fs.readFileSync(mockFilePath, "utf8");
40
- entry.fileContent = JSON.parse(mockData);
41
- } catch (error) {
42
- console.error(`Error reading mock data for ${entry.id}:`, error);
43
- return entry; // Return the original entry if there's an error
44
- }
45
- });
46
- return parsedData;
47
- } catch (error) {
48
- console.error(`Error reading or parsing default mocks:`, error);
49
- return [];
50
- }
51
- };
52
-
53
33
  // src/index.js
54
- const loadMockDataFromConfig = (testConfig, _testName) => {
55
- try {
56
- let testName = _testName;
57
- if (!testName) {
58
- // Read the test ID from mockServer.config.json
59
- const configPath = path.join(
60
- getMockDir(testConfig),
61
- "mockServer.config.json"
62
- );
63
- const configData = fs.readFileSync(configPath, "utf8");
64
- const config = JSON.parse(configData);
65
- testName = config.testName;
66
- }
67
- // Read the tests from testConfig
68
- const mocksPath = path.join(
69
- getMockDir(testConfig),
70
- `test_${nameToFolder(testName)}`,
71
- "_mock_list.json"
72
- );
73
- const mocksData = fs.readFileSync(mocksPath, "utf8");
74
- const mocks = JSON.parse(mocksData);
75
-
76
- mocks.forEach((mock) => {
77
- const fileContent = JSON.parse(
78
- fs.readFileSync(
79
- path.join(
80
- getMockDir(testConfig),
81
- `test_${nameToFolder(testName)}`,
82
- `mock_${mock.id}.json`
83
- ),
84
- "utf8"
85
- )
86
- );
87
- mock.fileContent = fileContent;
88
- });
89
-
90
- return mocks;
91
- } catch (error) {
92
- console.error("Error loading test data:", error.message);
93
- return [];
94
- }
95
- };
96
-
97
- const isSameRequest = (req1, req2) => {
98
- clearNulls(req1.postData);
99
- clearNulls(req2.postData);
100
- let matched = true;
101
- if (req1.url !== req2.url) {
102
- matched = false;
103
- } else if (req1.method?.toLowerCase() !== req2.method?.toLowerCase()) {
104
- matched = false;
105
- } else if (
106
- (!req1.postData && req2.postData) ||
107
- (req1.postData && !req2.postData)
108
- ) {
109
- matched = FtJSON.areJsonEqual(req1.postData || {}, req2.postData || {});
110
- } else if (
111
- req1.postData &&
112
- req2.postData &&
113
- !FtJSON.areJsonEqual(req1.postData, req2.postData)
114
- ) {
115
- matched = false;
116
- }
117
- return matched;
118
- };
119
-
120
- const getSameRequestRank = (req1, req2) => {
121
- let rank = 1;
122
- clearNulls(req1.postData);
123
- clearNulls(req2.postData);
124
- // Compare path names
125
- const url1 = new URL(`http://domain.com${req1.url}`);
126
- const url2 = new URL(`http://domain.com${req2.url}`);
127
- if (url1.pathname !== url2.pathname) {
128
- rank = 0;
129
- } else if (url1.method?.toLowerCase() !== url2.method?.toLowerCase()) {
130
- rank = 0;
131
- } else {
132
- // Compare query strings
133
- const queryDiff = charDifference(url1.search || "", url2.search || "");
134
- rank = rank + queryDiff;
135
- // Compare post data
136
- const charDiff = charDifference(
137
- FtJSON.stringify(req1.postData || {}),
138
- FtJSON.stringify(req2.postData || {})
139
- );
140
- rank = rank + charDiff;
141
- }
142
- return rank;
143
- };
144
-
145
- function compareMockToRequest(mock, req) {
146
- const mockURL = processURL(
147
- mock.fileContent.url,
148
- mock.fileContent.ignoreParams
149
- );
150
- const reqURL = processURL(req.originalUrl, mock.fileContent.ignoreParams);
151
- const postData = mock.fileContent.request?.postData?.text
152
- ? FtJSON.parse(mock.fileContent.request?.postData?.text)
153
- : mock.fileContent.request?.postData;
154
- return isSameRequest(
155
- { url: mockURL, method: mock.fileContent.method, postData },
156
- {
157
- method: req.method,
158
- postData: req.body,
159
- url: reqURL,
160
- }
161
- );
162
- }
163
-
164
- function compareMockToFetchRequest(mock, fetchReq) {
165
- try {
166
- const mockURL = processURL(
167
- mock.fileContent.url,
168
- mock.fileContent.ignoreParams
169
- );
170
- const reqURL = processURL(fetchReq.url, mock.fileContent.ignoreParams);
171
- const postData = mock.fileContent.request?.postData?.text
172
- ? FtJSON.parse(mock.fileContent.request?.postData?.text)
173
- : mock.fileContent.request?.postData;
174
- return isSameRequest(
175
- { url: mockURL, method: mock.fileContent.method, postData },
176
- {
177
- method: fetchReq.options.method || "GET",
178
- postData: fetchReq.options.body?.length
179
- ? FtJSON.parse(fetchReq.options.body)
180
- : fetchReq.options.body,
181
- url: reqURL,
182
- }
183
- );
184
- } catch (e) {
185
- console.error("error at compareMockToFetchRequest", mock, fetchReq);
186
- console.error(e);
187
- }
188
- return false;
189
- }
190
-
191
- function getCompareRankMockToFetchRequest(mock, fetchReq) {
192
- try {
193
- const mockURL = processURL(
194
- mock.fileContent.url,
195
- mock.fileContent.ignoreParams
196
- );
197
- const reqURL = processURL(fetchReq.url, mock.fileContent.ignoreParams);
198
- const postData = mock.fileContent.request?.postData?.text
199
- ? FtJSON.parse(mock.fileContent.request?.postData?.text)
200
- : mock.fileContent.request?.postData;
201
- return getSameRequestRank(
202
- { url: mockURL, method: mock.fileContent.method, postData },
203
- {
204
- method: fetchReq.options.method || "GET",
205
- postData: fetchReq.options.body?.length
206
- ? FtJSON.parse(fetchReq.options.body)
207
- : fetchReq.options.body,
208
- url: reqURL,
209
- }
210
- );
211
- } catch (e) {
212
- console.error("error at getCompareRankMockToFetchRequest", mock, fetchReq);
213
- console.error(e);
214
- }
215
- return false;
216
- }
217
-
218
34
  function getMatchingMockData({
219
35
  testMockData,
220
36
  defaultMockData,
@@ -300,22 +116,6 @@ function getMatchingMockData({
300
116
  return foundMock ? foundMock.fileContent : null;
301
117
  }
302
118
 
303
- async function resetAllMockStats({ testMockData, testConfig, testName }) {
304
- for (let i = 0; i < testMockData.length; i++) {
305
- const tmd = testMockData[i];
306
- const mockFilePath = path.join(
307
- getMockDir(testConfig),
308
- `test_${nameToFolder(testName)}`,
309
- `mock_${tmd.id}.json`
310
- );
311
- tmd.fileContent.served = false;
312
- await fs.writeFileSync(
313
- mockFilePath,
314
- JSON.stringify(tmd.fileContent, null, 2)
315
- );
316
- }
317
- }
318
-
319
119
  async function initiatePlaywrightRoutes(
320
120
  page,
321
121
  ftmocksConifg,
@@ -323,7 +123,11 @@ async function initiatePlaywrightRoutes(
323
123
  mockPath = "**/*",
324
124
  excludeMockPath = null
325
125
  ) {
326
- logger = new Logger({disableLogs: ftmocksConifg.DISABLE_LOGS}, ftmocksConifg, testName);
126
+ logger = new Logger(
127
+ { disableLogs: ftmocksConifg.DISABLE_LOGS },
128
+ ftmocksConifg,
129
+ testName
130
+ );
327
131
  const testMockData = testName
328
132
  ? loadMockDataFromConfig(ftmocksConifg, testName)
329
133
  : [];
@@ -692,15 +496,6 @@ const deleteAllSnaps = async (ftmocksConifg, testName) => {
692
496
  fs.rmSync(snapFolder, { recursive: true, force: true });
693
497
  };
694
498
 
695
- const deleteAllLogs = async (ftmocksConifg, testName) => {
696
- const mockDir = path.join(
697
- getMockDir(ftmocksConifg),
698
- `test_${nameToFolder(testName)}`
699
- );
700
- const logFilePath = path.join(mockDir, `_logs.json`);
701
- fs.rmSync(logFilePath, { recursive: true, force: true });
702
- };
703
-
704
499
  function initiateJestEventSnaps(jest, ftmocksConifg, testName) {
705
500
  const mouseEvents = ftmocksConifg.snapEvents || [
706
501
  "click",
@@ -720,95 +515,6 @@ function initiateJestEventSnaps(jest, ftmocksConifg, testName) {
720
515
  });
721
516
  }
722
517
 
723
- const createTest = async (ftmocksConifg, testName) => {
724
- const testsPath = path.join(getMockDir(ftmocksConifg), "tests.json");
725
- let tests = [];
726
- try {
727
- // Read existing tests
728
- const testsData = fs.readFileSync(testsPath, "utf8");
729
- tests = JSON.parse(testsData);
730
- const etest = tests.find((tst) => tst.name === testName);
731
- if (!etest) {
732
- const newTest = {
733
- id: uuidv4(),
734
- name: testName,
735
- };
736
- tests.push(newTest);
737
- fs.writeFileSync(testsPath, JSON.stringify(tests, null, 2));
738
- const folderPath = path.join(
739
- getMockDir(ftmocksConifg),
740
- `test_${nameToFolder(testName)}`
741
- );
742
- const mockListFilePath = path.join(folderPath, "_mock_list.json");
743
- fs.mkdir(folderPath, { recursive: true }, (err) => {
744
- if (err) {
745
- console.error("\x1b[31mError creating directory:\x1b[0m", err);
746
- } else {
747
- console.log("\x1b[32mDirectory created successfully!\x1b[0m");
748
- }
749
- });
750
- await fs.appendFile(mockListFilePath, "[]", () => {
751
- console.log("\x1b[32mmock list file created successfully\x1b[0m");
752
- });
753
-
754
- return newTest;
755
- } else {
756
- throw "Test already exists";
757
- }
758
- } catch (error) {
759
- console.error(`\x1b[31mError reading tests.json:\x1b[0m`, error);
760
- return null;
761
- }
762
- };
763
-
764
- const isSameResponse = (req1, req2) => {
765
- try {
766
- let matched = true;
767
- if (req1.response.status !== req2.response.status) {
768
- matched = false;
769
- // console.log('not matched at url', req1.method, req2.method);
770
- } else if (
771
- (!req1.response.content && req2.response.content) ||
772
- (req1.response.content && !req2.response.content)
773
- ) {
774
- matched = FtJSON.areJsonEqual(
775
- FtJSON.parse(req1.response.content) || {},
776
- FtJSON.parse(req2.response.content) || {}
777
- );
778
- // console.log('not matched at post Data 0', req1.postData, req2.postData);
779
- } else if (
780
- req1.response.content &&
781
- req2.response.content &&
782
- !FtJSON.areJsonEqual(
783
- FtJSON.parse(req1.response.content) || {},
784
- FtJSON.parse(req2.response.content) || {}
785
- )
786
- ) {
787
- matched = false;
788
- }
789
- // if (matched) {
790
- // console.log('matched responses', req1, req2);
791
- // }
792
- return matched;
793
- } catch (error) {
794
- console.error(error);
795
- return false;
796
- }
797
- };
798
-
799
- const compareMockToMock = (mock1, mock2, matchResponse) => {
800
- try {
801
- if (matchResponse) {
802
- return isSameRequest(mock1, mock2) && isSameResponse(mock1, mock2);
803
- } else {
804
- return isSameRequest(mock1, mock2);
805
- }
806
- } catch (error) {
807
- console.error(error);
808
- return false;
809
- }
810
- };
811
-
812
518
  const saveIfItIsFile = async (route, testName, ftmocksConifg) => {
813
519
  const urlObj = new URL(route.request().url());
814
520
 
package/src/log-utils.js CHANGED
@@ -30,7 +30,11 @@ class Logger {
30
30
  time: Date.now(),
31
31
  source: "ftmocks-utils",
32
32
  });
33
- fs.writeFileSync(this.logsFile, JSON.stringify(this.logs, null, 2), "utf8"); // Append the log message to the file
33
+ fs.writeFileSync(
34
+ this.logsFile,
35
+ JSON.stringify(this.logs, null, 2),
36
+ "utf8"
37
+ ); // Append the log message to the file
34
38
  } catch (error) {
35
39
  // Ignore error
36
40
  }
@@ -87,4 +91,13 @@ class Logger {
87
91
  }
88
92
  }
89
93
 
90
- module.exports = Logger;
94
+ const deleteAllLogs = async (ftmocksConifg, testName) => {
95
+ const mockDir = path.join(
96
+ getMockDir(ftmocksConifg),
97
+ `test_${nameToFolder(testName)}`
98
+ );
99
+ const logFilePath = path.join(mockDir, `_logs.json`);
100
+ fs.rmSync(logFilePath, { recursive: true, force: true });
101
+ };
102
+
103
+ module.exports = { Logger, deleteAllLogs };
@@ -0,0 +1,101 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { getMockDir, nameToFolder } = require("./common-utils");
4
+
5
+ const getDefaultMockDataFromConfig = (testConfig) => {
6
+ const defaultPath = path.join(
7
+ getMockDir(testConfig),
8
+ "defaultMocks",
9
+ "_mock_list.json"
10
+ );
11
+
12
+ try {
13
+ const defaultData = fs.readFileSync(defaultPath, "utf8");
14
+ let parsedData = JSON.parse(defaultData);
15
+
16
+ // Read and attach mock data for each entry in parsedData
17
+ parsedData.forEach((entry) => {
18
+ const mockFilePath = path.join(
19
+ getMockDir(testConfig),
20
+ "defaultMocks",
21
+ `mock_${entry.id}.json`
22
+ );
23
+ try {
24
+ const mockData = fs.readFileSync(mockFilePath, "utf8");
25
+ entry.fileContent = JSON.parse(mockData);
26
+ } catch (error) {
27
+ console.error(`Error reading mock data for ${entry.id}:`, error);
28
+ return entry; // Return the original entry if there's an error
29
+ }
30
+ });
31
+ return parsedData;
32
+ } catch (error) {
33
+ console.error(`Error reading or parsing default mocks:`, error);
34
+ return [];
35
+ }
36
+ };
37
+
38
+ const loadMockDataFromConfig = (testConfig, _testName) => {
39
+ try {
40
+ let testName = _testName;
41
+ if (!testName) {
42
+ // Read the test ID from mockServer.config.json
43
+ const configPath = path.join(
44
+ getMockDir(testConfig),
45
+ "mockServer.config.json"
46
+ );
47
+ const configData = fs.readFileSync(configPath, "utf8");
48
+ const config = JSON.parse(configData);
49
+ testName = config.testName;
50
+ }
51
+ // Read the tests from testConfig
52
+ const mocksPath = path.join(
53
+ getMockDir(testConfig),
54
+ `test_${nameToFolder(testName)}`,
55
+ "_mock_list.json"
56
+ );
57
+ const mocksData = fs.readFileSync(mocksPath, "utf8");
58
+ const mocks = JSON.parse(mocksData);
59
+
60
+ mocks.forEach((mock) => {
61
+ const fileContent = JSON.parse(
62
+ fs.readFileSync(
63
+ path.join(
64
+ getMockDir(testConfig),
65
+ `test_${nameToFolder(testName)}`,
66
+ `mock_${mock.id}.json`
67
+ ),
68
+ "utf8"
69
+ )
70
+ );
71
+ mock.fileContent = fileContent;
72
+ });
73
+
74
+ return mocks;
75
+ } catch (error) {
76
+ console.error("Error loading test data:", error.message);
77
+ return [];
78
+ }
79
+ };
80
+
81
+ async function resetAllMockStats({ testMockData, testConfig, testName }) {
82
+ for (let i = 0; i < testMockData.length; i++) {
83
+ const tmd = testMockData[i];
84
+ const mockFilePath = path.join(
85
+ getMockDir(testConfig),
86
+ `test_${nameToFolder(testName)}`,
87
+ `mock_${tmd.id}.json`
88
+ );
89
+ tmd.fileContent.served = false;
90
+ await fs.writeFileSync(
91
+ mockFilePath,
92
+ JSON.stringify(tmd.fileContent, null, 2)
93
+ );
94
+ }
95
+ }
96
+
97
+ module.exports = {
98
+ getDefaultMockDataFromConfig,
99
+ loadMockDataFromConfig,
100
+ resetAllMockStats,
101
+ };
@@ -0,0 +1,62 @@
1
+ const { clearNulls, charDifference, processURL } = require("./common-utils");
2
+ const { FtJSON } = require("./json-utils");
3
+ const { isUrlAndMethodSame } = require("./compare-utils");
4
+
5
+ const getSameRequestRank = (req1, req2) => {
6
+ let rank = 1;
7
+ clearNulls(req1.postData);
8
+ clearNulls(req2.postData);
9
+ // Compare query strings
10
+ const queryDiff = charDifference(
11
+ req1.url.split("?")[1] || "",
12
+ req2.url.split("?")[1] || ""
13
+ );
14
+ rank = rank + queryDiff;
15
+ // Compare post data
16
+ const postDataDiff = charDifference(
17
+ FtJSON.stringify(req1.postData || {}),
18
+ FtJSON.stringify(req2.postData || {})
19
+ );
20
+ rank = rank + postDataDiff;
21
+ return rank;
22
+ };
23
+
24
+ function getCompareRankMockToFetchRequest(mock, fetchReq) {
25
+ try {
26
+ const mockURL = processURL(
27
+ mock.fileContent.url,
28
+ mock.fileContent.ignoreParams
29
+ );
30
+ const reqURL = processURL(fetchReq.url, mock.fileContent.ignoreParams);
31
+ if (
32
+ !isUrlAndMethodSame(
33
+ { url: mockURL, method: mock.fileContent.method },
34
+ { url: reqURL, method: fetchReq.options.method || "GET" }
35
+ )
36
+ ) {
37
+ return 0;
38
+ }
39
+ const postData = mock.fileContent.request?.postData?.text
40
+ ? FtJSON.parse(mock.fileContent.request?.postData?.text)
41
+ : mock.fileContent.request?.postData;
42
+ return getSameRequestRank(
43
+ { url: mockURL, method: mock.fileContent.method, postData },
44
+ {
45
+ method: fetchReq.options.method || "GET",
46
+ postData: fetchReq.options.body?.length
47
+ ? FtJSON.parse(fetchReq.options.body)
48
+ : fetchReq.options.body,
49
+ url: reqURL,
50
+ }
51
+ );
52
+ } catch (e) {
53
+ console.error("error at getCompareRankMockToFetchRequest", mock, fetchReq);
54
+ console.error(e);
55
+ }
56
+ return false;
57
+ }
58
+
59
+ module.exports = {
60
+ getSameRequestRank,
61
+ getCompareRankMockToFetchRequest,
62
+ };
package/src/recorder.js CHANGED
@@ -1,288 +1,306 @@
1
1
  window.FTMOCKS_CONFIG = {
2
- record_mocks_url: 'http://localhost:5000/api/v1/recordMockdata',
3
- record_events_url: 'http://localhost:5000/api/v1/recordedEvents'
2
+ record_mocks_url: "http://localhost:5000/api/v1/recordMockdata",
3
+ record_events_url: "http://localhost:5000/api/v1/recordedEvents",
4
4
  };
5
5
 
6
-
7
6
  (function () {
8
- // Intercept Fetch API
9
- const originalFetch = window.fetch;
10
- const recordedTracks = [];
11
-
12
- const addTrack = track => {
13
- track.id = recordedTracks.length ? recordedTracks[recordedTracks.length - 1].id + 1 : 1;
14
- track.time = new Date();
15
- // track.bodyHtml = document.documentElement.outerHTML.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');;
16
-
17
- fetch(window.FTMOCKS_CONFIG.record_events_url, {
18
- method: 'POST',
19
- headers: {
20
- 'Content-Type': 'application/json',
21
- },
22
- body: JSON.stringify(track),
23
- }).then(response => response.json());
24
- };
25
-
26
-
27
- window.fetch = async function (url, options = {}) {
28
- const method = options.method || 'GET';
29
- const body = options.body;
30
- const headers = options.headers || {};
31
- const queryString = url.includes('?') ? url.split('?')[1] : null;
32
- const response = await originalFetch(url, options);
33
- const ftMocksURL = new URL(window.FTMOCKS_CONFIG.record_mocks_url);
34
- const currentURL = new URL(url.startsWith('http') ? url : `http://something/${url}`);
35
- const clonedResponse = response.clone();
36
- clonedResponse.text().then((text) => {
37
- if (ftMocksURL.hostname !== currentURL.hostname) {
38
- const mockResponse = {
39
- url: url,
40
- time: new Date().toString(),
41
- method: method,
42
- request: {
43
- headers: headers,
44
- queryString: queryString,
45
- postData: {
46
- mimeType: headers['Content-Type'] || null,
47
- text: body
48
- }
49
- },
50
- response: {
51
- status: response.status,
52
- headers: Array.from(clonedResponse.headers.entries()),
53
- content: text
54
- },
55
- };
56
- fetch(window.FTMOCKS_CONFIG.record_mocks_url, {
57
- method: 'POST',
58
- headers: {
59
- 'Content-Type': 'application/json',
60
- },
61
- body: JSON.stringify(mockResponse),
62
- }).then(response => response.json());
63
- addTrack({
64
- type: mockResponse.method,
65
- target: mockResponse.url,
66
- });
67
- }
7
+ // Intercept Fetch API
8
+ const originalFetch = window.fetch;
9
+ const recordedTracks = [];
10
+
11
+ const addTrack = (track) => {
12
+ track.id = recordedTracks.length
13
+ ? recordedTracks[recordedTracks.length - 1].id + 1
14
+ : 1;
15
+ track.time = new Date();
16
+ // track.bodyHtml = document.documentElement.outerHTML.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');;
17
+
18
+ fetch(window.FTMOCKS_CONFIG.record_events_url, {
19
+ method: "POST",
20
+ headers: {
21
+ "Content-Type": "application/json",
22
+ },
23
+ body: JSON.stringify(track),
24
+ }).then((response) => response.json());
25
+ };
26
+
27
+ window.fetch = async function (url, options = {}) {
28
+ const method = options.method || "GET";
29
+ const body = options.body;
30
+ const headers = options.headers || {};
31
+ const queryString = url.includes("?") ? url.split("?")[1] : null;
32
+ const response = await originalFetch(url, options);
33
+ const ftMocksURL = new URL(window.FTMOCKS_CONFIG.record_mocks_url);
34
+ const currentURL = new URL(
35
+ url.startsWith("http") ? url : `http://something/${url}`
36
+ );
37
+ const clonedResponse = response.clone();
38
+ clonedResponse.text().then((text) => {
39
+ if (ftMocksURL.hostname !== currentURL.hostname) {
40
+ const mockResponse = {
41
+ url: url,
42
+ time: new Date().toString(),
43
+ method: method,
44
+ request: {
45
+ headers: headers,
46
+ queryString: queryString,
47
+ postData: {
48
+ mimeType: headers["Content-Type"] || null,
49
+ text: body,
50
+ },
51
+ },
52
+ response: {
53
+ status: response.status,
54
+ headers: Array.from(clonedResponse.headers.entries()),
55
+ content: text,
56
+ },
57
+ };
58
+ fetch(window.FTMOCKS_CONFIG.record_mocks_url, {
59
+ method: "POST",
60
+ headers: {
61
+ "Content-Type": "application/json",
62
+ },
63
+ body: JSON.stringify(mockResponse),
64
+ }).then((response) => response.json());
65
+ addTrack({
66
+ type: mockResponse.method,
67
+ target: mockResponse.url,
68
68
  });
69
- return response;
69
+ }
70
+ });
71
+ return response;
72
+ };
73
+
74
+ // Intercept XMLHttpRequest
75
+ const originalXHR = window.XMLHttpRequest;
76
+
77
+ function MockXHR() {
78
+ const xhr = new originalXHR();
79
+ const originalOpen = xhr.open;
80
+ const originalSend = xhr.send;
81
+ const originalSetRequestHeader = xhr.setRequestHeader;
82
+ let requestDetails = {
83
+ headers: {},
70
84
  };
71
-
72
- // Intercept XMLHttpRequest
73
- const originalXHR = window.XMLHttpRequest;
74
-
75
- function MockXHR() {
76
- const xhr = new originalXHR();
77
- const originalOpen = xhr.open;
78
- const originalSend = xhr.send;
79
- const originalSetRequestHeader = xhr.setRequestHeader;
80
- let requestDetails = {
81
- headers: {},
82
- };
83
-
84
- // Override 'open' method
85
- xhr.open = function (method, url, async, user, password) {
86
- requestDetails.method = method;
87
- requestDetails.url = url;
88
- requestDetails.async = async;
89
- requestDetails.user = user;
90
- requestDetails.password = password;
91
- requestDetails.queryString = url.includes('?') ? url.split('?')[1] : null;
92
- originalOpen.apply(xhr, arguments);
93
- };
94
-
95
- // Override 'setRequestHeader' to log headers
96
- xhr.setRequestHeader = function (header, value) {
97
- requestDetails.headers[header] = value;
98
- originalSetRequestHeader.apply(xhr, arguments);
99
- };
100
-
101
- // Override 'send' method
102
- xhr.send = function (body) {
103
- requestDetails.body = body;
104
- const originalOnReadyStateChange = xhr.onreadystatechange;
105
- xhr.onreadystatechange = function () {
106
- if (xhr.readyState === 4) { // Complete
107
- const ftMocksURL = new URL(window.FTMOCKS_CONFIG.record_mocks_url);
108
- const currentURL = new URL(requestDetails.url.startsWith('http') ? requestDetails.url : `http://something/${requestDetails.url}`);
109
- if (ftMocksURL.hostname !== currentURL.hostname) {
110
- const mockResponse = {
111
- url: requestDetails.url,
112
- time: new Date().toString(),
113
- method: requestDetails.method,
114
- request: {
115
- headers: requestDetails.headers,
116
- queryString: requestDetails.queryString,
117
- postData: {
118
- mimeType: requestDetails.headers['Content-Type'] || null,
119
- text: requestDetails.body
120
- }
121
- },
122
- response: {
123
- status: xhr.status,
124
- headers: xhr.getAllResponseHeaders(),
125
- content: xhr.responseText
126
- },
127
- };
128
- fetch(window.FTMOCKS_CONFIG.record_mocks_url, {
129
- method: 'POST',
130
- headers: {
131
- 'Content-Type': 'application/json',
132
- },
133
- body: JSON.stringify(mockResponse),
134
- }).then(response => response.json());
135
-
136
- }
137
- }
138
- if (originalOnReadyStateChange) originalOnReadyStateChange.apply(xhr, arguments);
85
+
86
+ // Override 'open' method
87
+ xhr.open = function (method, url, async, user, password) {
88
+ requestDetails.method = method;
89
+ requestDetails.url = url;
90
+ requestDetails.async = async;
91
+ requestDetails.user = user;
92
+ requestDetails.password = password;
93
+ requestDetails.queryString = url.includes("?") ? url.split("?")[1] : null;
94
+ originalOpen.apply(xhr, arguments);
95
+ };
96
+
97
+ // Override 'setRequestHeader' to log headers
98
+ xhr.setRequestHeader = function (header, value) {
99
+ requestDetails.headers[header] = value;
100
+ originalSetRequestHeader.apply(xhr, arguments);
101
+ };
102
+
103
+ // Override 'send' method
104
+ xhr.send = function (body) {
105
+ requestDetails.body = body;
106
+ const originalOnReadyStateChange = xhr.onreadystatechange;
107
+ xhr.onreadystatechange = function () {
108
+ if (xhr.readyState === 4) {
109
+ // Complete
110
+ const ftMocksURL = new URL(window.FTMOCKS_CONFIG.record_mocks_url);
111
+ const currentURL = new URL(
112
+ requestDetails.url.startsWith("http")
113
+ ? requestDetails.url
114
+ : `http://something/${requestDetails.url}`
115
+ );
116
+ if (ftMocksURL.hostname !== currentURL.hostname) {
117
+ const mockResponse = {
118
+ url: requestDetails.url,
119
+ time: new Date().toString(),
120
+ method: requestDetails.method,
121
+ request: {
122
+ headers: requestDetails.headers,
123
+ queryString: requestDetails.queryString,
124
+ postData: {
125
+ mimeType: requestDetails.headers["Content-Type"] || null,
126
+ text: requestDetails.body,
127
+ },
128
+ },
129
+ response: {
130
+ status: xhr.status,
131
+ headers: xhr.getAllResponseHeaders(),
132
+ content: xhr.responseText,
133
+ },
139
134
  };
140
- originalSend.apply(xhr, arguments);
141
- };
142
-
143
- return xhr;
144
- }
145
-
146
- window.XMLHttpRequest = MockXHR;
147
-
148
-
149
- const generateXPathWithNearestParentId = (element) => {
150
- let path = '';
151
- let nearestParentId = null;
152
-
153
- // Check if the current element's has an ID
154
- if (element.id) {
155
- nearestParentId = element.id;
135
+ fetch(window.FTMOCKS_CONFIG.record_mocks_url, {
136
+ method: "POST",
137
+ headers: {
138
+ "Content-Type": "application/json",
139
+ },
140
+ body: JSON.stringify(mockResponse),
141
+ }).then((response) => response.json());
142
+ }
156
143
  }
157
-
158
- while (!nearestParentId && element !== document.body && element) {
159
- const tagName = element.tagName.toLowerCase();
160
- let index = 1;
161
- let sibling = element.previousElementSibling;
162
-
163
- while (sibling) {
164
- if (sibling.tagName.toLowerCase() === tagName) {
165
- index += 1;
166
- }
167
- sibling = sibling.previousElementSibling;
168
- }
169
-
170
- if (index === 1) {
171
- path = `/${tagName}${path}`;
172
- } else {
173
- path = `/${tagName}[${index}]${path}`;
174
- }
175
-
176
- // Check if the current element's parent has an ID
177
- if (element.parentElement && element.parentElement.id) {
178
- nearestParentId = element.parentElement.id;
179
- break; // Stop searching when we find the nearest parent with an ID
180
- }
181
-
182
- element = element.parentElement;
183
- }
184
-
185
- if (nearestParentId) {
186
- path = `//*[@id='${nearestParentId}']${path}`;
187
- return path;
188
- }
189
- return null; // No parent with an ID found
144
+ if (originalOnReadyStateChange)
145
+ originalOnReadyStateChange.apply(xhr, arguments);
146
+ };
147
+ originalSend.apply(xhr, arguments);
190
148
  };
191
-
192
- const handleMouseEvent = (type, limit) => event => {
193
- const target = generateXPathWithNearestParentId(event.target);
194
- const track = {
195
- id: recordedTracks.length ? recordedTracks[recordedTracks.length - 1].id + 1 : 1,
196
- type,
197
- target,
198
- time: new Date(),
199
- };
200
- if(recordedTracks.length > limit + 1) {
201
- recordedTracks.shift();
149
+
150
+ return xhr;
151
+ }
152
+
153
+ window.XMLHttpRequest = MockXHR;
154
+
155
+ const generateXPathWithNearestParentId = (element) => {
156
+ let path = "";
157
+ let nearestParentId = null;
158
+
159
+ // Check if the current element's has an ID
160
+ if (element.id) {
161
+ nearestParentId = element.id;
162
+ }
163
+
164
+ while (!nearestParentId && element !== document.body && element) {
165
+ const tagName = element.tagName.toLowerCase();
166
+ let index = 1;
167
+ let sibling = element.previousElementSibling;
168
+
169
+ while (sibling) {
170
+ if (sibling.tagName.toLowerCase() === tagName) {
171
+ index += 1;
202
172
  }
203
- recordedTracks.push(track);
204
- addTrack(track);
173
+ sibling = sibling.previousElementSibling;
174
+ }
175
+
176
+ if (index === 1) {
177
+ path = `/${tagName}${path}`;
178
+ } else {
179
+ path = `/${tagName}[${index}]${path}`;
180
+ }
181
+
182
+ // Check if the current element's parent has an ID
183
+ if (element.parentElement && element.parentElement.id) {
184
+ nearestParentId = element.parentElement.id;
185
+ break; // Stop searching when we find the nearest parent with an ID
186
+ }
187
+
188
+ element = element.parentElement;
189
+ }
190
+
191
+ if (nearestParentId) {
192
+ path = `//*[@id='${nearestParentId}']${path}`;
193
+ return path;
194
+ }
195
+ return null; // No parent with an ID found
196
+ };
197
+
198
+ const handleMouseEvent = (type, limit) => (event) => {
199
+ const target = generateXPathWithNearestParentId(event.target);
200
+ const track = {
201
+ id: recordedTracks.length
202
+ ? recordedTracks[recordedTracks.length - 1].id + 1
203
+ : 1,
204
+ type,
205
+ target,
206
+ time: new Date(),
205
207
  };
206
-
207
- const handleChange = limit => event => {
208
- const prevCommand =
209
- recordedTracks && recordedTracks.length ? recordedTracks[recordedTracks.length - 1] : null;
210
- const target = generateXPathWithNearestParentId(event.target);
208
+ if (recordedTracks.length > limit + 1) {
209
+ recordedTracks.shift();
210
+ }
211
+ recordedTracks.push(track);
212
+ addTrack(track);
213
+ };
214
+
215
+ const handleChange = (limit) => (event) => {
216
+ const prevCommand =
217
+ recordedTracks && recordedTracks.length
218
+ ? recordedTracks[recordedTracks.length - 1]
219
+ : null;
220
+ const target = generateXPathWithNearestParentId(event.target);
221
+ const track = {
222
+ id: recordedTracks.length
223
+ ? recordedTracks[recordedTracks.length - 1].id + 1
224
+ : 1,
225
+ type: "change",
226
+ target,
227
+ value: event.target.value,
228
+ time: new Date(),
229
+ };
230
+ if (recordedTracks.length > limit + 1) {
231
+ recordedTracks.shift();
232
+ }
233
+ if (
234
+ prevCommand &&
235
+ prevCommand.type === "change" &&
236
+ prevCommand.target === target
237
+ ) {
238
+ recordedTracks.pop();
239
+ }
240
+ recordedTracks.push(track);
241
+ addTrack(track);
242
+ };
243
+
244
+ const handleDocumentLoad = (limit) => () => {
245
+ let oldHref = document.location.href;
246
+ const body = document.querySelector("body");
247
+ const observer = new MutationObserver((mutations) => {
248
+ if (oldHref !== document.location.href) {
249
+ oldHref = document.location.href;
211
250
  const track = {
212
- id: recordedTracks.length ? recordedTracks[recordedTracks.length - 1].id + 1 : 1,
213
- type: 'change',
214
- target,
215
- value: event.target.value,
216
- time: new Date(),
251
+ id: recordedTracks.length
252
+ ? recordedTracks[recordedTracks.length - 1].id + 1
253
+ : 1,
254
+ type: "url",
255
+ value: oldHref,
256
+ time: new Date(),
217
257
  };
218
- if(recordedTracks.length > limit + 1) {
219
- recordedTracks.shift();
220
- }
221
- if (
222
- prevCommand &&
223
- prevCommand.type === 'change' &&
224
- prevCommand.target === target
225
- ) {
226
- recordedTracks.pop();
258
+ if (recordedTracks.length > limit + 1) {
259
+ recordedTracks.shift();
227
260
  }
228
261
  recordedTracks.push(track);
229
262
  addTrack(track);
263
+ }
264
+ });
265
+ observer.observe(body, { childList: true, subtree: true });
266
+ };
267
+
268
+ const clearTracks = () => {
269
+ recordedTracks = [];
270
+ };
271
+
272
+ const getAllTracks = () => {
273
+ return recordedTracks;
274
+ };
275
+
276
+ const initTracks = (
277
+ initInfo = {
278
+ events: ["click", "change", "url", "dblclick", "contextmenu"],
279
+ limit: 100,
280
+ }
281
+ ) => {
282
+ const { events, limit } = initInfo;
283
+ const mouseEvents = {
284
+ click: handleMouseEvent("click", limit),
285
+ contextmenu: handleMouseEvent("contextmenu", limit),
286
+ dblclick: handleMouseEvent("dblclick", limit),
287
+ mousedown: handleMouseEvent("mousedown", limit),
288
+ mouseenter: handleMouseEvent("mouseenter", limit),
289
+ mouseleave: handleMouseEvent("mouseleave", limit),
290
+ mousemove: handleMouseEvent("mousemove", limit),
291
+ mouseout: handleMouseEvent("mouseout", limit),
292
+ mouseover: handleMouseEvent("mouseover", limit),
293
+ mouseup: handleMouseEvent("mouseup", limit),
230
294
  };
231
-
232
- const handleDocumentLoad = limit => () => {
233
- let oldHref = document.location.href;
234
- const body = document.querySelector('body');
235
- const observer = new MutationObserver(mutations => {
236
- if (oldHref !== document.location.href) {
237
- oldHref = document.location.href;
238
- const track = {
239
- id: recordedTracks.length ? recordedTracks[recordedTracks.length - 1].id + 1 : 1,
240
- type: 'url',
241
- value: oldHref,
242
- time: new Date(),
243
- };
244
- if(recordedTracks.length > limit + 1) {
245
- recordedTracks.shift();
246
- }
247
- recordedTracks.push(track);
248
- addTrack(track);
249
- }
250
- });
251
- observer.observe(body, { childList: true, subtree: true });
252
- };
253
-
254
- const clearTracks = () => {
255
- recordedTracks = [];
256
- };
257
-
258
- const getAllTracks = () => {
259
- return recordedTracks;
260
- };
261
-
262
- const initTracks = (initInfo = {events: ['click', 'change', 'url', 'dblclick', 'contextmenu'], limit: 100}) => {
263
- const {events, limit} = initInfo;
264
- const mouseEvents = {
265
- click: handleMouseEvent('click', limit),
266
- contextmenu: handleMouseEvent('contextmenu', limit),
267
- dblclick: handleMouseEvent('dblclick', limit),
268
- mousedown: handleMouseEvent('mousedown', limit),
269
- mouseenter: handleMouseEvent('mouseenter', limit),
270
- mouseleave: handleMouseEvent('mouseleave', limit),
271
- mousemove: handleMouseEvent('mousemove', limit),
272
- mouseout: handleMouseEvent('mouseout', limit),
273
- mouseover: handleMouseEvent('mouseover', limit),
274
- mouseup: handleMouseEvent('mouseup', limit),
275
- };
276
- events.forEach(e => {
277
- if(e === 'url') {
278
- window.onload = handleDocumentLoad(limit);
279
- } else if (e === 'change') {
280
- document.addEventListener('input', handleChange(limit));
281
- } else {
282
- document.addEventListener(e, mouseEvents[e]);
283
- }
284
- });
285
- };
286
- initTracks();
287
-
288
- })();
295
+ events.forEach((e) => {
296
+ if (e === "url") {
297
+ window.onload = handleDocumentLoad(limit);
298
+ } else if (e === "change") {
299
+ document.addEventListener("input", handleChange(limit));
300
+ } else {
301
+ document.addEventListener(e, mouseEvents[e]);
302
+ }
303
+ });
304
+ };
305
+ initTracks();
306
+ })();
@@ -0,0 +1,49 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { getMockDir, nameToFolder } = require("./common-utils");
4
+ const { v4: uuidv4 } = require("uuid");
5
+
6
+ const createTest = async (ftmocksConifg, testName) => {
7
+ const testsPath = path.join(getMockDir(ftmocksConifg), "tests.json");
8
+ let tests = [];
9
+ try {
10
+ // Read existing tests
11
+ const testsData = fs.readFileSync(testsPath, "utf8");
12
+ tests = JSON.parse(testsData);
13
+ const etest = tests.find((tst) => tst.name === testName);
14
+ if (!etest) {
15
+ const newTest = {
16
+ id: uuidv4(),
17
+ name: testName,
18
+ };
19
+ tests.push(newTest);
20
+ fs.writeFileSync(testsPath, JSON.stringify(tests, null, 2));
21
+ const folderPath = path.join(
22
+ getMockDir(ftmocksConifg),
23
+ `test_${nameToFolder(testName)}`
24
+ );
25
+ const mockListFilePath = path.join(folderPath, "_mock_list.json");
26
+ fs.mkdir(folderPath, { recursive: true }, (err) => {
27
+ if (err) {
28
+ console.error("\x1b[31mError creating directory:\x1b[0m", err);
29
+ } else {
30
+ console.log("\x1b[32mDirectory created successfully!\x1b[0m");
31
+ }
32
+ });
33
+ await fs.appendFile(mockListFilePath, "[]", () => {
34
+ console.log("\x1b[32mmock list file created successfully\x1b[0m");
35
+ });
36
+
37
+ return newTest;
38
+ } else {
39
+ throw "Test already exists";
40
+ }
41
+ } catch (error) {
42
+ console.error(`\x1b[31mError reading tests.json:\x1b[0m`, error);
43
+ return null;
44
+ }
45
+ };
46
+
47
+ module.exports = {
48
+ createTest,
49
+ };