@dev-blinq/cucumber_client 1.0.1345-dev → 1.0.1345-stage
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/bin/assets/bundled_scripts/recorder.js +107 -107
- package/bin/assets/preload/css_gen.js +10 -10
- package/bin/assets/preload/recorderv3.js +3 -1
- package/bin/assets/preload/toolbar.js +27 -29
- package/bin/assets/preload/unique_locators.js +1 -1
- package/bin/assets/preload/yaml.js +288 -275
- package/bin/assets/scripts/aria_snapshot.js +223 -220
- package/bin/assets/scripts/dom_attr.js +329 -329
- package/bin/assets/scripts/dom_parent.js +169 -174
- package/bin/assets/scripts/event_utils.js +94 -94
- package/bin/assets/scripts/pw.js +2050 -1949
- package/bin/assets/scripts/recorder.js +12 -22
- package/bin/assets/scripts/snapshot_capturer.js +153 -146
- package/bin/assets/scripts/unique_locators.js +941 -815
- package/bin/assets/scripts/yaml.js +796 -783
- package/bin/assets/templates/_hooks_template.txt +41 -0
- package/bin/assets/templates/utils_template.txt +2 -45
- package/bin/client/apiTest/apiTest.js +6 -0
- package/bin/client/cli_helpers.js +11 -13
- package/bin/client/code_cleanup/utils.js +5 -1
- package/bin/client/code_gen/api_codegen.js +2 -2
- package/bin/client/code_gen/code_inversion.js +112 -4
- package/bin/client/code_gen/page_reflection.js +839 -906
- package/bin/client/code_gen/playwright_codeget.js +26 -18
- package/bin/client/cucumber/feature.js +89 -27
- package/bin/client/cucumber/feature_data.js +2 -2
- package/bin/client/cucumber/project_to_document.js +9 -3
- package/bin/client/cucumber/steps_definitions.js +6 -3
- package/bin/client/cucumber_selector.js +17 -1
- package/bin/client/local_agent.js +6 -5
- package/bin/client/parse_feature_file.js +23 -26
- package/bin/client/playground/projects/env.json +2 -2
- package/bin/client/project.js +186 -196
- package/bin/client/recorderv3/bvt_recorder.js +202 -89
- package/bin/client/recorderv3/implemented_steps.js +24 -14
- package/bin/client/recorderv3/index.js +59 -54
- package/bin/client/recorderv3/network.js +22 -5
- package/bin/client/recorderv3/scriptTest.js +1 -1
- package/bin/client/recorderv3/services.js +4 -16
- package/bin/client/recorderv3/step_runner.js +318 -209
- package/bin/client/recorderv3/step_utils.js +476 -17
- package/bin/client/recorderv3/update_feature.js +32 -30
- package/bin/client/recording.js +1 -0
- package/bin/client/run_cucumber.js +1 -1
- package/bin/client/scenario_report.js +0 -5
- package/bin/client/test_scenario.js +0 -1
- package/bin/client/upload-service.js +3 -2
- package/bin/client/utils/socket_logger.js +132 -0
- package/bin/index.js +1 -0
- package/bin/logger.js +3 -2
- package/bin/min/consoleApi.min.cjs +2 -3
- package/bin/min/injectedScript.min.cjs +16 -16
- package/package.json +21 -12
|
@@ -12,11 +12,12 @@ import { updateFeatureFile } from "./update_feature.js";
|
|
|
12
12
|
import { parseStepTextParameters } from "../cucumber/utils.js";
|
|
13
13
|
import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
|
|
14
14
|
import chokidar from "chokidar";
|
|
15
|
-
import logger from "../../logger.js";
|
|
16
15
|
import { unEscapeNonPrintables } from "../cucumber/utils.js";
|
|
17
16
|
import { findAvailablePort } from "../utils/index.js";
|
|
18
|
-
import
|
|
19
|
-
|
|
17
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
18
|
+
import { tmpdir } from "os";
|
|
19
|
+
import { faker } from "@faker-js/faker/locale/en_US";
|
|
20
|
+
import { chromium } from "playwright-core";
|
|
20
21
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
21
22
|
|
|
22
23
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -45,15 +46,15 @@ async function evaluate(frame, script) {
|
|
|
45
46
|
async function findNestedFrameSelector(frame, obj) {
|
|
46
47
|
try {
|
|
47
48
|
const parent = frame.parentFrame();
|
|
48
|
-
if (parent) console.log(`Parent frame: ${JSON.stringify(parent)}`);
|
|
49
49
|
if (!parent) return { children: obj };
|
|
50
50
|
const frameElement = await frame.frameElement();
|
|
51
51
|
if (!frameElement) return;
|
|
52
52
|
const selectors = await parent.evaluate((element) => {
|
|
53
|
-
return window.
|
|
53
|
+
return window.__bvt_Recorder.locatorGenerator.getElementLocators(element, { excludeText: true }).locators;
|
|
54
54
|
}, frameElement);
|
|
55
55
|
return findNestedFrameSelector(parent, { children: obj, selectors });
|
|
56
56
|
} catch (e) {
|
|
57
|
+
socketLogger.error(`Error in findNestedFrameSelector: ${e}`);
|
|
57
58
|
console.error(e);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
@@ -150,6 +151,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
|
|
|
150
151
|
};
|
|
151
152
|
}
|
|
152
153
|
default: {
|
|
154
|
+
socketLogger.error(`Action not supported: ${action.name}`);
|
|
153
155
|
console.log("action not supported", action);
|
|
154
156
|
throw new Error("action not supported");
|
|
155
157
|
}
|
|
@@ -161,6 +163,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
|
|
|
161
163
|
* @property {string} projectDir
|
|
162
164
|
* @property {string} TOKEN
|
|
163
165
|
* @property {(name:string, data:any)=> void} sendEvent
|
|
166
|
+
* @property {Object} logger
|
|
164
167
|
*/
|
|
165
168
|
export class BVTRecorder {
|
|
166
169
|
#currentURL = "";
|
|
@@ -174,7 +177,6 @@ export class BVTRecorder {
|
|
|
174
177
|
*/
|
|
175
178
|
constructor(initialState) {
|
|
176
179
|
Object.assign(this, initialState);
|
|
177
|
-
this.logger = logger;
|
|
178
180
|
this.screenshotMap = new Map();
|
|
179
181
|
this.snapshotMap = new Map();
|
|
180
182
|
this.scenariosStepsMap = new Map();
|
|
@@ -184,40 +186,16 @@ export class BVTRecorder {
|
|
|
184
186
|
projectDir: this.projectDir,
|
|
185
187
|
logger: this.logger,
|
|
186
188
|
});
|
|
187
|
-
this.stepRunner = new BVTStepRunner({
|
|
188
|
-
projectDir: this.projectDir,
|
|
189
|
-
sendExecutionStatus: (data) => {
|
|
190
|
-
if (data && data.type) {
|
|
191
|
-
switch (data.type) {
|
|
192
|
-
case "cmdExecutionStart":
|
|
193
|
-
console.log("Sending cmdExecutionStart event for cmdId:", data);
|
|
194
|
-
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
195
|
-
break;
|
|
196
|
-
case "cmdExecutionSuccess":
|
|
197
|
-
console.log("Sending cmdExecutionSuccess event for cmdId:", data);
|
|
198
|
-
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
199
|
-
break;
|
|
200
|
-
case "cmdExecutionError":
|
|
201
|
-
console.log("Sending cmdExecutionError event for cmdId:", data);
|
|
202
|
-
this.sendEvent(this.events.cmdExecutionError, data);
|
|
203
|
-
break;
|
|
204
|
-
case "interceptResults":
|
|
205
|
-
console.log("Sending interceptResults event");
|
|
206
|
-
this.sendEvent(this.events.interceptResults, data);
|
|
207
|
-
break;
|
|
208
|
-
default:
|
|
209
|
-
console.warn("Unknown command execution status type:", data.type);
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
});
|
|
215
189
|
this.pageSet = new Set();
|
|
190
|
+
this.pageMetaDataSet = new Set();
|
|
216
191
|
this.lastKnownUrlPath = "";
|
|
217
|
-
// TODO: what is world?
|
|
218
192
|
this.world = { attach: () => {} };
|
|
219
193
|
this.shouldTakeScreenshot = true;
|
|
220
194
|
this.watcher = null;
|
|
195
|
+
this.networkEventsFolder = path.join(tmpdir(), "blinq_network_events");
|
|
196
|
+
if (existsSync(this.networkEventsFolder)) {
|
|
197
|
+
rmSync(this.networkEventsFolder, { recursive: true, force: true });
|
|
198
|
+
}
|
|
221
199
|
}
|
|
222
200
|
events = {
|
|
223
201
|
onFrameNavigate: "BVTRecorder.onFrameNavigate",
|
|
@@ -232,12 +210,14 @@ export class BVTRecorder {
|
|
|
232
210
|
cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
|
|
233
211
|
cmdExecutionError: "BVTRecorder.cmdExecutionError",
|
|
234
212
|
interceptResults: "BVTRecorder.interceptResults",
|
|
213
|
+
onDebugURLChange: "BVTRecorder.onDebugURLChange",
|
|
214
|
+
updateCommand: "BVTRecorder.updateCommand",
|
|
235
215
|
};
|
|
236
216
|
bindings = {
|
|
237
217
|
__bvt_recordCommand: async ({ frame, page, context }, event) => {
|
|
238
218
|
this.#activeFrame = frame;
|
|
239
219
|
const nestFrmLoc = await findNestedFrameSelector(frame);
|
|
240
|
-
|
|
220
|
+
this.logger.info(`Time taken for action: ${event.statistics.time}`);
|
|
241
221
|
await this.onAction({ ...event, nestFrmLoc });
|
|
242
222
|
},
|
|
243
223
|
__bvt_getMode: async () => {
|
|
@@ -256,8 +236,7 @@ export class BVTRecorder {
|
|
|
256
236
|
await this.onClosePopup();
|
|
257
237
|
},
|
|
258
238
|
__bvt_log: async (src, message) => {
|
|
259
|
-
|
|
260
|
-
console.log(`Inside Browser: ${message}`);
|
|
239
|
+
this.logger.info(`Inside Browser: ${message}`);
|
|
261
240
|
},
|
|
262
241
|
__bvt_getObject: (_src, obj) => {
|
|
263
242
|
this.processObject(obj);
|
|
@@ -269,7 +248,6 @@ export class BVTRecorder {
|
|
|
269
248
|
const locator = await this.web.page.locator(selector);
|
|
270
249
|
const snapshot = await locator.ariaSnapshot();
|
|
271
250
|
return snapshot;
|
|
272
|
-
// Triggering workflow
|
|
273
251
|
};
|
|
274
252
|
|
|
275
253
|
processObject = async ({ type, action, value }) => {
|
|
@@ -320,10 +298,11 @@ export class BVTRecorder {
|
|
|
320
298
|
}
|
|
321
299
|
|
|
322
300
|
async _initBrowser({ url }) {
|
|
301
|
+
socketLogger.info("Only present in 1.0.1293-stage");
|
|
323
302
|
this.#remoteDebuggerPort = await findAvailablePort();
|
|
324
303
|
process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
|
|
325
304
|
|
|
326
|
-
this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
305
|
+
// this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
327
306
|
this.world = { attach: () => {} };
|
|
328
307
|
|
|
329
308
|
const ai_config_file = path.join(this.projectDir, "ai_config.json");
|
|
@@ -332,9 +311,10 @@ export class BVTRecorder {
|
|
|
332
311
|
try {
|
|
333
312
|
ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
|
|
334
313
|
} catch (error) {
|
|
335
|
-
|
|
314
|
+
this.logger.error("Error reading ai_config.json", error);
|
|
336
315
|
}
|
|
337
316
|
}
|
|
317
|
+
this.config = ai_config;
|
|
338
318
|
const initScripts = {
|
|
339
319
|
// recorderCjs: injectedScriptSource,
|
|
340
320
|
scripts: [
|
|
@@ -343,16 +323,37 @@ export class BVTRecorder {
|
|
|
343
323
|
],
|
|
344
324
|
};
|
|
345
325
|
|
|
346
|
-
let startTime = Date.now();
|
|
347
326
|
const bvtContext = await initContext(url, false, false, this.world, 450, initScripts, this.envName);
|
|
348
|
-
let stopTime = Date.now();
|
|
349
|
-
this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
|
|
350
327
|
this.bvtContext = bvtContext;
|
|
328
|
+
this.stepRunner = new BVTStepRunner({
|
|
329
|
+
projectDir: this.projectDir,
|
|
330
|
+
sendExecutionStatus: (data) => {
|
|
331
|
+
if (data && data.type) {
|
|
332
|
+
switch (data.type) {
|
|
333
|
+
case "cmdExecutionStart":
|
|
334
|
+
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
335
|
+
break;
|
|
336
|
+
case "cmdExecutionSuccess":
|
|
337
|
+
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
338
|
+
break;
|
|
339
|
+
case "cmdExecutionError":
|
|
340
|
+
this.sendEvent(this.events.cmdExecutionError, data);
|
|
341
|
+
break;
|
|
342
|
+
case "interceptResults":
|
|
343
|
+
this.sendEvent(this.events.interceptResults, data);
|
|
344
|
+
break;
|
|
345
|
+
default:
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
bvtContext: this.bvtContext,
|
|
351
|
+
});
|
|
351
352
|
const context = bvtContext.playContext;
|
|
352
353
|
this.context = context;
|
|
353
354
|
this.web = bvtContext.stable || bvtContext.web;
|
|
355
|
+
this.web.tryAllStrategies = true;
|
|
354
356
|
this.page = bvtContext.page;
|
|
355
|
-
|
|
356
357
|
this.pageSet.add(this.page);
|
|
357
358
|
this.lastKnownUrlPath = this._updateUrlPath();
|
|
358
359
|
const browser = await this.context.browser();
|
|
@@ -366,6 +367,14 @@ export class BVTRecorder {
|
|
|
366
367
|
this.web.onRestoreSaveState = (url) => {
|
|
367
368
|
this._initBrowser({ url });
|
|
368
369
|
};
|
|
370
|
+
|
|
371
|
+
// create a second browser for locator generation
|
|
372
|
+
this.backgroundBrowser = await chromium.launch({
|
|
373
|
+
headless: true,
|
|
374
|
+
});
|
|
375
|
+
this.backgroundContext = await this.backgroundBrowser.newContext({});
|
|
376
|
+
await this.backgroundContext.addInitScript({ content: this.getInitScripts(this.config) });
|
|
377
|
+
await this.backgroundContext.newPage();
|
|
369
378
|
}
|
|
370
379
|
async onClosePopup() {
|
|
371
380
|
// console.log("close popups");
|
|
@@ -380,13 +389,15 @@ export class BVTRecorder {
|
|
|
380
389
|
}
|
|
381
390
|
return;
|
|
382
391
|
} catch (error) {
|
|
383
|
-
console.error("Error evaluting in context:", error);
|
|
392
|
+
// console.error("Error evaluting in context:", error);
|
|
393
|
+
this.logger.error("Error evaluating in context:", error);
|
|
384
394
|
}
|
|
385
395
|
}
|
|
386
396
|
}
|
|
387
397
|
|
|
388
398
|
getMode() {
|
|
389
|
-
console.log("getMode", this.#mode);
|
|
399
|
+
// console.log("getMode", this.#mode);
|
|
400
|
+
this.logger.info("Current mode:", this.#mode);
|
|
390
401
|
return this.#mode;
|
|
391
402
|
}
|
|
392
403
|
|
|
@@ -428,6 +439,8 @@ export class BVTRecorder {
|
|
|
428
439
|
this.sendEvent(this.events.onBrowserClose);
|
|
429
440
|
}
|
|
430
441
|
} catch (error) {
|
|
442
|
+
this.logger.error("Error in page close event");
|
|
443
|
+
this.logger.error(error);
|
|
431
444
|
console.error("Error in page close event");
|
|
432
445
|
console.error(error);
|
|
433
446
|
}
|
|
@@ -438,8 +451,10 @@ export class BVTRecorder {
|
|
|
438
451
|
if (frame !== page.mainFrame()) return;
|
|
439
452
|
this.handlePageTransition();
|
|
440
453
|
} catch (error) {
|
|
454
|
+
this.logger.error("Error in handlePageTransition event");
|
|
455
|
+
this.logger.error(error);
|
|
441
456
|
console.error("Error in handlePageTransition event");
|
|
442
|
-
|
|
457
|
+
console.error(error);
|
|
443
458
|
}
|
|
444
459
|
try {
|
|
445
460
|
if (frame !== this.#activeFrame) return;
|
|
@@ -458,6 +473,8 @@ export class BVTRecorder {
|
|
|
458
473
|
// await this._setRecordingMode(frame);
|
|
459
474
|
// await this._initPage(page);
|
|
460
475
|
} catch (error) {
|
|
476
|
+
this.logger.error("Error in frame navigate event");
|
|
477
|
+
this.logger.error(error);
|
|
461
478
|
console.error("Error in frame navigate event");
|
|
462
479
|
// console.error(error);
|
|
463
480
|
}
|
|
@@ -540,13 +557,9 @@ export class BVTRecorder {
|
|
|
540
557
|
|
|
541
558
|
try {
|
|
542
559
|
const result = await client.send("Page.getNavigationHistory");
|
|
543
|
-
// console.log("Navigation History:", result);
|
|
544
560
|
const entries = result.entries;
|
|
545
561
|
const currentIndex = result.currentIndex;
|
|
546
562
|
|
|
547
|
-
// ignore if currentIndex is not the last entry
|
|
548
|
-
// if (currentIndex !== entries.length - 1) return;
|
|
549
|
-
|
|
550
563
|
const currentEntry = entries[currentIndex];
|
|
551
564
|
const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
|
|
552
565
|
this.previousIndex = currentIndex;
|
|
@@ -559,6 +572,8 @@ export class BVTRecorder {
|
|
|
559
572
|
navigationAction: transitionInfo.action,
|
|
560
573
|
};
|
|
561
574
|
} catch (error) {
|
|
575
|
+
this.logger.error("Error in getCurrentTransition event");
|
|
576
|
+
this.logger.error(error);
|
|
562
577
|
console.error("Error in getTransistionType event", error);
|
|
563
578
|
} finally {
|
|
564
579
|
await client.detach();
|
|
@@ -623,9 +638,12 @@ export class BVTRecorder {
|
|
|
623
638
|
this.pageSet.add(page);
|
|
624
639
|
|
|
625
640
|
await page.waitForLoadState("domcontentloaded");
|
|
641
|
+
|
|
626
642
|
// add listener for frame navigation on new tab
|
|
627
643
|
this._addFrameNavigateListener(page);
|
|
628
644
|
} catch (error) {
|
|
645
|
+
this.logger.error("Error in page event");
|
|
646
|
+
this.logger.error(error);
|
|
629
647
|
console.error("Error in page event");
|
|
630
648
|
console.error(error);
|
|
631
649
|
}
|
|
@@ -667,6 +685,7 @@ export class BVTRecorder {
|
|
|
667
685
|
const { data } = await client.send("Page.captureScreenshot", { format: "png" });
|
|
668
686
|
return data;
|
|
669
687
|
} catch (error) {
|
|
688
|
+
this.logger.error("Error in taking browser screenshot");
|
|
670
689
|
console.error("Error in taking browser screenshot", error);
|
|
671
690
|
} finally {
|
|
672
691
|
await client.detach();
|
|
@@ -682,6 +701,52 @@ export class BVTRecorder {
|
|
|
682
701
|
console.error("Error in saving screenshot: ", error);
|
|
683
702
|
}
|
|
684
703
|
}
|
|
704
|
+
async generateLocators(event) {
|
|
705
|
+
const snapshotDetails = event.snapshotDetails;
|
|
706
|
+
if (!snapshotDetails) {
|
|
707
|
+
throw new Error("No snapshot details found");
|
|
708
|
+
}
|
|
709
|
+
const mode = event.mode;
|
|
710
|
+
const inputID = event.element.inputID;
|
|
711
|
+
|
|
712
|
+
const { id, contextId, doc } = snapshotDetails;
|
|
713
|
+
// const selector = `[data-blinq-id="${id}"]`;
|
|
714
|
+
const newPage = await this.backgroundContext.newPage();
|
|
715
|
+
await newPage.setContent(doc);
|
|
716
|
+
const locatorsObj = await newPage.evaluate(
|
|
717
|
+
([id, contextId, mode]) => {
|
|
718
|
+
const recorder = window.__bvt_Recorder;
|
|
719
|
+
const contextElement = document.querySelector(`[data-blinq-context-id="${contextId}"]`);
|
|
720
|
+
const el = document.querySelector(`[data-blinq-id="${id}"]`);
|
|
721
|
+
if (contextElement) {
|
|
722
|
+
const result = recorder.locatorGenerator.toContextLocators(el, contextElement);
|
|
723
|
+
return result;
|
|
724
|
+
}
|
|
725
|
+
const isRecordingText = mode === "recordingText";
|
|
726
|
+
return recorder.locatorGenerator.getElementLocators(el, {
|
|
727
|
+
excludeText: isRecordingText,
|
|
728
|
+
});
|
|
729
|
+
},
|
|
730
|
+
[id, contextId, mode]
|
|
731
|
+
);
|
|
732
|
+
|
|
733
|
+
console.log(`Generated locators: for ${inputID}: `, JSON.stringify(locatorsObj));
|
|
734
|
+
await newPage.close();
|
|
735
|
+
if (event.nestFrmLoc?.children) {
|
|
736
|
+
locatorsObj.nestFrmLoc = event.nestFrmLoc.children;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
this.sendEvent(this.events.updateCommand, {
|
|
740
|
+
locators: {
|
|
741
|
+
locators: locatorsObj.locators,
|
|
742
|
+
nestFrmLoc: locatorsObj.nestFrmLoc,
|
|
743
|
+
iframe_src: !event.frame.isTop ? event.frame.url : undefined,
|
|
744
|
+
},
|
|
745
|
+
allStrategyLocators: locatorsObj.allStrategyLocators,
|
|
746
|
+
inputID,
|
|
747
|
+
});
|
|
748
|
+
// const
|
|
749
|
+
}
|
|
685
750
|
async onAction(event) {
|
|
686
751
|
this._updateUrlPath();
|
|
687
752
|
// const locators = this.overlayLocators(event);
|
|
@@ -695,26 +760,26 @@ export class BVTRecorder {
|
|
|
695
760
|
event.mode === "recordingHover",
|
|
696
761
|
event.mode === "multiInspecting"
|
|
697
762
|
),
|
|
698
|
-
locators: {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
},
|
|
702
|
-
allStrategyLocators: event.allStrategyLocators,
|
|
763
|
+
// locators: {
|
|
764
|
+
// locators: event.locators,
|
|
765
|
+
// iframe_src: !event.frame.isTop ? event.frame.url : undefined,
|
|
766
|
+
// },
|
|
767
|
+
// allStrategyLocators: event.allStrategyLocators,
|
|
703
768
|
url: event.frame.url,
|
|
704
769
|
title: event.frame.title,
|
|
705
770
|
extract: {},
|
|
706
771
|
lastKnownUrlPath: this.lastKnownUrlPath,
|
|
707
772
|
};
|
|
708
|
-
if (event.nestFrmLoc?.children) {
|
|
709
|
-
|
|
710
|
-
}
|
|
773
|
+
// if (event.nestFrmLoc?.children) {
|
|
774
|
+
// cmdEvent.locators.nestFrmLoc = event.nestFrmLoc.children;
|
|
775
|
+
// }
|
|
711
776
|
// this.logger.info({ event });
|
|
712
777
|
if (this.shouldTakeScreenshot) {
|
|
713
778
|
await this.storeScreenshot(event);
|
|
714
779
|
}
|
|
715
|
-
|
|
716
780
|
this.sendEvent(this.events.onNewCommand, cmdEvent);
|
|
717
781
|
this._updateUrlPath();
|
|
782
|
+
await this.generateLocators(event);
|
|
718
783
|
}
|
|
719
784
|
_updateUrlPath() {
|
|
720
785
|
try {
|
|
@@ -788,7 +853,6 @@ export class BVTRecorder {
|
|
|
788
853
|
}
|
|
789
854
|
|
|
790
855
|
async startRecordingInput() {
|
|
791
|
-
console.log("startRecordingInput");
|
|
792
856
|
await this.setMode("recordingInput");
|
|
793
857
|
}
|
|
794
858
|
async stopRecordingInput() {
|
|
@@ -812,9 +876,17 @@ export class BVTRecorder {
|
|
|
812
876
|
}
|
|
813
877
|
|
|
814
878
|
async abortExecution() {
|
|
815
|
-
this.bvtContext.web.abortedExecution = true;
|
|
816
879
|
await this.stepRunner.abortExecution();
|
|
817
880
|
}
|
|
881
|
+
|
|
882
|
+
async pauseExecution({ cmdId }) {
|
|
883
|
+
await this.stepRunner.pauseExecution(cmdId);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
async resumeExecution({ cmdId }) {
|
|
887
|
+
await this.stepRunner.resumeExecution(cmdId);
|
|
888
|
+
}
|
|
889
|
+
|
|
818
890
|
async dealyedRevertMode() {
|
|
819
891
|
const timerId = setTimeout(async () => {
|
|
820
892
|
await this.revertMode();
|
|
@@ -828,13 +900,15 @@ export class BVTRecorder {
|
|
|
828
900
|
TEMP_RUN: true,
|
|
829
901
|
REPORT_FOLDER: this.bvtContext.reportFolder,
|
|
830
902
|
BLINQ_ENV: this.envName,
|
|
831
|
-
STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
|
|
832
|
-
CURRENT_STEP_ID: step.id,
|
|
833
903
|
};
|
|
834
904
|
|
|
835
905
|
this.bvtContext.navigate = true;
|
|
836
906
|
this.bvtContext.loadedRoutes = null;
|
|
837
|
-
|
|
907
|
+
if (listenNetwork) {
|
|
908
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = true;
|
|
909
|
+
} else {
|
|
910
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
|
|
911
|
+
}
|
|
838
912
|
for (const [key, value] of Object.entries(_env)) {
|
|
839
913
|
process.env[key] = value;
|
|
840
914
|
}
|
|
@@ -852,6 +926,7 @@ export class BVTRecorder {
|
|
|
852
926
|
parametersMap,
|
|
853
927
|
envPath: this.envName,
|
|
854
928
|
tags,
|
|
929
|
+
config: this.config,
|
|
855
930
|
},
|
|
856
931
|
this.bvtContext,
|
|
857
932
|
{
|
|
@@ -906,6 +981,7 @@ export class BVTRecorder {
|
|
|
906
981
|
// map: this.scenariosStepsMap,
|
|
907
982
|
// });
|
|
908
983
|
}
|
|
984
|
+
|
|
909
985
|
async generateStepName({ commands, stepsNames, parameters, map }) {
|
|
910
986
|
return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
|
|
911
987
|
}
|
|
@@ -957,10 +1033,11 @@ export class BVTRecorder {
|
|
|
957
1033
|
if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
|
|
958
1034
|
try {
|
|
959
1035
|
const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
960
|
-
this.logger.info("Test data", testData);
|
|
1036
|
+
// this.logger.info("Test data", testData);
|
|
961
1037
|
this.sendEvent(this.events.getTestData, testData);
|
|
962
1038
|
} catch (e) {
|
|
963
|
-
this.logger.error("Error reading test data file", e);
|
|
1039
|
+
// this.logger.error("Error reading test data file", e);
|
|
1040
|
+
console.log("Error reading test data file", e);
|
|
964
1041
|
}
|
|
965
1042
|
}
|
|
966
1043
|
|
|
@@ -969,10 +1046,12 @@ export class BVTRecorder {
|
|
|
969
1046
|
this.watcher.on("all", async (event, path) => {
|
|
970
1047
|
try {
|
|
971
1048
|
const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
972
|
-
this.logger.info("Test data", testData);
|
|
1049
|
+
// this.logger.info("Test data", testData);
|
|
1050
|
+
console.log("Test data changed", testData);
|
|
973
1051
|
this.sendEvent(this.events.getTestData, testData);
|
|
974
1052
|
} catch (e) {
|
|
975
|
-
this.logger.error("Error reading test data file", e);
|
|
1053
|
+
// this.logger.error("Error reading test data file", e);
|
|
1054
|
+
console.log("Error reading test data file", e);
|
|
976
1055
|
}
|
|
977
1056
|
});
|
|
978
1057
|
}
|
|
@@ -1005,7 +1084,7 @@ export class BVTRecorder {
|
|
|
1005
1084
|
.filter((file) => file.endsWith(".feature"))
|
|
1006
1085
|
.map((file) => path.join(this.projectDir, "features", file));
|
|
1007
1086
|
try {
|
|
1008
|
-
const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
|
|
1087
|
+
const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
|
|
1009
1088
|
const output = {};
|
|
1010
1089
|
parsedFiles.forEach((file) => {
|
|
1011
1090
|
if (!file.feature) return;
|
|
@@ -1033,7 +1112,7 @@ export class BVTRecorder {
|
|
|
1033
1112
|
loadExistingScenario({ featureName, scenarioName }) {
|
|
1034
1113
|
const step_definitions = loadStepDefinitions(this.projectDir);
|
|
1035
1114
|
const featureFilePath = path.join(this.projectDir, "features", featureName);
|
|
1036
|
-
const gherkinDoc = parseFeatureFile(featureFilePath);
|
|
1115
|
+
const gherkinDoc = this.parseFeatureFile(featureFilePath);
|
|
1037
1116
|
const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
|
|
1038
1117
|
|
|
1039
1118
|
const steps = [];
|
|
@@ -1189,23 +1268,57 @@ export class BVTRecorder {
|
|
|
1189
1268
|
}
|
|
1190
1269
|
async resetExecution({ tags = [] }) {
|
|
1191
1270
|
// run after hooks followed by before hooks
|
|
1192
|
-
await this.
|
|
1193
|
-
await this.
|
|
1271
|
+
await this.cleanupExecution({ tags });
|
|
1272
|
+
await this.initExecution({ tags });
|
|
1194
1273
|
}
|
|
1195
|
-
}
|
|
1196
1274
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1275
|
+
parseFeatureFile(featureFilePath) {
|
|
1276
|
+
try {
|
|
1277
|
+
let id = 0;
|
|
1278
|
+
const uuidFn = () => (++id).toString(16);
|
|
1279
|
+
const builder = new AstBuilder(uuidFn);
|
|
1280
|
+
const matcher = new GherkinClassicTokenMatcher();
|
|
1281
|
+
const parser = new Parser(builder, matcher);
|
|
1282
|
+
const source = readFileSync(featureFilePath, "utf8");
|
|
1283
|
+
const gherkinDocument = parser.parse(source);
|
|
1284
|
+
return gherkinDocument;
|
|
1285
|
+
} catch (e) {
|
|
1286
|
+
this.logger.error(`Error parsing feature file: ${featureFilePath}`);
|
|
1287
|
+
console.log(e);
|
|
1288
|
+
}
|
|
1289
|
+
return {};
|
|
1209
1290
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1291
|
+
|
|
1292
|
+
stopRecordingNetwork(input) {
|
|
1293
|
+
if (this.bvtContext) {
|
|
1294
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
async fakeParams(params) {
|
|
1299
|
+
const newFakeParams = {};
|
|
1300
|
+
Object.keys(params).forEach((key) => {
|
|
1301
|
+
if (!params[key].startsWith("{{") || !params[key].endsWith("}}")) {
|
|
1302
|
+
newFakeParams[key] = params[key];
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
try {
|
|
1307
|
+
const value = params[key].substring(2, params[key].length - 2).trim();
|
|
1308
|
+
const faking = value.split("(")[0].split(".");
|
|
1309
|
+
let argument = value.substring(value.indexOf("(") + 1, value.lastIndexOf(")"));
|
|
1310
|
+
argument = isNaN(Number(argument)) || argument === "" ? argument : Number(argument);
|
|
1311
|
+
let fakeFunc = faker;
|
|
1312
|
+
faking.forEach((f) => {
|
|
1313
|
+
fakeFunc = fakeFunc[f];
|
|
1314
|
+
});
|
|
1315
|
+
const newValue = fakeFunc(argument);
|
|
1316
|
+
newFakeParams[key] = newValue;
|
|
1317
|
+
} catch (error) {
|
|
1318
|
+
newFakeParams[key] = params[key];
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
return newFakeParams;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
@@ -13,7 +13,6 @@ const uuidFn = () => (++id).toString(16);
|
|
|
13
13
|
const builder = new AstBuilder(uuidFn);
|
|
14
14
|
const matcher = new GherkinClassicTokenMatcher();
|
|
15
15
|
const parser = new Parser(builder, matcher);
|
|
16
|
-
|
|
17
16
|
let i = 0;
|
|
18
17
|
const getImplId = () => {
|
|
19
18
|
return `I-${i++}`;
|
|
@@ -61,6 +60,11 @@ export function parseRouteFiles(projectDir, step) {
|
|
|
61
60
|
const routeFolder = path.join(projectDir, "data", "routes");
|
|
62
61
|
const templateRouteMap = new Map();
|
|
63
62
|
|
|
63
|
+
if (!existsSync(routeFolder)) {
|
|
64
|
+
step.routeItems = null;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
64
68
|
// Go over all the files in the route folder and parse them
|
|
65
69
|
const routeFiles = readdirSync(routeFolder).filter((file) => file.endsWith(".json"));
|
|
66
70
|
for (const file of routeFiles) {
|
|
@@ -87,8 +91,8 @@ export function parseRouteFiles(projectDir, step) {
|
|
|
87
91
|
const filters = item.filters || {};
|
|
88
92
|
const queryParams = filters?.queryParams || {};
|
|
89
93
|
const queryParamsArray = Object.keys(queryParams).map((key) => ({
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
key: key,
|
|
95
|
+
value: queryParams[key],
|
|
92
96
|
}));
|
|
93
97
|
filters.queryParams = queryParamsArray || [];
|
|
94
98
|
});
|
|
@@ -112,6 +116,12 @@ export const getImplementedSteps = async (projectDir) => {
|
|
|
112
116
|
const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
|
|
113
117
|
//console.log({ utilsContent });
|
|
114
118
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
119
|
+
const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
|
|
120
|
+
if (existsSync(hooksTemplateFilePath)) {
|
|
121
|
+
const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
|
|
122
|
+
const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
|
|
123
|
+
writeFileSync(hooksFilePath, hooksContent, "utf8");
|
|
124
|
+
}
|
|
115
125
|
} catch (error) {
|
|
116
126
|
foundErrors.push({ error });
|
|
117
127
|
}
|
|
@@ -284,17 +294,17 @@ export const getImplementedSteps = async (projectDir) => {
|
|
|
284
294
|
for (const tag of scenario.tags) {
|
|
285
295
|
delete tag.location;
|
|
286
296
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
297
|
+
for (const scenario of scenarios) {
|
|
298
|
+
for (const step of scenario.steps) {
|
|
299
|
+
if (step.templateIndex === undefined) {
|
|
300
|
+
const cleanStepName = stepsDefinitions._stepNameToTemplate(step.text);
|
|
301
|
+
const index = implementedSteps.findIndex((istep) => {
|
|
302
|
+
return cleanStepName === istep.pattern;
|
|
303
|
+
});
|
|
304
|
+
step.templateIndex = index;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
298
308
|
}
|
|
299
309
|
if (foundErrors.length > 0) {
|
|
300
310
|
console.log("foundErrors", foundErrors);
|