ftmocks-utils 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ftmocks-utils",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Util functions for FtMocks",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/index.js CHANGED
@@ -183,6 +183,144 @@ async function resetAllMockStats({testMockData, testConfig, testName}) {
183
183
  }
184
184
  }
185
185
 
186
+ async function initiateJestFetch (jest, ftmocksConifg, testName) {
187
+ const testMockData = testName ? loadMockDataFromConfig(ftmocksConifg, testName) : [];
188
+ resetAllMockStats({testMockData, testConfig: ftmocksConifg, testName});
189
+ const defaultMockData = getDefaultMockDataFromConfig(ftmocksConifg);
190
+ global.fetch = jest.fn((url, options = {}) => {
191
+ let mockData = getMatchingMockData({testMockData, defaultMockData, url, options, testConfig: ftmocksConifg, testName});
192
+ if (mockData) {
193
+ console.debug('mocked', url, options);
194
+ } else {
195
+ console.debug('missing mock data', url, options);
196
+ return Promise.resolve({
197
+ status: 404,
198
+ headers: new Map([['content-type', 'application/json']]),
199
+ json: () => Promise.resolve({ error: 'Mock data not found' }),
200
+ });
201
+ }
202
+
203
+ const { content, headers, status } = mockData.response;
204
+
205
+ return Promise.resolve({
206
+ status,
207
+ headers,
208
+ json: () => Promise.resolve(JSON.parse(content)),
209
+ });
210
+ });
211
+
212
+ global.XMLHttpRequest = jest.fn(function () {
213
+ const xhrMock = {
214
+ open: jest.fn(),
215
+ send: jest.fn(),
216
+ setRequestHeader: jest.fn(),
217
+ getAllResponseHeaders: jest.fn(() => {
218
+ return '';
219
+ }),
220
+ getResponseHeader: jest.fn((header) => {
221
+ return null;
222
+ }),
223
+ readyState: 4,
224
+ status: 0,
225
+ response: null,
226
+ responseText: '',
227
+ onreadystatechange: null,
228
+ onload: null,
229
+ onerror: null,
230
+ };
231
+
232
+ xhrMock.send.mockImplementation(function () {
233
+ const mockData = getMatchingMockData({
234
+ testMockData,
235
+ defaultMockData,
236
+ url: xhrMock._url,
237
+ options: xhrMock._options,
238
+ testConfig: ftmocksConifg,
239
+ testName,
240
+ });
241
+
242
+ if (mockData) {
243
+ console.debug('mocked', xhrMock._url, xhrMock._options);
244
+ const { content, headers, status } = mockData.response;
245
+
246
+ xhrMock.status = status;
247
+ xhrMock.responseText = content;
248
+ xhrMock.response = content;
249
+ xhrMock.headers = headers;
250
+
251
+ if (xhrMock.onreadystatechange) {
252
+ xhrMock.onreadystatechange();
253
+ }
254
+ if (xhrMock.onload) {
255
+ xhrMock.onload();
256
+ }
257
+ } else {
258
+ console.debug('missing mock data', xhrMock._url, xhrMock._options);
259
+
260
+ xhrMock.status = 404;
261
+ xhrMock.responseText = JSON.stringify({ error: 'Mock data not found' });
262
+ xhrMock.response = xhrMock.responseText;
263
+
264
+ if (xhrMock.onreadystatechange) {
265
+ xhrMock.onreadystatechange();
266
+ }
267
+ if (xhrMock.onerror) {
268
+ xhrMock.onerror();
269
+ }
270
+ }
271
+ });
272
+
273
+ xhrMock.open.mockImplementation(function (method, url) {
274
+ xhrMock._options = { method };
275
+ xhrMock._url = url;
276
+ });
277
+
278
+ return xhrMock;
279
+ });
280
+
281
+ return;
282
+ };
283
+
284
+
285
+ function countFilesInDirectory(directoryPath) {
286
+ return new Promise((resolve, reject) => {
287
+ fs.readdir(directoryPath, (err, files) => {
288
+ if (err) {
289
+ return reject(err); // Handle error
290
+ }
291
+
292
+ // Filter out directories and only count files
293
+ const fileCount = files.filter(file => {
294
+ const filePath = path.join(directoryPath, file);
295
+ return fs.statSync(filePath).isFile();
296
+ }).length;
297
+
298
+ resolve(fileCount);
299
+ });
300
+ });
301
+ }
302
+
303
+ const saveSnap = async (html, ftmocksConifg, testName) => {
304
+ const snapFolder = path.join(ftmocksConifg.MOCK_DIR, `test_${nameToFolder(testName)}`, '_snaps');
305
+ const snapTemplate = path.join(ftmocksConifg.MOCK_DIR, 'snap_template.html');
306
+
307
+ if (!fs.existsSync(snapFolder)) {
308
+ fs.mkdirSync(snapFolder);
309
+ }
310
+ const fileCount = await (countFilesInDirectory(snapFolder));
311
+ const snapFilePath = path.join(snapFolder, `snap_${fileCount + 1}.html`);
312
+ let resHtml = html;
313
+ if (fs.existsSync(snapFolder)) {
314
+ const templateHtml = fs.readFileSync(snapTemplate, 'utf8');;
315
+ resHtml = templateHtml.replace('<!--FtMocks-Snap-Template-To-Be-Replaced-->', html)
316
+ }
317
+ fs.writeFileSync(snapFilePath, resHtml)
318
+ };
319
+
320
+ const deleteAllSnaps = async (ftmocksConifg, testName) => {
321
+ const snapFolder = path.join(ftmocksConifg.MOCK_DIR, `test_${nameToFolder(testName)}`, '_snaps');
322
+ fs.rmSync(snapFolder, { recursive: true, force: true });
323
+ };
186
324
 
