@dev-blinq/cucumber_client 1.0.1220-dev → 1.0.1220-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 +220 -0
- package/bin/assets/preload/recorderv3.js +70 -3
- package/bin/assets/preload/unique_locators.js +24 -3
- package/bin/assets/scripts/aria_snapshot.js +235 -0
- package/bin/assets/scripts/dom_attr.js +372 -0
- package/bin/assets/scripts/dom_element.js +0 -0
- package/bin/assets/scripts/dom_parent.js +185 -0
- package/bin/assets/scripts/event_utils.js +105 -0
- package/bin/assets/scripts/pw.js +7886 -0
- package/bin/assets/scripts/recorder.js +1147 -0
- package/bin/assets/scripts/snapshot_capturer.js +155 -0
- package/bin/assets/scripts/unique_locators.js +841 -0
- package/bin/assets/scripts/yaml.js +4770 -0
- package/bin/assets/templates/_hooks_template.txt +37 -0
- package/bin/assets/templates/page_template.txt +2 -16
- package/bin/assets/templates/utils_template.txt +48 -63
- package/bin/client/apiTest/apiTest.js +6 -0
- package/bin/client/cli_helpers.js +11 -13
- package/bin/client/code_cleanup/utils.js +42 -14
- package/bin/client/code_gen/code_inversion.js +92 -11
- package/bin/client/code_gen/index.js +3 -0
- package/bin/client/code_gen/page_reflection.js +37 -20
- package/bin/client/code_gen/playwright_codeget.js +170 -33
- package/bin/client/cucumber/feature.js +85 -27
- package/bin/client/cucumber/steps_definitions.js +84 -76
- package/bin/client/local_agent.js +7 -3
- package/bin/client/project.js +7 -1
- package/bin/client/recorderv3/bvt_recorder.js +285 -81
- package/bin/client/recorderv3/implemented_steps.js +75 -14
- package/bin/client/recorderv3/index.js +58 -8
- package/bin/client/recorderv3/network.js +299 -0
- package/bin/client/recorderv3/step_runner.js +319 -67
- package/bin/client/recorderv3/step_utils.js +157 -6
- package/bin/client/recorderv3/update_feature.js +58 -30
- package/bin/client/recording.js +7 -0
- package/bin/client/run_cucumber.js +15 -2
- package/bin/client/scenario_report.js +4 -8
- package/bin/client/test_scenario.js +0 -1
- package/bin/index.js +1 -0
- package/package.json +15 -8
package/bin/client/project.js
CHANGED
|
@@ -82,6 +82,12 @@ class Project {
|
|
|
82
82
|
const utilsContent = readFileSync(utilsPath, "utf8");
|
|
83
83
|
const utilsFileName = this.stepsFolder + "/utils.mjs";
|
|
84
84
|
writeFileSync(utilsFileName, utilsContent, "utf8");
|
|
85
|
+
const hooksTemplateFilePath = path.join(__dirname, "../assets", "templates", "_hooks_template.txt");
|
|
86
|
+
if (existsSync(hooksTemplateFilePath)) {
|
|
87
|
+
const hooksFilePath = path.join(this.stepsFolder, "_hooks.mjs");
|
|
88
|
+
const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
|
|
89
|
+
writeFileSync(hooksFilePath, hooksContent, "utf8");
|
|
90
|
+
}
|
|
85
91
|
} catch (e) {
|
|
86
92
|
logger.error("Error loading utils_template");
|
|
87
93
|
}
|
|
@@ -104,7 +110,7 @@ class Project {
|
|
|
104
110
|
this.pages.push(page);
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
|
-
return
|
|
113
|
+
return this.environment.name;
|
|
108
114
|
}
|
|
109
115
|
getPagesNames() {
|
|
110
116
|
let result = [];
|
|
@@ -3,8 +3,7 @@ import { closeContext, initContext, _getDataFile, resetTestData } from "automati
|
|
|
3
3
|
import { existsSync, readdirSync, readFileSync, rmSync } from "fs";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import url from "url";
|
|
6
|
-
import
|
|
7
|
-
import { getImplementedSteps, getStepsAndCommandsForScenario } from "./implemented_steps.js";
|
|
6
|
+
import { getImplementedSteps, parseRouteFiles } from "./implemented_steps.js";
|
|
8
7
|
import { NamesService } from "./services.js";
|
|
9
8
|
import { BVTStepRunner } from "./step_runner.js";
|
|
10
9
|
import { readFile, writeFile } from "fs/promises";
|
|
@@ -16,41 +15,21 @@ import chokidar from "chokidar";
|
|
|
16
15
|
import logger from "../../logger.js";
|
|
17
16
|
import { unEscapeNonPrintables } from "../cucumber/utils.js";
|
|
18
17
|
import { findAvailablePort } from "../utils/index.js";
|
|
18
|
+
import { Step } from "../cucumber/feature.js";
|
|
19
|
+
|
|
19
20
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
20
|
-
|
|
21
|
+
|
|
21
22
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
|
-
export function getInitScript(config) {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
window.__bvt_Recorder.setPopupHandlers(${JSON.stringify(popupHandlers)});
|
|
31
|
-
window.__bvt_Recorder.setDisableHighlight(${JSON.stringify(disableHighlight)});
|
|
32
|
-
window.__bvt_Recorder.setImproviseLocators(${JSON.stringify(!disableMultipleLocators)});`;
|
|
33
|
-
return (
|
|
34
|
-
[
|
|
35
|
-
path.join(__dirname, "..", "..", "assets", "preload", "accessibility.js"),
|
|
36
|
-
path.join(__dirname, "..", "..", "assets", "preload", "dom-utils.js"),
|
|
37
|
-
path.join(__dirname, "..", "..", "assets", "preload", "generateSelector.js"),
|
|
38
|
-
path.join(__dirname, "..", "..", "assets", "preload", "locators.js"),
|
|
39
|
-
path.join(__dirname, "..", "..", "assets", "preload", "unique_locators.js"),
|
|
40
|
-
// path.join(__dirname, "..", "..", "assets", "preload", "pw_locators.js"),
|
|
41
|
-
path.join(__dirname, "..", "..", "assets", "preload", "climb.js"),
|
|
42
|
-
path.join(__dirname, "..", "..", "assets", "preload", "text-locator.js"),
|
|
43
|
-
path.join(__dirname, "..", "..", "assets", "preload", "toolbar.js"),
|
|
44
|
-
path.join(__dirname, "..", "..", "assets", "preload", "recording-tool.js"),
|
|
45
|
-
path.join(__dirname, "..", "..", "assets", "preload", "find_context.js"),
|
|
46
|
-
path.join(__dirname, "..", "..", "assets", "preload", "recorderv3.js"),
|
|
47
|
-
path.join(__dirname, "..", "..", "assets", "preload", "findElementText.js"),
|
|
48
|
-
path.join(__dirname, "..", "..", "assets", "preload", "css_gen.js"),
|
|
49
|
-
path.join(__dirname, "..", "..", "assets", "preload", "yaml.js"),
|
|
50
|
-
]
|
|
51
|
-
.map((filePath) => readFileSync(filePath, "utf8"))
|
|
52
|
-
.join("\n") + popupScript
|
|
23
|
+
export function getInitScript(config, options) {
|
|
24
|
+
const preScript = `
|
|
25
|
+
window.__bvt_Recorder_config = ${JSON.stringify(config ?? null)};
|
|
26
|
+
window.__PW_options = ${JSON.stringify(options ?? null)};
|
|
27
|
+
`;
|
|
28
|
+
const recorderScript = readFileSync(
|
|
29
|
+
path.join(__dirname, "..", "..", "assets", "bundled_scripts", "recorder.js"),
|
|
30
|
+
"utf8"
|
|
53
31
|
);
|
|
32
|
+
return preScript + recorderScript;
|
|
54
33
|
}
|
|
55
34
|
|
|
56
35
|
async function evaluate(frame, script) {
|
|
@@ -71,7 +50,7 @@ async function findNestedFrameSelector(frame, obj) {
|
|
|
71
50
|
const frameElement = await frame.frameElement();
|
|
72
51
|
if (!frameElement) return;
|
|
73
52
|
const selectors = await parent.evaluate((element) => {
|
|
74
|
-
return window.
|
|
53
|
+
return window.__bvt_Recorder.locatorGenerator.getElementLocators(element, { excludeText: true }).locators;
|
|
75
54
|
}, frameElement);
|
|
76
55
|
return findNestedFrameSelector(parent, { children: obj, selectors });
|
|
77
56
|
} catch (e) {
|
|
@@ -198,17 +177,15 @@ export class BVTRecorder {
|
|
|
198
177
|
this.logger = logger;
|
|
199
178
|
this.screenshotMap = new Map();
|
|
200
179
|
this.snapshotMap = new Map();
|
|
180
|
+
this.scenariosStepsMap = new Map();
|
|
201
181
|
this.namesService = new NamesService({
|
|
202
182
|
screenshotMap: this.screenshotMap,
|
|
203
183
|
TOKEN: this.TOKEN,
|
|
204
184
|
projectDir: this.projectDir,
|
|
205
185
|
logger: this.logger,
|
|
206
186
|
});
|
|
207
|
-
this.stepRunner = new BVTStepRunner({
|
|
208
|
-
projectDir: this.projectDir,
|
|
209
|
-
});
|
|
210
187
|
this.pageSet = new Set();
|
|
211
|
-
|
|
188
|
+
this.pageMetaDataSet = new Set();
|
|
212
189
|
this.lastKnownUrlPath = "";
|
|
213
190
|
// TODO: what is world?
|
|
214
191
|
this.world = { attach: () => {} };
|
|
@@ -224,6 +201,10 @@ export class BVTRecorder {
|
|
|
224
201
|
onStepDetails: "BVTRecorder.onStepDetails",
|
|
225
202
|
getTestData: "BVTRecorder.getTestData",
|
|
226
203
|
onGoto: "BVTRecorder.onGoto",
|
|
204
|
+
cmdExecutionStart: "BVTRecorder.cmdExecutionStart",
|
|
205
|
+
cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
|
|
206
|
+
cmdExecutionError: "BVTRecorder.cmdExecutionError",
|
|
207
|
+
interceptResults: "BVTRecorder.interceptResults",
|
|
227
208
|
};
|
|
228
209
|
bindings = {
|
|
229
210
|
__bvt_recordCommand: async ({ frame, page, context }, event) => {
|
|
@@ -299,29 +280,22 @@ export class BVTRecorder {
|
|
|
299
280
|
return result;
|
|
300
281
|
}
|
|
301
282
|
getInitScripts(config) {
|
|
302
|
-
return getInitScript(config
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
// const inlineScript = `
|
|
312
|
-
// window.__bvt_Recorder.setPopupHandlers(${JSON.stringify(popupHandlers)});
|
|
313
|
-
// window.__bvt_Recorder.setDisableHighlight(${JSON.stringify(disableHighlight)});
|
|
314
|
-
// window.__bvt_Recorder.setImproviseLocators(${JSON.stringify(!disableMultipleLocators)});
|
|
315
|
-
// `
|
|
316
|
-
// scripts.push(inlineScript);
|
|
317
|
-
// return scripts;
|
|
283
|
+
return getInitScript(config, {
|
|
284
|
+
sdkLanguage: "javascript",
|
|
285
|
+
testIdAttributeName: "blinq-test-id",
|
|
286
|
+
stableRafCount: 0,
|
|
287
|
+
browserName: this.browser?.browserType().name(),
|
|
288
|
+
inputFileRoleTextbox: false,
|
|
289
|
+
customEngines: [],
|
|
290
|
+
isUnderTest: true,
|
|
291
|
+
});
|
|
318
292
|
}
|
|
319
293
|
|
|
320
294
|
async _initBrowser({ url }) {
|
|
321
295
|
this.#remoteDebuggerPort = await findAvailablePort();
|
|
322
296
|
process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
|
|
323
297
|
|
|
324
|
-
this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
298
|
+
// this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
325
299
|
this.world = { attach: () => {} };
|
|
326
300
|
|
|
327
301
|
const ai_config_file = path.join(this.projectDir, "ai_config.json");
|
|
@@ -333,8 +307,9 @@ export class BVTRecorder {
|
|
|
333
307
|
console.error("Error reading ai_config.json", error);
|
|
334
308
|
}
|
|
335
309
|
}
|
|
310
|
+
this.config = ai_config;
|
|
336
311
|
const initScripts = {
|
|
337
|
-
recorderCjs: injectedScriptSource,
|
|
312
|
+
// recorderCjs: injectedScriptSource,
|
|
338
313
|
scripts: [
|
|
339
314
|
this.getInitScripts(ai_config),
|
|
340
315
|
`\ndelete Object.getPrototypeOf(navigator).webdriver;${process.env.WINDOW_DEBUGGER ? "window.debug=true;\n" : ""}`,
|
|
@@ -346,11 +321,40 @@ export class BVTRecorder {
|
|
|
346
321
|
let stopTime = Date.now();
|
|
347
322
|
this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
|
|
348
323
|
this.bvtContext = bvtContext;
|
|
324
|
+
this.stepRunner = new BVTStepRunner({
|
|
325
|
+
projectDir: this.projectDir,
|
|
326
|
+
sendExecutionStatus: (data) => {
|
|
327
|
+
if (data && data.type) {
|
|
328
|
+
switch (data.type) {
|
|
329
|
+
case "cmdExecutionStart":
|
|
330
|
+
console.log("Sending cmdExecutionStart event for cmdId:", data);
|
|
331
|
+
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
332
|
+
break;
|
|
333
|
+
case "cmdExecutionSuccess":
|
|
334
|
+
console.log("Sending cmdExecutionSuccess event for cmdId:", data);
|
|
335
|
+
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
336
|
+
break;
|
|
337
|
+
case "cmdExecutionError":
|
|
338
|
+
console.log("Sending cmdExecutionError event for cmdId:", data);
|
|
339
|
+
this.sendEvent(this.events.cmdExecutionError, data);
|
|
340
|
+
break;
|
|
341
|
+
case "interceptResults":
|
|
342
|
+
console.log("Sending interceptResults event");
|
|
343
|
+
this.sendEvent(this.events.interceptResults, data);
|
|
344
|
+
break;
|
|
345
|
+
default:
|
|
346
|
+
console.warn("Unknown command execution status type:", data.type);
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
bvtContext: this.bvtContext,
|
|
352
|
+
});
|
|
349
353
|
const context = bvtContext.playContext;
|
|
350
354
|
this.context = context;
|
|
351
355
|
this.web = bvtContext.stable || bvtContext.web;
|
|
356
|
+
this.web.tryAllStrategies = true;
|
|
352
357
|
this.page = bvtContext.page;
|
|
353
|
-
|
|
354
358
|
this.pageSet.add(this.page);
|
|
355
359
|
this.lastKnownUrlPath = this._updateUrlPath();
|
|
356
360
|
const browser = await this.context.browser();
|
|
@@ -461,6 +465,75 @@ export class BVTRecorder {
|
|
|
461
465
|
}
|
|
462
466
|
});
|
|
463
467
|
}
|
|
468
|
+
|
|
469
|
+
hasHistoryReplacementAtIndex(previousEntries, currentEntries, index) {
|
|
470
|
+
if (!previousEntries || !currentEntries) return false;
|
|
471
|
+
if (index >= previousEntries.length || index >= currentEntries.length) return false;
|
|
472
|
+
|
|
473
|
+
const prevEntry = previousEntries[index];
|
|
474
|
+
// console.log("prevEntry", prevEntry);
|
|
475
|
+
const currEntry = currentEntries[index];
|
|
476
|
+
// console.log("currEntry", currEntry);
|
|
477
|
+
|
|
478
|
+
// Check if the entry at this index has been replaced
|
|
479
|
+
return prevEntry.id !== currEntry.id;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Even simpler approach for your specific case
|
|
483
|
+
analyzeTransitionType(entries, currentIndex, currentEntry) {
|
|
484
|
+
// console.log("Analyzing transition type");
|
|
485
|
+
// console.log("===========================");
|
|
486
|
+
// console.log("Current Index:", currentIndex);
|
|
487
|
+
// console.log("Current Entry:", currentEntry);
|
|
488
|
+
// console.log("Current Entries:", entries);
|
|
489
|
+
// console.log("Current entries length:", entries.length);
|
|
490
|
+
// console.log("===========================");
|
|
491
|
+
// console.log("Previous Index:", this.previousIndex);
|
|
492
|
+
// // console.log("Previous Entry:", this.previousEntries[this.previousIndex]);
|
|
493
|
+
// console.log("Previous Entries:", this.previousEntries);
|
|
494
|
+
// console.log("Previous entries length:", this.previousHistoryLength);
|
|
495
|
+
|
|
496
|
+
if (this.previousIndex === null || this.previousHistoryLength === null || !this.previousEntries) {
|
|
497
|
+
return {
|
|
498
|
+
action: "initial",
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const indexDiff = currentIndex - this.previousIndex;
|
|
503
|
+
const lengthDiff = entries.length - this.previousHistoryLength;
|
|
504
|
+
|
|
505
|
+
// Backward navigation
|
|
506
|
+
if (indexDiff < 0) {
|
|
507
|
+
return { action: "back" };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Forward navigation
|
|
511
|
+
if (indexDiff > 0 && lengthDiff === 0) {
|
|
512
|
+
// Check if the entry at current index is the same as before
|
|
513
|
+
const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
|
|
514
|
+
|
|
515
|
+
if (entryReplaced) {
|
|
516
|
+
return { action: "navigate" }; // New navigation that replaced forward history
|
|
517
|
+
} else {
|
|
518
|
+
return { action: "forward" }; // True forward navigation
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// New navigation (history grew)
|
|
523
|
+
if (lengthDiff > 0) {
|
|
524
|
+
return { action: "navigate" };
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Same position, same length
|
|
528
|
+
if (lengthDiff <= 0) {
|
|
529
|
+
const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
|
|
530
|
+
|
|
531
|
+
return entryReplaced ? { action: "navigate" } : { action: "reload" };
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return { action: "unknown" };
|
|
535
|
+
}
|
|
536
|
+
|
|
464
537
|
async getCurrentTransition() {
|
|
465
538
|
if (this?.web?.browser?._name !== "chromium") {
|
|
466
539
|
return;
|
|
@@ -469,29 +542,70 @@ export class BVTRecorder {
|
|
|
469
542
|
|
|
470
543
|
try {
|
|
471
544
|
const result = await client.send("Page.getNavigationHistory");
|
|
472
|
-
// console.log("
|
|
545
|
+
// console.log("Navigation History:", result);
|
|
473
546
|
const entries = result.entries;
|
|
474
547
|
const currentIndex = result.currentIndex;
|
|
475
548
|
|
|
476
549
|
// ignore if currentIndex is not the last entry
|
|
477
|
-
if (currentIndex !== entries.length - 1) return;
|
|
550
|
+
// if (currentIndex !== entries.length - 1) return;
|
|
478
551
|
|
|
479
552
|
const currentEntry = entries[currentIndex];
|
|
480
|
-
|
|
553
|
+
const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
|
|
554
|
+
this.previousIndex = currentIndex;
|
|
555
|
+
this.previousHistoryLength = entries.length;
|
|
556
|
+
this.previousUrl = currentEntry.url;
|
|
557
|
+
this.previousEntries = [...entries]; // Store a copy of current entries
|
|
558
|
+
|
|
559
|
+
return {
|
|
560
|
+
currentEntry,
|
|
561
|
+
navigationAction: transitionInfo.action,
|
|
562
|
+
};
|
|
481
563
|
} catch (error) {
|
|
482
|
-
console.error("Error in getTransistionType event");
|
|
564
|
+
console.error("Error in getTransistionType event", error);
|
|
483
565
|
} finally {
|
|
484
566
|
await client.detach();
|
|
485
567
|
}
|
|
486
568
|
}
|
|
487
|
-
|
|
569
|
+
userInitiatedTransitionTypes = ["typed", "address_bar"];
|
|
488
570
|
async handlePageTransition() {
|
|
489
571
|
const transition = await this.getCurrentTransition();
|
|
490
572
|
if (!transition) return;
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
573
|
+
|
|
574
|
+
const { currentEntry, navigationAction } = transition;
|
|
575
|
+
|
|
576
|
+
switch (navigationAction) {
|
|
577
|
+
case "initial":
|
|
578
|
+
// console.log("Initial navigation, no action taken");
|
|
579
|
+
return;
|
|
580
|
+
case "navigate":
|
|
581
|
+
// console.log("transitionType", transition.transitionType);
|
|
582
|
+
// console.log("sending onGoto event", { url: currentEntry.url,
|
|
583
|
+
// type: "navigate", });
|
|
584
|
+
if (this.userInitiatedTransitionTypes.includes(currentEntry.transitionType)) {
|
|
585
|
+
const env = JSON.parse(readFileSync(this.envName), "utf8");
|
|
586
|
+
const baseUrl = env.baseUrl;
|
|
587
|
+
let url = currentEntry.userTypedURL;
|
|
588
|
+
if (baseUrl && url.startsWith(baseUrl)) {
|
|
589
|
+
url = url.replace(baseUrl, "{{env.baseUrl}}");
|
|
590
|
+
}
|
|
591
|
+
// console.log("User initiated transition");
|
|
592
|
+
this.sendEvent(this.events.onGoto, { url, type: "navigate" });
|
|
593
|
+
}
|
|
594
|
+
return;
|
|
595
|
+
case "back":
|
|
596
|
+
// console.log("User navigated back");
|
|
597
|
+
// console.log("sending onGoto event", {
|
|
598
|
+
// type: "back",
|
|
599
|
+
// });
|
|
600
|
+
this.sendEvent(this.events.onGoto, { type: "back" });
|
|
601
|
+
return;
|
|
602
|
+
case "forward":
|
|
603
|
+
// console.log("User navigated forward"); console.log("sending onGoto event", { type: "forward", });
|
|
604
|
+
this.sendEvent(this.events.onGoto, { type: "forward" });
|
|
605
|
+
return;
|
|
606
|
+
default:
|
|
607
|
+
this.sendEvent(this.events.onGoto, { type: "unknown" });
|
|
608
|
+
return;
|
|
495
609
|
}
|
|
496
610
|
}
|
|
497
611
|
|
|
@@ -620,6 +734,11 @@ export class BVTRecorder {
|
|
|
620
734
|
delete process.env.TEMP_RUN;
|
|
621
735
|
await this.watcher.close().then(() => {});
|
|
622
736
|
this.watcher = null;
|
|
737
|
+
this.previousIndex = null;
|
|
738
|
+
this.previousHistoryLength = null;
|
|
739
|
+
this.previousUrl = null;
|
|
740
|
+
this.previousEntries = null;
|
|
741
|
+
|
|
623
742
|
await closeContext();
|
|
624
743
|
this.pageSet.clear();
|
|
625
744
|
}
|
|
@@ -696,40 +815,61 @@ export class BVTRecorder {
|
|
|
696
815
|
async abortExecution() {
|
|
697
816
|
await this.stepRunner.abortExecution();
|
|
698
817
|
}
|
|
818
|
+
|
|
819
|
+
async pauseExecution({ cmdId }) {
|
|
820
|
+
await this.stepRunner.pauseExecution(cmdId);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
async resumeExecution({ cmdId }) {
|
|
824
|
+
await this.stepRunner.resumeExecution(cmdId);
|
|
825
|
+
}
|
|
826
|
+
|
|
699
827
|
async dealyedRevertMode() {
|
|
700
828
|
const timerId = setTimeout(async () => {
|
|
701
829
|
await this.revertMode();
|
|
702
830
|
}, 100);
|
|
703
831
|
this.timerId = timerId;
|
|
704
832
|
}
|
|
705
|
-
async runStep({ step, parametersMap }, options) {
|
|
833
|
+
async runStep({ step, parametersMap, tags, isFirstStep, listenNetwork }, options) {
|
|
834
|
+
const { skipAfter = true, skipBefore = !isFirstStep } = options || {};
|
|
706
835
|
const _env = {
|
|
707
836
|
TOKEN: this.TOKEN,
|
|
708
837
|
TEMP_RUN: true,
|
|
709
838
|
REPORT_FOLDER: this.bvtContext.reportFolder,
|
|
710
839
|
BLINQ_ENV: this.envName,
|
|
840
|
+
STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
|
|
841
|
+
CURRENT_STEP_ID: step.id,
|
|
711
842
|
};
|
|
712
843
|
|
|
713
844
|
this.bvtContext.navigate = true;
|
|
845
|
+
this.bvtContext.loadedRoutes = null;
|
|
714
846
|
for (const [key, value] of Object.entries(_env)) {
|
|
715
847
|
process.env[key] = value;
|
|
716
848
|
}
|
|
849
|
+
|
|
717
850
|
if (this.timerId) {
|
|
718
851
|
clearTimeout(this.timerId);
|
|
719
852
|
this.timerId = null;
|
|
720
853
|
}
|
|
721
854
|
await this.setMode("running");
|
|
855
|
+
|
|
722
856
|
try {
|
|
723
|
-
await this.stepRunner.runStep(
|
|
857
|
+
const { result, info } = await this.stepRunner.runStep(
|
|
724
858
|
{
|
|
725
859
|
step,
|
|
726
860
|
parametersMap,
|
|
727
861
|
envPath: this.envName,
|
|
862
|
+
tags,
|
|
863
|
+
config: this.config,
|
|
728
864
|
},
|
|
729
865
|
this.bvtContext,
|
|
730
|
-
|
|
866
|
+
{
|
|
867
|
+
skipAfter,
|
|
868
|
+
skipBefore,
|
|
869
|
+
}
|
|
731
870
|
);
|
|
732
871
|
await this.revertMode();
|
|
872
|
+
return { info };
|
|
733
873
|
} catch (error) {
|
|
734
874
|
await this.revertMode();
|
|
735
875
|
throw error;
|
|
@@ -740,22 +880,42 @@ export class BVTRecorder {
|
|
|
740
880
|
this.bvtContext.navigate = false;
|
|
741
881
|
}
|
|
742
882
|
}
|
|
743
|
-
async runScenario({ steps, parametersMap }) {
|
|
744
|
-
for (const step of steps) {
|
|
745
|
-
await this.runStep({ step, parametersMap });
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
883
|
async saveScenario({ scenario, featureName, override, isSingleStep }) {
|
|
749
884
|
await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
|
|
750
885
|
if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
|
|
751
|
-
await this.cleanup();
|
|
886
|
+
await this.cleanup({ tags: scenario.tags });
|
|
752
887
|
}
|
|
753
888
|
async getImplementedSteps() {
|
|
754
|
-
|
|
889
|
+
const stepsAndScenarios = await getImplementedSteps(this.projectDir);
|
|
890
|
+
const implementedSteps = stepsAndScenarios.implementedSteps;
|
|
891
|
+
const scenarios = stepsAndScenarios.scenarios;
|
|
892
|
+
for (const scenario of scenarios) {
|
|
893
|
+
this.scenariosStepsMap.set(scenario.name, scenario.steps);
|
|
894
|
+
delete scenario.steps;
|
|
895
|
+
}
|
|
896
|
+
return {
|
|
897
|
+
implementedSteps,
|
|
898
|
+
scenarios,
|
|
899
|
+
};
|
|
755
900
|
}
|
|
756
901
|
async getStepsAndCommandsForScenario({ name, featureName }) {
|
|
757
|
-
|
|
902
|
+
const steps = this.scenariosStepsMap.get(name) || [];
|
|
903
|
+
for (const step of steps) {
|
|
904
|
+
if (step.isImplemented) {
|
|
905
|
+
step.commands = this.getCommandsForImplementedStep({ stepName: step.text });
|
|
906
|
+
} else {
|
|
907
|
+
step.commands = [];
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return steps;
|
|
911
|
+
// return getStepsAndCommandsForScenario({
|
|
912
|
+
// name,
|
|
913
|
+
// featureName,
|
|
914
|
+
// projectDir: this.projectDir,
|
|
915
|
+
// map: this.scenariosStepsMap,
|
|
916
|
+
// });
|
|
758
917
|
}
|
|
918
|
+
|
|
759
919
|
async generateStepName({ commands, stepsNames, parameters, map }) {
|
|
760
920
|
return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
|
|
761
921
|
}
|
|
@@ -838,9 +998,9 @@ export class BVTRecorder {
|
|
|
838
998
|
}
|
|
839
999
|
}
|
|
840
1000
|
|
|
841
|
-
async discardTestData() {
|
|
1001
|
+
async discardTestData({ tags }) {
|
|
842
1002
|
resetTestData(this.envName, this.world);
|
|
843
|
-
await this.cleanup();
|
|
1003
|
+
await this.cleanup({ tags });
|
|
844
1004
|
}
|
|
845
1005
|
async addToTestData(obj) {
|
|
846
1006
|
if (!existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
|
|
@@ -879,6 +1039,7 @@ export class BVTRecorder {
|
|
|
879
1039
|
const stepParams = parseStepTextParameters(stepName);
|
|
880
1040
|
return getCommandsForImplementedStep(stepName, step_definitions, stepParams).commands;
|
|
881
1041
|
}
|
|
1042
|
+
|
|
882
1043
|
loadExistingScenario({ featureName, scenarioName }) {
|
|
883
1044
|
const step_definitions = loadStepDefinitions(this.projectDir);
|
|
884
1045
|
const featureFilePath = path.join(this.projectDir, "features", featureName);
|
|
@@ -907,6 +1068,7 @@ export class BVTRecorder {
|
|
|
907
1068
|
..._s,
|
|
908
1069
|
keyword: step.keyword.trim(),
|
|
909
1070
|
};
|
|
1071
|
+
parseRouteFiles(this.projectDir, _step);
|
|
910
1072
|
steps.push(_step);
|
|
911
1073
|
}
|
|
912
1074
|
return {
|
|
@@ -946,7 +1108,7 @@ export class BVTRecorder {
|
|
|
946
1108
|
}
|
|
947
1109
|
return result;
|
|
948
1110
|
}
|
|
949
|
-
async cleanup() {
|
|
1111
|
+
async cleanup({ tags }) {
|
|
950
1112
|
const noopStep = {
|
|
951
1113
|
text: "Noop",
|
|
952
1114
|
isImplemented: true,
|
|
@@ -960,6 +1122,7 @@ export class BVTRecorder {
|
|
|
960
1122
|
{
|
|
961
1123
|
step: noopStep,
|
|
962
1124
|
parametersMap: {},
|
|
1125
|
+
tags: tags || [],
|
|
963
1126
|
},
|
|
964
1127
|
{
|
|
965
1128
|
skipAfter: false,
|
|
@@ -998,6 +1161,47 @@ export class BVTRecorder {
|
|
|
998
1161
|
return false;
|
|
999
1162
|
}
|
|
1000
1163
|
}
|
|
1164
|
+
async initExecution({ tags = [] }) {
|
|
1165
|
+
// run before hooks
|
|
1166
|
+
const noopStep = {
|
|
1167
|
+
text: "Noop",
|
|
1168
|
+
isImplemented: true,
|
|
1169
|
+
};
|
|
1170
|
+
await this.runStep(
|
|
1171
|
+
{
|
|
1172
|
+
step: noopStep,
|
|
1173
|
+
parametersMap: {},
|
|
1174
|
+
tags,
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
skipBefore: false,
|
|
1178
|
+
skipAfter: true,
|
|
1179
|
+
}
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
async cleanupExecution({ tags = [] }) {
|
|
1183
|
+
// run after hooks
|
|
1184
|
+
const noopStep = {
|
|
1185
|
+
text: "Noop",
|
|
1186
|
+
isImplemented: true,
|
|
1187
|
+
};
|
|
1188
|
+
await this.runStep(
|
|
1189
|
+
{
|
|
1190
|
+
step: noopStep,
|
|
1191
|
+
parametersMap: {},
|
|
1192
|
+
tags,
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
skipBefore: true,
|
|
1196
|
+
skipAfter: false,
|
|
1197
|
+
}
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
async resetExecution({ tags = [] }) {
|
|
1201
|
+
// run after hooks followed by before hooks
|
|
1202
|
+
await this.cleanupExecution({ tags });
|
|
1203
|
+
await this.initExecution({ tags });
|
|
1204
|
+
}
|
|
1001
1205
|
}
|
|
1002
1206
|
|
|
1003
1207
|
const parseFeatureFile = (featureFilePath) => {
|