@dev-blinq/cucumber_client 1.0.1263-dev → 1.0.1263-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 +159 -14068
- package/bin/assets/preload/recorderv3.js +3 -1
- package/bin/assets/scripts/dom_parent.js +26 -0
- package/bin/assets/scripts/recorder.js +8 -4
- package/bin/assets/scripts/unique_locators.js +847 -547
- 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 +44 -71
- 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/code_inversion.js +61 -4
- package/bin/client/code_gen/page_reflection.js +838 -899
- package/bin/client/code_gen/playwright_codeget.js +52 -13
- package/bin/client/cucumber/feature.js +89 -27
- package/bin/client/cucumber/project_to_document.js +1 -1
- package/bin/client/cucumber/steps_definitions.js +84 -76
- package/bin/client/cucumber_selector.js +17 -1
- package/bin/client/local_agent.js +3 -3
- package/bin/client/project.js +186 -196
- package/bin/client/recorderv3/bvt_recorder.js +290 -80
- package/bin/client/recorderv3/implemented_steps.js +74 -16
- package/bin/client/recorderv3/index.js +47 -25
- package/bin/client/recorderv3/network.js +299 -0
- package/bin/client/recorderv3/services.js +4 -16
- package/bin/client/recorderv3/step_runner.js +331 -67
- package/bin/client/recorderv3/step_utils.js +152 -5
- package/bin/client/recorderv3/update_feature.js +32 -30
- package/bin/client/recording.js +3 -0
- package/bin/client/run_cucumber.js +5 -1
- package/bin/client/scenario_report.js +0 -5
- package/bin/client/test_scenario.js +0 -1
- 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 +24 -14
|
@@ -3,7 +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 { getImplementedSteps,
|
|
6
|
+
import { getImplementedSteps, parseRouteFiles } from "./implemented_steps.js";
|
|
7
7
|
import { NamesService } from "./services.js";
|
|
8
8
|
import { BVTStepRunner } from "./step_runner.js";
|
|
9
9
|
import { readFile, writeFile } from "fs/promises";
|
|
@@ -12,10 +12,9 @@ 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
|
-
|
|
17
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
19
18
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
20
19
|
|
|
21
20
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -24,11 +23,11 @@ export function getInitScript(config, options) {
|
|
|
24
23
|
window.__bvt_Recorder_config = ${JSON.stringify(config ?? null)};
|
|
25
24
|
window.__PW_options = ${JSON.stringify(options ?? null)};
|
|
26
25
|
`;
|
|
27
|
-
const recorderScript = readFileSync(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
recorderScript
|
|
26
|
+
const recorderScript = readFileSync(
|
|
27
|
+
path.join(__dirname, "..", "..", "assets", "bundled_scripts", "recorder.js"),
|
|
28
|
+
"utf8"
|
|
31
29
|
);
|
|
30
|
+
return preScript + recorderScript;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
async function evaluate(frame, script) {
|
|
@@ -44,15 +43,15 @@ async function evaluate(frame, script) {
|
|
|
44
43
|
async function findNestedFrameSelector(frame, obj) {
|
|
45
44
|
try {
|
|
46
45
|
const parent = frame.parentFrame();
|
|
47
|
-
if (parent) console.log(`Parent frame: ${JSON.stringify(parent)}`);
|
|
48
46
|
if (!parent) return { children: obj };
|
|
49
47
|
const frameElement = await frame.frameElement();
|
|
50
48
|
if (!frameElement) return;
|
|
51
49
|
const selectors = await parent.evaluate((element) => {
|
|
52
|
-
return window.
|
|
50
|
+
return window.__bvt_Recorder.locatorGenerator.getElementLocators(element, { excludeText: true }).locators;
|
|
53
51
|
}, frameElement);
|
|
54
52
|
return findNestedFrameSelector(parent, { children: obj, selectors });
|
|
55
53
|
} catch (e) {
|
|
54
|
+
socketLogger.error(`Error in findNestedFrameSelector: ${e}`);
|
|
56
55
|
console.error(e);
|
|
57
56
|
}
|
|
58
57
|
}
|
|
@@ -149,6 +148,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
|
|
|
149
148
|
};
|
|
150
149
|
}
|
|
151
150
|
default: {
|
|
151
|
+
socketLogger.error(`Action not supported: ${action.name}`);
|
|
152
152
|
console.log("action not supported", action);
|
|
153
153
|
throw new Error("action not supported");
|
|
154
154
|
}
|
|
@@ -160,6 +160,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
|
|
|
160
160
|
* @property {string} projectDir
|
|
161
161
|
* @property {string} TOKEN
|
|
162
162
|
* @property {(name:string, data:any)=> void} sendEvent
|
|
163
|
+
* @property {Object} logger
|
|
163
164
|
*/
|
|
164
165
|
export class BVTRecorder {
|
|
165
166
|
#currentURL = "";
|
|
@@ -173,7 +174,6 @@ export class BVTRecorder {
|
|
|
173
174
|
*/
|
|
174
175
|
constructor(initialState) {
|
|
175
176
|
Object.assign(this, initialState);
|
|
176
|
-
this.logger = logger;
|
|
177
177
|
this.screenshotMap = new Map();
|
|
178
178
|
this.snapshotMap = new Map();
|
|
179
179
|
this.scenariosStepsMap = new Map();
|
|
@@ -183,14 +183,10 @@ export class BVTRecorder {
|
|
|
183
183
|
projectDir: this.projectDir,
|
|
184
184
|
logger: this.logger,
|
|
185
185
|
});
|
|
186
|
-
this.stepRunner = new BVTStepRunner({
|
|
187
|
-
projectDir: this.projectDir,
|
|
188
|
-
});
|
|
189
186
|
this.pageSet = new Set();
|
|
190
|
-
|
|
187
|
+
this.pageMetaDataSet = new Set();
|
|
191
188
|
this.lastKnownUrlPath = "";
|
|
192
|
-
|
|
193
|
-
this.world = { attach: () => { } };
|
|
189
|
+
this.world = { attach: () => {} };
|
|
194
190
|
this.shouldTakeScreenshot = true;
|
|
195
191
|
this.watcher = null;
|
|
196
192
|
}
|
|
@@ -203,12 +199,16 @@ export class BVTRecorder {
|
|
|
203
199
|
onStepDetails: "BVTRecorder.onStepDetails",
|
|
204
200
|
getTestData: "BVTRecorder.getTestData",
|
|
205
201
|
onGoto: "BVTRecorder.onGoto",
|
|
202
|
+
cmdExecutionStart: "BVTRecorder.cmdExecutionStart",
|
|
203
|
+
cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
|
|
204
|
+
cmdExecutionError: "BVTRecorder.cmdExecutionError",
|
|
205
|
+
interceptResults: "BVTRecorder.interceptResults",
|
|
206
206
|
};
|
|
207
207
|
bindings = {
|
|
208
208
|
__bvt_recordCommand: async ({ frame, page, context }, event) => {
|
|
209
209
|
this.#activeFrame = frame;
|
|
210
210
|
const nestFrmLoc = await findNestedFrameSelector(frame);
|
|
211
|
-
|
|
211
|
+
this.logger.info(`Time taken for action: ${event.statistics.time}`);
|
|
212
212
|
await this.onAction({ ...event, nestFrmLoc });
|
|
213
213
|
},
|
|
214
214
|
__bvt_getMode: async () => {
|
|
@@ -227,8 +227,7 @@ export class BVTRecorder {
|
|
|
227
227
|
await this.onClosePopup();
|
|
228
228
|
},
|
|
229
229
|
__bvt_log: async (src, message) => {
|
|
230
|
-
|
|
231
|
-
console.log(`Inside Browser: ${message}`);
|
|
230
|
+
this.logger.info(`Inside Browser: ${message}`);
|
|
232
231
|
},
|
|
233
232
|
__bvt_getObject: (_src, obj) => {
|
|
234
233
|
this.processObject(obj);
|
|
@@ -293,8 +292,8 @@ export class BVTRecorder {
|
|
|
293
292
|
this.#remoteDebuggerPort = await findAvailablePort();
|
|
294
293
|
process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
|
|
295
294
|
|
|
296
|
-
this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
297
|
-
this.world = { attach: () => {
|
|
295
|
+
// this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
296
|
+
this.world = { attach: () => {} };
|
|
298
297
|
|
|
299
298
|
const ai_config_file = path.join(this.projectDir, "ai_config.json");
|
|
300
299
|
let ai_config = {};
|
|
@@ -302,9 +301,10 @@ export class BVTRecorder {
|
|
|
302
301
|
try {
|
|
303
302
|
ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
|
|
304
303
|
} catch (error) {
|
|
305
|
-
|
|
304
|
+
this.logger.error("Error reading ai_config.json", error);
|
|
306
305
|
}
|
|
307
306
|
}
|
|
307
|
+
this.config = ai_config;
|
|
308
308
|
const initScripts = {
|
|
309
309
|
// recorderCjs: injectedScriptSource,
|
|
310
310
|
scripts: [
|
|
@@ -313,16 +313,37 @@ export class BVTRecorder {
|
|
|
313
313
|
],
|
|
314
314
|
};
|
|
315
315
|
|
|
316
|
-
let startTime = Date.now();
|
|
317
316
|
const bvtContext = await initContext(url, false, false, this.world, 450, initScripts, this.envName);
|
|
318
|
-
let stopTime = Date.now();
|
|
319
|
-
this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
|
|
320
317
|
this.bvtContext = bvtContext;
|
|
318
|
+
this.stepRunner = new BVTStepRunner({
|
|
319
|
+
projectDir: this.projectDir,
|
|
320
|
+
sendExecutionStatus: (data) => {
|
|
321
|
+
if (data && data.type) {
|
|
322
|
+
switch (data.type) {
|
|
323
|
+
case "cmdExecutionStart":
|
|
324
|
+
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
325
|
+
break;
|
|
326
|
+
case "cmdExecutionSuccess":
|
|
327
|
+
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
328
|
+
break;
|
|
329
|
+
case "cmdExecutionError":
|
|
330
|
+
this.sendEvent(this.events.cmdExecutionError, data);
|
|
331
|
+
break;
|
|
332
|
+
case "interceptResults":
|
|
333
|
+
this.sendEvent(this.events.interceptResults, data);
|
|
334
|
+
break;
|
|
335
|
+
default:
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
bvtContext: this.bvtContext,
|
|
341
|
+
});
|
|
321
342
|
const context = bvtContext.playContext;
|
|
322
343
|
this.context = context;
|
|
323
344
|
this.web = bvtContext.stable || bvtContext.web;
|
|
345
|
+
this.web.tryAllStrategies = true;
|
|
324
346
|
this.page = bvtContext.page;
|
|
325
|
-
|
|
326
347
|
this.pageSet.add(this.page);
|
|
327
348
|
this.lastKnownUrlPath = this._updateUrlPath();
|
|
328
349
|
const browser = await this.context.browser();
|
|
@@ -350,13 +371,15 @@ export class BVTRecorder {
|
|
|
350
371
|
}
|
|
351
372
|
return;
|
|
352
373
|
} catch (error) {
|
|
353
|
-
console.error("Error evaluting in context:", error);
|
|
374
|
+
// console.error("Error evaluting in context:", error);
|
|
375
|
+
this.logger.error("Error evaluating in context:", error);
|
|
354
376
|
}
|
|
355
377
|
}
|
|
356
378
|
}
|
|
357
379
|
|
|
358
380
|
getMode() {
|
|
359
|
-
console.log("getMode", this.#mode);
|
|
381
|
+
// console.log("getMode", this.#mode);
|
|
382
|
+
this.logger.info("Current mode:", this.#mode);
|
|
360
383
|
return this.#mode;
|
|
361
384
|
}
|
|
362
385
|
|
|
@@ -398,6 +421,8 @@ export class BVTRecorder {
|
|
|
398
421
|
this.sendEvent(this.events.onBrowserClose);
|
|
399
422
|
}
|
|
400
423
|
} catch (error) {
|
|
424
|
+
this.logger.error("Error in page close event");
|
|
425
|
+
this.logger.error(error);
|
|
401
426
|
console.error("Error in page close event");
|
|
402
427
|
console.error(error);
|
|
403
428
|
}
|
|
@@ -408,8 +433,10 @@ export class BVTRecorder {
|
|
|
408
433
|
if (frame !== page.mainFrame()) return;
|
|
409
434
|
this.handlePageTransition();
|
|
410
435
|
} catch (error) {
|
|
436
|
+
this.logger.error("Error in handlePageTransition event");
|
|
437
|
+
this.logger.error(error);
|
|
411
438
|
console.error("Error in handlePageTransition event");
|
|
412
|
-
|
|
439
|
+
console.error(error);
|
|
413
440
|
}
|
|
414
441
|
try {
|
|
415
442
|
if (frame !== this.#activeFrame) return;
|
|
@@ -428,11 +455,82 @@ export class BVTRecorder {
|
|
|
428
455
|
// await this._setRecordingMode(frame);
|
|
429
456
|
// await this._initPage(page);
|
|
430
457
|
} catch (error) {
|
|
458
|
+
this.logger.error("Error in frame navigate event");
|
|
459
|
+
this.logger.error(error);
|
|
431
460
|
console.error("Error in frame navigate event");
|
|
432
461
|
// console.error(error);
|
|
433
462
|
}
|
|
434
463
|
});
|
|
435
464
|
}
|
|
465
|
+
|
|
466
|
+
hasHistoryReplacementAtIndex(previousEntries, currentEntries, index) {
|
|
467
|
+
if (!previousEntries || !currentEntries) return false;
|
|
468
|
+
if (index >= previousEntries.length || index >= currentEntries.length) return false;
|
|
469
|
+
|
|
470
|
+
const prevEntry = previousEntries[index];
|
|
471
|
+
// console.log("prevEntry", prevEntry);
|
|
472
|
+
const currEntry = currentEntries[index];
|
|
473
|
+
// console.log("currEntry", currEntry);
|
|
474
|
+
|
|
475
|
+
// Check if the entry at this index has been replaced
|
|
476
|
+
return prevEntry.id !== currEntry.id;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Even simpler approach for your specific case
|
|
480
|
+
analyzeTransitionType(entries, currentIndex, currentEntry) {
|
|
481
|
+
// console.log("Analyzing transition type");
|
|
482
|
+
// console.log("===========================");
|
|
483
|
+
// console.log("Current Index:", currentIndex);
|
|
484
|
+
// console.log("Current Entry:", currentEntry);
|
|
485
|
+
// console.log("Current Entries:", entries);
|
|
486
|
+
// console.log("Current entries length:", entries.length);
|
|
487
|
+
// console.log("===========================");
|
|
488
|
+
// console.log("Previous Index:", this.previousIndex);
|
|
489
|
+
// // console.log("Previous Entry:", this.previousEntries[this.previousIndex]);
|
|
490
|
+
// console.log("Previous Entries:", this.previousEntries);
|
|
491
|
+
// console.log("Previous entries length:", this.previousHistoryLength);
|
|
492
|
+
|
|
493
|
+
if (this.previousIndex === null || this.previousHistoryLength === null || !this.previousEntries) {
|
|
494
|
+
return {
|
|
495
|
+
action: "initial",
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const indexDiff = currentIndex - this.previousIndex;
|
|
500
|
+
const lengthDiff = entries.length - this.previousHistoryLength;
|
|
501
|
+
|
|
502
|
+
// Backward navigation
|
|
503
|
+
if (indexDiff < 0) {
|
|
504
|
+
return { action: "back" };
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Forward navigation
|
|
508
|
+
if (indexDiff > 0 && lengthDiff === 0) {
|
|
509
|
+
// Check if the entry at current index is the same as before
|
|
510
|
+
const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
|
|
511
|
+
|
|
512
|
+
if (entryReplaced) {
|
|
513
|
+
return { action: "navigate" }; // New navigation that replaced forward history
|
|
514
|
+
} else {
|
|
515
|
+
return { action: "forward" }; // True forward navigation
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// New navigation (history grew)
|
|
520
|
+
if (lengthDiff > 0) {
|
|
521
|
+
return { action: "navigate" };
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Same position, same length
|
|
525
|
+
if (lengthDiff <= 0) {
|
|
526
|
+
const entryReplaced = this.hasHistoryReplacementAtIndex(this.previousEntries, entries, currentIndex);
|
|
527
|
+
|
|
528
|
+
return entryReplaced ? { action: "navigate" } : { action: "reload" };
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return { action: "unknown" };
|
|
532
|
+
}
|
|
533
|
+
|
|
436
534
|
async getCurrentTransition() {
|
|
437
535
|
if (this?.web?.browser?._name !== "chromium") {
|
|
438
536
|
return;
|
|
@@ -441,35 +539,68 @@ export class BVTRecorder {
|
|
|
441
539
|
|
|
442
540
|
try {
|
|
443
541
|
const result = await client.send("Page.getNavigationHistory");
|
|
444
|
-
// console.log("result", result);
|
|
445
542
|
const entries = result.entries;
|
|
446
543
|
const currentIndex = result.currentIndex;
|
|
447
544
|
|
|
448
|
-
// ignore if currentIndex is not the last entry
|
|
449
|
-
if (currentIndex !== entries.length - 1) return;
|
|
450
|
-
|
|
451
545
|
const currentEntry = entries[currentIndex];
|
|
452
|
-
|
|
546
|
+
const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
|
|
547
|
+
this.previousIndex = currentIndex;
|
|
548
|
+
this.previousHistoryLength = entries.length;
|
|
549
|
+
this.previousUrl = currentEntry.url;
|
|
550
|
+
this.previousEntries = [...entries]; // Store a copy of current entries
|
|
551
|
+
|
|
552
|
+
return {
|
|
553
|
+
currentEntry,
|
|
554
|
+
navigationAction: transitionInfo.action,
|
|
555
|
+
};
|
|
453
556
|
} catch (error) {
|
|
454
|
-
|
|
557
|
+
this.logger.error("Error in getCurrentTransition event");
|
|
558
|
+
this.logger.error(error);
|
|
559
|
+
console.error("Error in getTransistionType event", error);
|
|
455
560
|
} finally {
|
|
456
561
|
await client.detach();
|
|
457
562
|
}
|
|
458
563
|
}
|
|
459
|
-
|
|
564
|
+
userInitiatedTransitionTypes = ["typed", "address_bar"];
|
|
460
565
|
async handlePageTransition() {
|
|
461
566
|
const transition = await this.getCurrentTransition();
|
|
462
567
|
if (!transition) return;
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
568
|
+
|
|
569
|
+
const { currentEntry, navigationAction } = transition;
|
|
570
|
+
|
|
571
|
+
switch (navigationAction) {
|
|
572
|
+
case "initial":
|
|
573
|
+
// console.log("Initial navigation, no action taken");
|
|
574
|
+
return;
|
|
575
|
+
case "navigate":
|
|
576
|
+
// console.log("transitionType", transition.transitionType);
|
|
577
|
+
// console.log("sending onGoto event", { url: currentEntry.url,
|
|
578
|
+
// type: "navigate", });
|
|
579
|
+
if (this.userInitiatedTransitionTypes.includes(currentEntry.transitionType)) {
|
|
580
|
+
const env = JSON.parse(readFileSync(this.envName), "utf8");
|
|
581
|
+
const baseUrl = env.baseUrl;
|
|
582
|
+
let url = currentEntry.userTypedURL;
|
|
583
|
+
if (baseUrl && url.startsWith(baseUrl)) {
|
|
584
|
+
url = url.replace(baseUrl, "{{env.baseUrl}}");
|
|
585
|
+
}
|
|
586
|
+
// console.log("User initiated transition");
|
|
587
|
+
this.sendEvent(this.events.onGoto, { url, type: "navigate" });
|
|
588
|
+
}
|
|
589
|
+
return;
|
|
590
|
+
case "back":
|
|
591
|
+
// console.log("User navigated back");
|
|
592
|
+
// console.log("sending onGoto event", {
|
|
593
|
+
// type: "back",
|
|
594
|
+
// });
|
|
595
|
+
this.sendEvent(this.events.onGoto, { type: "back" });
|
|
596
|
+
return;
|
|
597
|
+
case "forward":
|
|
598
|
+
// console.log("User navigated forward"); console.log("sending onGoto event", { type: "forward", });
|
|
599
|
+
this.sendEvent(this.events.onGoto, { type: "forward" });
|
|
600
|
+
return;
|
|
601
|
+
default:
|
|
602
|
+
this.sendEvent(this.events.onGoto, { type: "unknown" });
|
|
603
|
+
return;
|
|
473
604
|
}
|
|
474
605
|
}
|
|
475
606
|
|
|
@@ -493,6 +624,8 @@ export class BVTRecorder {
|
|
|
493
624
|
// add listener for frame navigation on new tab
|
|
494
625
|
this._addFrameNavigateListener(page);
|
|
495
626
|
} catch (error) {
|
|
627
|
+
this.logger.error("Error in page event");
|
|
628
|
+
this.logger.error(error);
|
|
496
629
|
console.error("Error in page event");
|
|
497
630
|
console.error(error);
|
|
498
631
|
}
|
|
@@ -534,6 +667,7 @@ export class BVTRecorder {
|
|
|
534
667
|
const { data } = await client.send("Page.captureScreenshot", { format: "png" });
|
|
535
668
|
return data;
|
|
536
669
|
} catch (error) {
|
|
670
|
+
this.logger.error("Error in taking browser screenshot");
|
|
537
671
|
console.error("Error in taking browser screenshot", error);
|
|
538
672
|
} finally {
|
|
539
673
|
await client.detach();
|
|
@@ -596,8 +730,13 @@ export class BVTRecorder {
|
|
|
596
730
|
}
|
|
597
731
|
async closeBrowser() {
|
|
598
732
|
delete process.env.TEMP_RUN;
|
|
599
|
-
await this.watcher.close().then(() => {
|
|
733
|
+
await this.watcher.close().then(() => {});
|
|
600
734
|
this.watcher = null;
|
|
735
|
+
this.previousIndex = null;
|
|
736
|
+
this.previousHistoryLength = null;
|
|
737
|
+
this.previousUrl = null;
|
|
738
|
+
this.previousEntries = null;
|
|
739
|
+
|
|
601
740
|
await closeContext();
|
|
602
741
|
this.pageSet.clear();
|
|
603
742
|
}
|
|
@@ -674,40 +813,66 @@ export class BVTRecorder {
|
|
|
674
813
|
async abortExecution() {
|
|
675
814
|
await this.stepRunner.abortExecution();
|
|
676
815
|
}
|
|
816
|
+
|
|
817
|
+
async pauseExecution({ cmdId }) {
|
|
818
|
+
await this.stepRunner.pauseExecution(cmdId);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
async resumeExecution({ cmdId }) {
|
|
822
|
+
await this.stepRunner.resumeExecution(cmdId);
|
|
823
|
+
}
|
|
824
|
+
|
|
677
825
|
async dealyedRevertMode() {
|
|
678
826
|
const timerId = setTimeout(async () => {
|
|
679
827
|
await this.revertMode();
|
|
680
828
|
}, 100);
|
|
681
829
|
this.timerId = timerId;
|
|
682
830
|
}
|
|
683
|
-
async runStep({ step, parametersMap }, options) {
|
|
831
|
+
async runStep({ step, parametersMap, tags, isFirstStep, listenNetwork }, options) {
|
|
832
|
+
const { skipAfter = true, skipBefore = !isFirstStep } = options || {};
|
|
684
833
|
const _env = {
|
|
685
834
|
TOKEN: this.TOKEN,
|
|
686
835
|
TEMP_RUN: true,
|
|
687
836
|
REPORT_FOLDER: this.bvtContext.reportFolder,
|
|
688
837
|
BLINQ_ENV: this.envName,
|
|
838
|
+
//STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
|
|
839
|
+
DEBUG: "blinq:route",
|
|
689
840
|
};
|
|
690
841
|
|
|
691
842
|
this.bvtContext.navigate = true;
|
|
843
|
+
this.bvtContext.loadedRoutes = null;
|
|
844
|
+
if (listenNetwork) {
|
|
845
|
+
process.env.STORE_DETAILED_NETWORK_DATA = "true";
|
|
846
|
+
} else {
|
|
847
|
+
process.env.STORE_DETAILED_NETWORK_DATA = "false";
|
|
848
|
+
}
|
|
692
849
|
for (const [key, value] of Object.entries(_env)) {
|
|
693
850
|
process.env[key] = value;
|
|
694
851
|
}
|
|
852
|
+
|
|
695
853
|
if (this.timerId) {
|
|
696
854
|
clearTimeout(this.timerId);
|
|
697
855
|
this.timerId = null;
|
|
698
856
|
}
|
|
699
857
|
await this.setMode("running");
|
|
858
|
+
|
|
700
859
|
try {
|
|
701
|
-
await this.stepRunner.runStep(
|
|
860
|
+
const { result, info } = await this.stepRunner.runStep(
|
|
702
861
|
{
|
|
703
862
|
step,
|
|
704
863
|
parametersMap,
|
|
705
864
|
envPath: this.envName,
|
|
865
|
+
tags,
|
|
866
|
+
config: this.config,
|
|
706
867
|
},
|
|
707
868
|
this.bvtContext,
|
|
708
|
-
|
|
869
|
+
{
|
|
870
|
+
skipAfter,
|
|
871
|
+
skipBefore,
|
|
872
|
+
}
|
|
709
873
|
);
|
|
710
874
|
await this.revertMode();
|
|
875
|
+
return { info };
|
|
711
876
|
} catch (error) {
|
|
712
877
|
await this.revertMode();
|
|
713
878
|
throw error;
|
|
@@ -718,15 +883,10 @@ export class BVTRecorder {
|
|
|
718
883
|
this.bvtContext.navigate = false;
|
|
719
884
|
}
|
|
720
885
|
}
|
|
721
|
-
async runScenario({ steps, parametersMap }) {
|
|
722
|
-
for (const step of steps) {
|
|
723
|
-
await this.runStep({ step, parametersMap });
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
886
|
async saveScenario({ scenario, featureName, override, isSingleStep }) {
|
|
727
887
|
await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
|
|
728
888
|
if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
|
|
729
|
-
await this.cleanup();
|
|
889
|
+
await this.cleanup({ tags: scenario.tags });
|
|
730
890
|
}
|
|
731
891
|
async getImplementedSteps() {
|
|
732
892
|
const stepsAndScenarios = await getImplementedSteps(this.projectDir);
|
|
@@ -750,6 +910,7 @@ export class BVTRecorder {
|
|
|
750
910
|
step.commands = [];
|
|
751
911
|
}
|
|
752
912
|
}
|
|
913
|
+
return steps;
|
|
753
914
|
// return getStepsAndCommandsForScenario({
|
|
754
915
|
// name,
|
|
755
916
|
// featureName,
|
|
@@ -757,6 +918,7 @@ export class BVTRecorder {
|
|
|
757
918
|
// map: this.scenariosStepsMap,
|
|
758
919
|
// });
|
|
759
920
|
}
|
|
921
|
+
|
|
760
922
|
async generateStepName({ commands, stepsNames, parameters, map }) {
|
|
761
923
|
return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
|
|
762
924
|
}
|
|
@@ -808,10 +970,11 @@ export class BVTRecorder {
|
|
|
808
970
|
if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
|
|
809
971
|
try {
|
|
810
972
|
const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
811
|
-
this.logger.info("Test data", testData);
|
|
973
|
+
// this.logger.info("Test data", testData);
|
|
812
974
|
this.sendEvent(this.events.getTestData, testData);
|
|
813
975
|
} catch (e) {
|
|
814
|
-
this.logger.error("Error reading test data file", e);
|
|
976
|
+
// this.logger.error("Error reading test data file", e);
|
|
977
|
+
console.log("Error reading test data file", e);
|
|
815
978
|
}
|
|
816
979
|
}
|
|
817
980
|
|
|
@@ -820,10 +983,12 @@ export class BVTRecorder {
|
|
|
820
983
|
this.watcher.on("all", async (event, path) => {
|
|
821
984
|
try {
|
|
822
985
|
const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
823
|
-
this.logger.info("Test data", testData);
|
|
986
|
+
// this.logger.info("Test data", testData);
|
|
987
|
+
console.log("Test data changed", testData);
|
|
824
988
|
this.sendEvent(this.events.getTestData, testData);
|
|
825
989
|
} catch (e) {
|
|
826
|
-
this.logger.error("Error reading test data file", e);
|
|
990
|
+
// this.logger.error("Error reading test data file", e);
|
|
991
|
+
console.log("Error reading test data file", e);
|
|
827
992
|
}
|
|
828
993
|
});
|
|
829
994
|
}
|
|
@@ -839,9 +1004,9 @@ export class BVTRecorder {
|
|
|
839
1004
|
}
|
|
840
1005
|
}
|
|
841
1006
|
|
|
842
|
-
async discardTestData() {
|
|
1007
|
+
async discardTestData({ tags }) {
|
|
843
1008
|
resetTestData(this.envName, this.world);
|
|
844
|
-
await this.cleanup();
|
|
1009
|
+
await this.cleanup({ tags });
|
|
845
1010
|
}
|
|
846
1011
|
async addToTestData(obj) {
|
|
847
1012
|
if (!existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
|
|
@@ -856,7 +1021,7 @@ export class BVTRecorder {
|
|
|
856
1021
|
.filter((file) => file.endsWith(".feature"))
|
|
857
1022
|
.map((file) => path.join(this.projectDir, "features", file));
|
|
858
1023
|
try {
|
|
859
|
-
const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
|
|
1024
|
+
const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
|
|
860
1025
|
const output = {};
|
|
861
1026
|
parsedFiles.forEach((file) => {
|
|
862
1027
|
if (!file.feature) return;
|
|
@@ -880,10 +1045,11 @@ export class BVTRecorder {
|
|
|
880
1045
|
const stepParams = parseStepTextParameters(stepName);
|
|
881
1046
|
return getCommandsForImplementedStep(stepName, step_definitions, stepParams).commands;
|
|
882
1047
|
}
|
|
1048
|
+
|
|
883
1049
|
loadExistingScenario({ featureName, scenarioName }) {
|
|
884
1050
|
const step_definitions = loadStepDefinitions(this.projectDir);
|
|
885
1051
|
const featureFilePath = path.join(this.projectDir, "features", featureName);
|
|
886
|
-
const gherkinDoc = parseFeatureFile(featureFilePath);
|
|
1052
|
+
const gherkinDoc = this.parseFeatureFile(featureFilePath);
|
|
887
1053
|
const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
|
|
888
1054
|
|
|
889
1055
|
const steps = [];
|
|
@@ -908,6 +1074,7 @@ export class BVTRecorder {
|
|
|
908
1074
|
..._s,
|
|
909
1075
|
keyword: step.keyword.trim(),
|
|
910
1076
|
};
|
|
1077
|
+
parseRouteFiles(this.projectDir, _step);
|
|
911
1078
|
steps.push(_step);
|
|
912
1079
|
}
|
|
913
1080
|
return {
|
|
@@ -947,7 +1114,7 @@ export class BVTRecorder {
|
|
|
947
1114
|
}
|
|
948
1115
|
return result;
|
|
949
1116
|
}
|
|
950
|
-
async cleanup() {
|
|
1117
|
+
async cleanup({ tags }) {
|
|
951
1118
|
const noopStep = {
|
|
952
1119
|
text: "Noop",
|
|
953
1120
|
isImplemented: true,
|
|
@@ -961,6 +1128,7 @@ export class BVTRecorder {
|
|
|
961
1128
|
{
|
|
962
1129
|
step: noopStep,
|
|
963
1130
|
parametersMap: {},
|
|
1131
|
+
tags: tags || [],
|
|
964
1132
|
},
|
|
965
1133
|
{
|
|
966
1134
|
skipAfter: false,
|
|
@@ -999,20 +1167,62 @@ export class BVTRecorder {
|
|
|
999
1167
|
return false;
|
|
1000
1168
|
}
|
|
1001
1169
|
}
|
|
1002
|
-
}
|
|
1170
|
+
async initExecution({ tags = [] }) {
|
|
1171
|
+
// run before hooks
|
|
1172
|
+
const noopStep = {
|
|
1173
|
+
text: "Noop",
|
|
1174
|
+
isImplemented: true,
|
|
1175
|
+
};
|
|
1176
|
+
await this.runStep(
|
|
1177
|
+
{
|
|
1178
|
+
step: noopStep,
|
|
1179
|
+
parametersMap: {},
|
|
1180
|
+
tags,
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
skipBefore: false,
|
|
1184
|
+
skipAfter: true,
|
|
1185
|
+
}
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
async cleanupExecution({ tags = [] }) {
|
|
1189
|
+
// run after hooks
|
|
1190
|
+
const noopStep = {
|
|
1191
|
+
text: "Noop",
|
|
1192
|
+
isImplemented: true,
|
|
1193
|
+
};
|
|
1194
|
+
await this.runStep(
|
|
1195
|
+
{
|
|
1196
|
+
step: noopStep,
|
|
1197
|
+
parametersMap: {},
|
|
1198
|
+
tags,
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
skipBefore: true,
|
|
1202
|
+
skipAfter: false,
|
|
1203
|
+
}
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
async resetExecution({ tags = [] }) {
|
|
1207
|
+
// run after hooks followed by before hooks
|
|
1208
|
+
await this.cleanupExecution({ tags });
|
|
1209
|
+
await this.initExecution({ tags });
|
|
1210
|
+
}
|
|
1003
1211
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1212
|
+
parseFeatureFile(featureFilePath) {
|
|
1213
|
+
try {
|
|
1214
|
+
let id = 0;
|
|
1215
|
+
const uuidFn = () => (++id).toString(16);
|
|
1216
|
+
const builder = new AstBuilder(uuidFn);
|
|
1217
|
+
const matcher = new GherkinClassicTokenMatcher();
|
|
1218
|
+
const parser = new Parser(builder, matcher);
|
|
1219
|
+
const source = readFileSync(featureFilePath, "utf8");
|
|
1220
|
+
const gherkinDocument = parser.parse(source);
|
|
1221
|
+
return gherkinDocument;
|
|
1222
|
+
} catch (e) {
|
|
1223
|
+
this.logger.error(`Error parsing feature file: ${featureFilePath}`);
|
|
1224
|
+
console.log(e);
|
|
1225
|
+
}
|
|
1226
|
+
return {};
|
|
1016
1227
|
}
|
|
1017
|
-
|
|
1018
|
-
};
|
|
1228
|
+
}
|