187
325
  // Export functions as a module
188
326
  module.exports = {
@@ -194,5 +332,8 @@ module.exports = {
194
332
  nameToFolder,
195
333
  compareMockToFetchRequest,
196
334
  getMatchingMockData,
197
- resetAllMockStats
335
+ resetAllMockStats,
336
+ initiateJestFetch,
337
+ saveSnap,
338
+ deleteAllSnaps
198
339
  };
package/src/recorder.js CHANGED
@@ -2,10 +2,12 @@
2
2
  // Intercept Fetch API
3
3
  const originalFetch = window.fetch;
4
4
  const recordedTracks = [];
5
-
5
+
6
6
  const addTrack = track => {
7
7
  track.id = recordedTracks.length ? recordedTracks[recordedTracks.length - 1].id + 1 : 1;
8
8
  track.time = new Date();
9
+ track.bodyHtml = document.documentElement.outerHTML.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');;
10
+
9
11
  fetch(window.FTMOCKS_CONFIG.record_events_url, {
10
12
  method: 'POST',
11
13
  headers: {
@@ -14,8 +16,8 @@
14
16
  body: JSON.stringify(track),
15
17
  }).then(response => response.json());
16
18
  };
17
-
18
-
19
+
20
+
19
21
  window.fetch = async function (url, options = {}) {
20
22
  const method = options.method || 'GET';
21
23
  const body = options.body;
@@ -60,10 +62,10 @@
60
62
  });
61
63
  return response;
62
64
  };
63
-
65
+
64
66
  // Intercept XMLHttpRequest
65
67
  const originalXHR = window.XMLHttpRequest;
66
-
68
+
67
69
  function MockXHR() {
68
70
  const xhr = new originalXHR();
69
71
  const originalOpen = xhr.open;
@@ -72,7 +74,7 @@
72
74
  let requestDetails = {
73
75
  headers: {},
74
76
  };
75
-
77
+
76
78
  // Override 'open' method
77
79
  xhr.open = function (method, url, async, user, password) {
78
80
  requestDetails.method = method;
@@ -83,13 +85,13 @@
83
85
  requestDetails.queryString = url.includes('?') ? url.split('?')[1] : null;
84
86
  originalOpen.apply(xhr, arguments);
85
87
  };
86
-
88
+
87
89
  // Override 'setRequestHeader' to log headers
88
90
  xhr.setRequestHeader = function (header, value) {
89
91
  requestDetails.headers[header] = value;
90
92
  originalSetRequestHeader.apply(xhr, arguments);
91
93
  };
92
-
94
+
93
95
  // Override 'send' method
94
96
  xhr.send = function (body) {
95
97
  requestDetails.body = body;
@@ -124,63 +126,63 @@
124
126
  },
125
127
  body: JSON.stringify(mockResponse),
126
128
  }).then(response => response.json());
127
-
129
+
128
130
  }
129
131
  }
130
132
  if (originalOnReadyStateChange) originalOnReadyStateChange.apply(xhr, arguments);
131
133
  };
132
134
  originalSend.apply(xhr, arguments);
133
135
  };
134
-
136
+
135
137
  return xhr;
136
138
  }
137
-
139
+
138
140
  window.XMLHttpRequest = MockXHR;
139
-
140
-
141
+
142
+
141
143
  const generateXPathWithNearestParentId = (element) => {
142
144
  let path = '';
143
145
  let nearestParentId = null;
144
-
146
+
145
147
  // Check if the current element's has an ID
146
148
  if (element.id) {
147
149
  nearestParentId = element.id;
148
150
  }
149
-
151
+
150
152
  while (!nearestParentId && element !== document.body && element) {
151
153
  const tagName = element.tagName.toLowerCase();
152
154
  let index = 1;
153
155
  let sibling = element.previousElementSibling;
154
-
156
+
155
157
  while (sibling) {
156
158
  if (sibling.tagName.toLowerCase() === tagName) {
157
159
  index += 1;
158
160
  }
159
161
  sibling = sibling.previousElementSibling;
160
162
  }
161
-
163
+
162
164
  if (index === 1) {
163
165
  path = `/${tagName}${path}`;
164
166
  } else {
165
167
  path = `/${tagName}[${index}]${path}`;
166
168
  }
167
-
169
+
168
170
  // Check if the current element's parent has an ID
169
171
  if (element.parentElement && element.parentElement.id) {
170
172
  nearestParentId = element.parentElement.id;
171
173
  break; // Stop searching when we find the nearest parent with an ID
172
174
  }
173
-
175
+
174
176
  element = element.parentElement;
175
177
  }
176
-
178
+
177
179
  if (nearestParentId) {
178
180
  path = `//*[@id='${nearestParentId}']${path}`;
179
181
  return path;
180
182
  }
181
183
  return null; // No parent with an ID found
182
184
  };
183
-
185
+
184
186
  const handleMouseEvent = (type, limit) => event => {
185
187
  const target = generateXPathWithNearestParentId(event.target);
186
188
  const track = {
@@ -195,7 +197,7 @@
195
197
  recordedTracks.push(track);
196
198
  addTrack(track);
197
199
  };
198
-
200
+
199
201
  const handleChange = limit => event => {
200
202
  const prevCommand =
201
203
  recordedTracks && recordedTracks.length ? recordedTracks[recordedTracks.length - 1] : null;
@@ -220,7 +222,7 @@
220
222
  recordedTracks.push(track);
221
223
  addTrack(track);
222
224
  };
223
-
225
+
224
226
  const handleDocumentLoad = limit => () => {
225
227
  let oldHref = document.location.href;
226
228
  const body = document.querySelector('body');
@@ -242,15 +244,15 @@
242
244
  });
243
245
  observer.observe(body, { childList: true, subtree: true });
244
246
  };
245
-
247
+
246
248
  const clearTracks = () => {
247
249
  recordedTracks = [];
248
250
  };
249
-
251
+
250
252
  const getAllTracks = () => {
251
253
  return recordedTracks;
252
254
  };
253
-
255
+
254
256
  const initTracks = (initInfo = {events: ['click', 'change', 'url', 'dblclick', 'contextmenu'], limit: 100}) => {
255
257
  const {events, limit} = initInfo;
256
258
  const mouseEvents = {
@@ -276,5 +278,5 @@
276
278
  });
277
279
  };
278
280
  initTracks();
279
-
280
- })();
281
+
282
+ })();