@dev-blinq/cucumber_client 1.0.1363-dev → 1.0.1363-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/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 +13 -23
- package/bin/assets/scripts/snapshot_capturer.js +147 -147
- package/bin/assets/scripts/unique_locators.js +163 -44
- 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/code_cleanup/utils.js +5 -1
- package/bin/client/code_gen/api_codegen.js +2 -2
- package/bin/client/code_gen/code_inversion.js +107 -2
- package/bin/client/code_gen/function_signature.js +4 -0
- package/bin/client/code_gen/page_reflection.js +846 -906
- package/bin/client/code_gen/playwright_codeget.js +45 -12
- package/bin/client/cucumber/feature.js +4 -0
- 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 +4 -3
- 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 +238 -95
- package/bin/client/recorderv3/implemented_steps.js +8 -0
- package/bin/client/recorderv3/index.js +59 -54
- package/bin/client/recorderv3/scriptTest.js +1 -1
- package/bin/client/recorderv3/services.js +66 -16
- package/bin/client/recorderv3/step_runner.js +315 -205
- package/bin/client/recorderv3/step_utils.js +476 -16
- package/bin/client/recorderv3/update_feature.js +9 -5
- package/bin/client/recording.js +1 -0
- package/bin/client/upload-service.js +3 -2
- package/bin/client/utils/socket_logger.js +132 -0
- package/bin/index.js +4 -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 +20 -10
|
@@ -4,7 +4,7 @@ import { existsSync, readdirSync, readFileSync, rmSync } from "fs";
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import url from "url";
|
|
6
6
|
import { getImplementedSteps, parseRouteFiles } from "./implemented_steps.js";
|
|
7
|
-
import { NamesService } from "./services.js";
|
|
7
|
+
import { NamesService, PublishService } from "./services.js";
|
|
8
8
|
import { BVTStepRunner } from "./step_runner.js";
|
|
9
9
|
import { readFile, writeFile } from "fs/promises";
|
|
10
10
|
import { updateStepDefinitions, loadStepDefinitions, getCommandsForImplementedStep } from "./step_utils.js";
|
|
@@ -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,7 +46,6 @@ 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;
|
|
@@ -54,6 +54,7 @@ async function findNestedFrameSelector(frame, obj) {
|
|
|
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,17 +151,30 @@ 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
|
}
|
|
156
158
|
}
|
|
157
159
|
};
|
|
160
|
+
const diffPaths = (currentPath, newPath) => {
|
|
161
|
+
const currentDomain = new URL(currentPath).hostname;
|
|
162
|
+
const newDomain = new URL(newPath).hostname;
|
|
163
|
+
if (currentDomain !== newDomain) {
|
|
164
|
+
return true;
|
|
165
|
+
} else {
|
|
166
|
+
const currentRoute = new URL(currentPath).pathname;
|
|
167
|
+
const newRoute = new URL(newPath).pathname;
|
|
168
|
+
return currentRoute !== newRoute;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
158
171
|
/**
|
|
159
172
|
* @typedef {Object} BVTRecorderInput
|
|
160
173
|
* @property {string} envName
|
|
161
174
|
* @property {string} projectDir
|
|
162
175
|
* @property {string} TOKEN
|
|
163
176
|
* @property {(name:string, data:any)=> void} sendEvent
|
|
177
|
+
* @property {Object} logger
|
|
164
178
|
*/
|
|
165
179
|
export class BVTRecorder {
|
|
166
180
|
#currentURL = "";
|
|
@@ -174,7 +188,6 @@ export class BVTRecorder {
|
|
|
174
188
|
*/
|
|
175
189
|
constructor(initialState) {
|
|
176
190
|
Object.assign(this, initialState);
|
|
177
|
-
this.logger = logger;
|
|
178
191
|
this.screenshotMap = new Map();
|
|
179
192
|
this.snapshotMap = new Map();
|
|
180
193
|
this.scenariosStepsMap = new Map();
|
|
@@ -184,40 +197,21 @@ export class BVTRecorder {
|
|
|
184
197
|
projectDir: this.projectDir,
|
|
185
198
|
logger: this.logger,
|
|
186
199
|
});
|
|
187
|
-
this.
|
|
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
|
-
});
|
|
200
|
+
this.workspaceService = new PublishService(this.TOKEN);
|
|
215
201
|
this.pageSet = new Set();
|
|
202
|
+
this.pageMetaDataSet = new Set();
|
|
216
203
|
this.lastKnownUrlPath = "";
|
|
217
|
-
// TODO: what is world?
|
|
218
204
|
this.world = { attach: () => {} };
|
|
219
205
|
this.shouldTakeScreenshot = true;
|
|
220
206
|
this.watcher = null;
|
|
207
|
+
this.networkEventsFolder = path.join(tmpdir(), "blinq_network_events");
|
|
208
|
+
|
|
209
|
+
this.tempProjectFolder = `${tmpdir()}/bvt_temp_project_${Math.floor(Math.random() * 1000000)}`;
|
|
210
|
+
this.tempSnapshotsFolder = path.join(this.tempProjectFolder, "data/snapshots");
|
|
211
|
+
|
|
212
|
+
if (existsSync(this.networkEventsFolder)) {
|
|
213
|
+
rmSync(this.networkEventsFolder, { recursive: true, force: true });
|
|
214
|
+
}
|
|
221
215
|
}
|
|
222
216
|
events = {
|
|
223
217
|
onFrameNavigate: "BVTRecorder.onFrameNavigate",
|
|
@@ -232,12 +226,14 @@ export class BVTRecorder {
|
|
|
232
226
|
cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
|
|
233
227
|
cmdExecutionError: "BVTRecorder.cmdExecutionError",
|
|
234
228
|
interceptResults: "BVTRecorder.interceptResults",
|
|
229
|
+
onDebugURLChange: "BVTRecorder.onDebugURLChange",
|
|
230
|
+
updateCommand: "BVTRecorder.updateCommand",
|
|
235
231
|
};
|
|
236
232
|
bindings = {
|
|
237
233
|
__bvt_recordCommand: async ({ frame, page, context }, event) => {
|
|
238
234
|
this.#activeFrame = frame;
|
|
239
235
|
const nestFrmLoc = await findNestedFrameSelector(frame);
|
|
240
|
-
|
|
236
|
+
this.logger.info(`Time taken for action: ${event.statistics.time}`);
|
|
241
237
|
await this.onAction({ ...event, nestFrmLoc });
|
|
242
238
|
},
|
|
243
239
|
__bvt_getMode: async () => {
|
|
@@ -256,8 +252,7 @@ export class BVTRecorder {
|
|
|
256
252
|
await this.onClosePopup();
|
|
257
253
|
},
|
|
258
254
|
__bvt_log: async (src, message) => {
|
|
259
|
-
|
|
260
|
-
console.log(`Inside Browser: ${message}`);
|
|
255
|
+
this.logger.info(`Inside Browser: ${message}`);
|
|
261
256
|
},
|
|
262
257
|
__bvt_getObject: (_src, obj) => {
|
|
263
258
|
this.processObject(obj);
|
|
@@ -319,10 +314,11 @@ export class BVTRecorder {
|
|
|
319
314
|
}
|
|
320
315
|
|
|
321
316
|
async _initBrowser({ url }) {
|
|
317
|
+
socketLogger.info("Only present in 1.0.1293-stage");
|
|
322
318
|
this.#remoteDebuggerPort = await findAvailablePort();
|
|
323
319
|
process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
|
|
324
320
|
|
|
325
|
-
this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
321
|
+
// this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
326
322
|
this.world = { attach: () => {} };
|
|
327
323
|
|
|
328
324
|
const ai_config_file = path.join(this.projectDir, "ai_config.json");
|
|
@@ -331,7 +327,7 @@ export class BVTRecorder {
|
|
|
331
327
|
try {
|
|
332
328
|
ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
|
|
333
329
|
} catch (error) {
|
|
334
|
-
|
|
330
|
+
this.logger.error("Error reading ai_config.json", error);
|
|
335
331
|
}
|
|
336
332
|
}
|
|
337
333
|
this.config = ai_config;
|
|
@@ -343,16 +339,37 @@ export class BVTRecorder {
|
|
|
343
339
|
],
|
|
344
340
|
};
|
|
345
341
|
|
|
346
|
-
let startTime = Date.now();
|
|
347
342
|
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
343
|
this.bvtContext = bvtContext;
|
|
344
|
+
this.stepRunner = new BVTStepRunner({
|
|
345
|
+
projectDir: this.projectDir,
|
|
346
|
+
sendExecutionStatus: (data) => {
|
|
347
|
+
if (data && data.type) {
|
|
348
|
+
switch (data.type) {
|
|
349
|
+
case "cmdExecutionStart":
|
|
350
|
+
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
351
|
+
break;
|
|
352
|
+
case "cmdExecutionSuccess":
|
|
353
|
+
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
354
|
+
break;
|
|
355
|
+
case "cmdExecutionError":
|
|
356
|
+
this.sendEvent(this.events.cmdExecutionError, data);
|
|
357
|
+
break;
|
|
358
|
+
case "interceptResults":
|
|
359
|
+
this.sendEvent(this.events.interceptResults, data);
|
|
360
|
+
break;
|
|
361
|
+
default:
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
bvtContext: this.bvtContext,
|
|
367
|
+
});
|
|
351
368
|
const context = bvtContext.playContext;
|
|
352
369
|
this.context = context;
|
|
353
370
|
this.web = bvtContext.stable || bvtContext.web;
|
|
371
|
+
this.web.tryAllStrategies = true;
|
|
354
372
|
this.page = bvtContext.page;
|
|
355
|
-
|
|
356
373
|
this.pageSet.add(this.page);
|
|
357
374
|
this.lastKnownUrlPath = this._updateUrlPath();
|
|
358
375
|
const browser = await this.context.browser();
|
|
@@ -366,6 +383,14 @@ export class BVTRecorder {
|
|
|
366
383
|
this.web.onRestoreSaveState = (url) => {
|
|
367
384
|
this._initBrowser({ url });
|
|
368
385
|
};
|
|
386
|
+
|
|
387
|
+
// create a second browser for locator generation
|
|
388
|
+
this.backgroundBrowser = await chromium.launch({
|
|
389
|
+
headless: true,
|
|
390
|
+
});
|
|
391
|
+
this.backgroundContext = await this.backgroundBrowser.newContext({});
|
|
392
|
+
await this.backgroundContext.addInitScript({ content: this.getInitScripts(this.config) });
|
|
393
|
+
await this.backgroundContext.newPage();
|
|
369
394
|
}
|
|
370
395
|
async onClosePopup() {
|
|
371
396
|
// console.log("close popups");
|
|
@@ -380,13 +405,15 @@ export class BVTRecorder {
|
|
|
380
405
|
}
|
|
381
406
|
return;
|
|
382
407
|
} catch (error) {
|
|
383
|
-
console.error("Error evaluting in context:", error);
|
|
408
|
+
// console.error("Error evaluting in context:", error);
|
|
409
|
+
this.logger.error("Error evaluating in context:", error);
|
|
384
410
|
}
|
|
385
411
|
}
|
|
386
412
|
}
|
|
387
413
|
|
|
388
414
|
getMode() {
|
|
389
|
-
console.log("getMode", this.#mode);
|
|
415
|
+
// console.log("getMode", this.#mode);
|
|
416
|
+
this.logger.info("Current mode:", this.#mode);
|
|
390
417
|
return this.#mode;
|
|
391
418
|
}
|
|
392
419
|
|
|
@@ -411,7 +438,7 @@ export class BVTRecorder {
|
|
|
411
438
|
|
|
412
439
|
// eval init script on current tab
|
|
413
440
|
// await this._initPage(this.page);
|
|
414
|
-
this.#currentURL =
|
|
441
|
+
this.#currentURL = url;
|
|
415
442
|
|
|
416
443
|
await this.page.dispatchEvent("html", "scroll");
|
|
417
444
|
await delay(1000);
|
|
@@ -428,6 +455,8 @@ export class BVTRecorder {
|
|
|
428
455
|
this.sendEvent(this.events.onBrowserClose);
|
|
429
456
|
}
|
|
430
457
|
} catch (error) {
|
|
458
|
+
this.logger.error("Error in page close event");
|
|
459
|
+
this.logger.error(error);
|
|
431
460
|
console.error("Error in page close event");
|
|
432
461
|
console.error(error);
|
|
433
462
|
}
|
|
@@ -438,8 +467,10 @@ export class BVTRecorder {
|
|
|
438
467
|
if (frame !== page.mainFrame()) return;
|
|
439
468
|
this.handlePageTransition();
|
|
440
469
|
} catch (error) {
|
|
470
|
+
this.logger.error("Error in handlePageTransition event");
|
|
471
|
+
this.logger.error(error);
|
|
441
472
|
console.error("Error in handlePageTransition event");
|
|
442
|
-
|
|
473
|
+
console.error(error);
|
|
443
474
|
}
|
|
444
475
|
try {
|
|
445
476
|
if (frame !== this.#activeFrame) return;
|
|
@@ -449,15 +480,18 @@ export class BVTRecorder {
|
|
|
449
480
|
element: { inputID: "frame" },
|
|
450
481
|
});
|
|
451
482
|
|
|
452
|
-
const
|
|
483
|
+
const newUrl = frame.url();
|
|
484
|
+
const newPath = new URL(newUrl).pathname;
|
|
453
485
|
const newTitle = await frame.title();
|
|
454
|
-
|
|
486
|
+
const changed = diffPaths(this.#currentURL, newUrl);
|
|
487
|
+
|
|
488
|
+
if (changed) {
|
|
455
489
|
this.sendEvent(this.events.onFrameNavigate, { url: newPath, title: newTitle });
|
|
456
|
-
this.#currentURL =
|
|
490
|
+
this.#currentURL = newUrl;
|
|
457
491
|
}
|
|
458
|
-
// await this._setRecordingMode(frame);
|
|
459
|
-
// await this._initPage(page);
|
|
460
492
|
} catch (error) {
|
|
493
|
+
this.logger.error("Error in frame navigate event");
|
|
494
|
+
this.logger.error(error);
|
|
461
495
|
console.error("Error in frame navigate event");
|
|
462
496
|
// console.error(error);
|
|
463
497
|
}
|
|
@@ -540,13 +574,9 @@ export class BVTRecorder {
|
|
|
540
574
|
|
|
541
575
|
try {
|
|
542
576
|
const result = await client.send("Page.getNavigationHistory");
|
|
543
|
-
// console.log("Navigation History:", result);
|
|
544
577
|
const entries = result.entries;
|
|
545
578
|
const currentIndex = result.currentIndex;
|
|
546
579
|
|
|
547
|
-
// ignore if currentIndex is not the last entry
|
|
548
|
-
// if (currentIndex !== entries.length - 1) return;
|
|
549
|
-
|
|
550
580
|
const currentEntry = entries[currentIndex];
|
|
551
581
|
const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
|
|
552
582
|
this.previousIndex = currentIndex;
|
|
@@ -559,6 +589,8 @@ export class BVTRecorder {
|
|
|
559
589
|
navigationAction: transitionInfo.action,
|
|
560
590
|
};
|
|
561
591
|
} catch (error) {
|
|
592
|
+
this.logger.error("Error in getCurrentTransition event");
|
|
593
|
+
this.logger.error(error);
|
|
562
594
|
console.error("Error in getTransistionType event", error);
|
|
563
595
|
} finally {
|
|
564
596
|
await client.detach();
|
|
@@ -627,6 +659,8 @@ export class BVTRecorder {
|
|
|
627
659
|
// add listener for frame navigation on new tab
|
|
628
660
|
this._addFrameNavigateListener(page);
|
|
629
661
|
} catch (error) {
|
|
662
|
+
this.logger.error("Error in page event");
|
|
663
|
+
this.logger.error(error);
|
|
630
664
|
console.error("Error in page event");
|
|
631
665
|
console.error(error);
|
|
632
666
|
}
|
|
@@ -668,6 +702,7 @@ export class BVTRecorder {
|
|
|
668
702
|
const { data } = await client.send("Page.captureScreenshot", { format: "png" });
|
|
669
703
|
return data;
|
|
670
704
|
} catch (error) {
|
|
705
|
+
this.logger.error("Error in taking browser screenshot");
|
|
671
706
|
console.error("Error in taking browser screenshot", error);
|
|
672
707
|
} finally {
|
|
673
708
|
await client.detach();
|
|
@@ -683,6 +718,52 @@ export class BVTRecorder {
|
|
|
683
718
|
console.error("Error in saving screenshot: ", error);
|
|
684
719
|
}
|
|
685
720
|
}
|
|
721
|
+
async generateLocators(event) {
|
|
722
|
+
const snapshotDetails = event.snapshotDetails;
|
|
723
|
+
if (!snapshotDetails) {
|
|
724
|
+
throw new Error("No snapshot details found");
|
|
725
|
+
}
|
|
726
|
+
const mode = event.mode;
|
|
727
|
+
const inputID = event.element.inputID;
|
|
728
|
+
|
|
729
|
+
const { id, contextId, doc } = snapshotDetails;
|
|
730
|
+
// const selector = `[data-blinq-id="${id}"]`;
|
|
731
|
+
const newPage = await this.backgroundContext.newPage();
|
|
732
|
+
await newPage.setContent(doc, { waitUntil: "domcontentloaded" });
|
|
733
|
+
const locatorsObj = await newPage.evaluate(
|
|
734
|
+
([id, contextId, mode]) => {
|
|
735
|
+
const recorder = window.__bvt_Recorder;
|
|
736
|
+
const contextElement = document.querySelector(`[data-blinq-context-id="${contextId}"]`);
|
|
737
|
+
const el = document.querySelector(`[data-blinq-id="${id}"]`);
|
|
738
|
+
if (contextElement) {
|
|
739
|
+
const result = recorder.locatorGenerator.toContextLocators(el, contextElement);
|
|
740
|
+
return result;
|
|
741
|
+
}
|
|
742
|
+
const isRecordingText = mode === "recordingText";
|
|
743
|
+
return recorder.locatorGenerator.getElementLocators(el, {
|
|
744
|
+
excludeText: isRecordingText,
|
|
745
|
+
});
|
|
746
|
+
},
|
|
747
|
+
[id, contextId, mode]
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
console.log(`Generated locators: for ${inputID}: `, JSON.stringify(locatorsObj));
|
|
751
|
+
await newPage.close();
|
|
752
|
+
if (event.nestFrmLoc?.children) {
|
|
753
|
+
locatorsObj.nestFrmLoc = event.nestFrmLoc.children;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
this.sendEvent(this.events.updateCommand, {
|
|
757
|
+
locators: {
|
|
758
|
+
locators: locatorsObj.locators,
|
|
759
|
+
nestFrmLoc: locatorsObj.nestFrmLoc,
|
|
760
|
+
iframe_src: !event.frame.isTop ? event.frame.url : undefined,
|
|
761
|
+
},
|
|
762
|
+
allStrategyLocators: locatorsObj.allStrategyLocators,
|
|
763
|
+
inputID,
|
|
764
|
+
});
|
|
765
|
+
// const
|
|
766
|
+
}
|
|
686
767
|
async onAction(event) {
|
|
687
768
|
this._updateUrlPath();
|
|
688
769
|
// const locators = this.overlayLocators(event);
|
|
@@ -696,25 +777,26 @@ export class BVTRecorder {
|
|
|
696
777
|
event.mode === "recordingHover",
|
|
697
778
|
event.mode === "multiInspecting"
|
|
698
779
|
),
|
|
699
|
-
locators: {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
},
|
|
703
|
-
allStrategyLocators: event.allStrategyLocators,
|
|
780
|
+
// locators: {
|
|
781
|
+
// locators: event.locators,
|
|
782
|
+
// iframe_src: !event.frame.isTop ? event.frame.url : undefined,
|
|
783
|
+
// },
|
|
784
|
+
// allStrategyLocators: event.allStrategyLocators,
|
|
704
785
|
url: event.frame.url,
|
|
705
786
|
title: event.frame.title,
|
|
706
787
|
extract: {},
|
|
707
788
|
lastKnownUrlPath: this.lastKnownUrlPath,
|
|
708
789
|
};
|
|
709
|
-
if (event.nestFrmLoc?.children) {
|
|
710
|
-
|
|
711
|
-
}
|
|
790
|
+
// if (event.nestFrmLoc?.children) {
|
|
791
|
+
// cmdEvent.locators.nestFrmLoc = event.nestFrmLoc.children;
|
|
792
|
+
// }
|
|
712
793
|
// this.logger.info({ event });
|
|
713
794
|
if (this.shouldTakeScreenshot) {
|
|
714
795
|
await this.storeScreenshot(event);
|
|
715
796
|
}
|
|
716
797
|
this.sendEvent(this.events.onNewCommand, cmdEvent);
|
|
717
798
|
this._updateUrlPath();
|
|
799
|
+
await this.generateLocators(event);
|
|
718
800
|
}
|
|
719
801
|
_updateUrlPath() {
|
|
720
802
|
try {
|
|
@@ -788,7 +870,6 @@ export class BVTRecorder {
|
|
|
788
870
|
}
|
|
789
871
|
|
|
790
872
|
async startRecordingInput() {
|
|
791
|
-
console.log("startRecordingInput");
|
|
792
873
|
await this.setMode("recordingInput");
|
|
793
874
|
}
|
|
794
875
|
async stopRecordingInput() {
|
|
@@ -812,9 +893,17 @@ export class BVTRecorder {
|
|
|
812
893
|
}
|
|
813
894
|
|
|
814
895
|
async abortExecution() {
|
|
815
|
-
this.bvtContext.web.abortedExecution = true;
|
|
816
896
|
await this.stepRunner.abortExecution();
|
|
817
897
|
}
|
|
898
|
+
|
|
899
|
+
async pauseExecution({ cmdId }) {
|
|
900
|
+
await this.stepRunner.pauseExecution(cmdId);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
async resumeExecution({ cmdId }) {
|
|
904
|
+
await this.stepRunner.resumeExecution(cmdId);
|
|
905
|
+
}
|
|
906
|
+
|
|
818
907
|
async dealyedRevertMode() {
|
|
819
908
|
const timerId = setTimeout(async () => {
|
|
820
909
|
await this.revertMode();
|
|
@@ -828,13 +917,17 @@ export class BVTRecorder {
|
|
|
828
917
|
TEMP_RUN: true,
|
|
829
918
|
REPORT_FOLDER: this.bvtContext.reportFolder,
|
|
830
919
|
BLINQ_ENV: this.envName,
|
|
831
|
-
|
|
832
|
-
|
|
920
|
+
DEBUG: "blinq:route",
|
|
921
|
+
BVT_TEMP_SNAPSHOTS_FOLDER: this.tempSnapshotsFolder,
|
|
833
922
|
};
|
|
834
923
|
|
|
835
924
|
this.bvtContext.navigate = true;
|
|
836
925
|
this.bvtContext.loadedRoutes = null;
|
|
837
|
-
|
|
926
|
+
if (listenNetwork) {
|
|
927
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = true;
|
|
928
|
+
} else {
|
|
929
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
|
|
930
|
+
}
|
|
838
931
|
for (const [key, value] of Object.entries(_env)) {
|
|
839
932
|
process.env[key] = value;
|
|
840
933
|
}
|
|
@@ -872,10 +965,23 @@ export class BVTRecorder {
|
|
|
872
965
|
this.bvtContext.navigate = false;
|
|
873
966
|
}
|
|
874
967
|
}
|
|
875
|
-
async saveScenario({ scenario, featureName, override, isSingleStep }) {
|
|
876
|
-
await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
|
|
877
|
-
if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
|
|
878
|
-
await this.
|
|
968
|
+
async saveScenario({ scenario, featureName, override, isSingleStep, branch, isEditing }) {
|
|
969
|
+
// await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
|
|
970
|
+
// if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
|
|
971
|
+
const res = await this.workspaceService.saveScenario({
|
|
972
|
+
scenario,
|
|
973
|
+
featureName,
|
|
974
|
+
override,
|
|
975
|
+
isSingleStep,
|
|
976
|
+
branch,
|
|
977
|
+
isEditing,
|
|
978
|
+
projectId: path.basename(this.projectDir),
|
|
979
|
+
});
|
|
980
|
+
if (res.success) {
|
|
981
|
+
await this.cleanup({ tags: scenario.tags });
|
|
982
|
+
} else {
|
|
983
|
+
throw new Error(res.message || "Error saving scenario");
|
|
984
|
+
}
|
|
879
985
|
}
|
|
880
986
|
async getImplementedSteps() {
|
|
881
987
|
const stepsAndScenarios = await getImplementedSteps(this.projectDir);
|
|
@@ -959,10 +1065,11 @@ export class BVTRecorder {
|
|
|
959
1065
|
if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
|
|
960
1066
|
try {
|
|
961
1067
|
const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
962
|
-
this.logger.info("Test data", testData);
|
|
1068
|
+
// this.logger.info("Test data", testData);
|
|
963
1069
|
this.sendEvent(this.events.getTestData, testData);
|
|
964
1070
|
} catch (e) {
|
|
965
|
-
this.logger.error("Error reading test data file", e);
|
|
1071
|
+
// this.logger.error("Error reading test data file", e);
|
|
1072
|
+
console.log("Error reading test data file", e);
|
|
966
1073
|
}
|
|
967
1074
|
}
|
|
968
1075
|
|
|
@@ -971,10 +1078,12 @@ export class BVTRecorder {
|
|
|
971
1078
|
this.watcher.on("all", async (event, path) => {
|
|
972
1079
|
try {
|
|
973
1080
|
const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
974
|
-
this.logger.info("Test data", testData);
|
|
1081
|
+
// this.logger.info("Test data", testData);
|
|
1082
|
+
console.log("Test data changed", testData);
|
|
975
1083
|
this.sendEvent(this.events.getTestData, testData);
|
|
976
1084
|
} catch (e) {
|
|
977
|
-
this.logger.error("Error reading test data file", e);
|
|
1085
|
+
// this.logger.error("Error reading test data file", e);
|
|
1086
|
+
console.log("Error reading test data file", e);
|
|
978
1087
|
}
|
|
979
1088
|
});
|
|
980
1089
|
}
|
|
@@ -1007,7 +1116,7 @@ export class BVTRecorder {
|
|
|
1007
1116
|
.filter((file) => file.endsWith(".feature"))
|
|
1008
1117
|
.map((file) => path.join(this.projectDir, "features", file));
|
|
1009
1118
|
try {
|
|
1010
|
-
const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
|
|
1119
|
+
const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
|
|
1011
1120
|
const output = {};
|
|
1012
1121
|
parsedFiles.forEach((file) => {
|
|
1013
1122
|
if (!file.feature) return;
|
|
@@ -1035,7 +1144,7 @@ export class BVTRecorder {
|
|
|
1035
1144
|
loadExistingScenario({ featureName, scenarioName }) {
|
|
1036
1145
|
const step_definitions = loadStepDefinitions(this.projectDir);
|
|
1037
1146
|
const featureFilePath = path.join(this.projectDir, "features", featureName);
|
|
1038
|
-
const gherkinDoc = parseFeatureFile(featureFilePath);
|
|
1147
|
+
const gherkinDoc = this.parseFeatureFile(featureFilePath);
|
|
1039
1148
|
const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
|
|
1040
1149
|
|
|
1041
1150
|
const steps = [];
|
|
@@ -1194,20 +1303,54 @@ export class BVTRecorder {
|
|
|
1194
1303
|
await this.cleanupExecution({ tags });
|
|
1195
1304
|
await this.initExecution({ tags });
|
|
1196
1305
|
}
|
|
1197
|
-
}
|
|
1198
1306
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1307
|
+
parseFeatureFile(featureFilePath) {
|
|
1308
|
+
try {
|
|
1309
|
+
let id = 0;
|
|
1310
|
+
const uuidFn = () => (++id).toString(16);
|
|
1311
|
+
const builder = new AstBuilder(uuidFn);
|
|
1312
|
+
const matcher = new GherkinClassicTokenMatcher();
|
|
1313
|
+
const parser = new Parser(builder, matcher);
|
|
1314
|
+
const source = readFileSync(featureFilePath, "utf8");
|
|
1315
|
+
const gherkinDocument = parser.parse(source);
|
|
1316
|
+
return gherkinDocument;
|
|
1317
|
+
} catch (e) {
|
|
1318
|
+
this.logger.error(`Error parsing feature file: ${featureFilePath}`);
|
|
1319
|
+
console.log(e);
|
|
1320
|
+
}
|
|
1321
|
+
return {};
|
|
1211
1322
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1323
|
+
|
|
1324
|
+
stopRecordingNetwork(input) {
|
|
1325
|
+
if (this.bvtContext) {
|
|
1326
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
async fakeParams(params) {
|
|
1331
|
+
const newFakeParams = {};
|
|
1332
|
+
Object.keys(params).forEach((key) => {
|
|
1333
|
+
if (!params[key].startsWith("{{") || !params[key].endsWith("}}")) {
|
|
1334
|
+
newFakeParams[key] = params[key];
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
try {
|
|
1339
|
+
const value = params[key].substring(2, params[key].length - 2).trim();
|
|
1340
|
+
const faking = value.split("(")[0].split(".");
|
|
1341
|
+
let argument = value.substring(value.indexOf("(") + 1, value.lastIndexOf(")"));
|
|
1342
|
+
argument = isNaN(Number(argument)) || argument === "" ? argument : Number(argument);
|
|
1343
|
+
let fakeFunc = faker;
|
|
1344
|
+
faking.forEach((f) => {
|
|
1345
|
+
fakeFunc = fakeFunc[f];
|
|
1346
|
+
});
|
|
1347
|
+
const newValue = fakeFunc(argument);
|
|
1348
|
+
newFakeParams[key] = newValue;
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
newFakeParams[key] = params[key];
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
|
|
1354
|
+
return newFakeParams;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
@@ -116,6 +116,12 @@ export const getImplementedSteps = async (projectDir) => {
|
|
|
116
116
|
const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
|
|
117
117
|
//console.log({ utilsContent });
|
|
118
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
|
+
}
|
|
119
125
|
} catch (error) {
|
|
120
126
|
foundErrors.push({ error });
|
|
121
127
|
}
|
|
@@ -229,6 +235,7 @@ export const getImplementedSteps = async (projectDir) => {
|
|
|
229
235
|
pattern: template.pattern,
|
|
230
236
|
paths: template.paths,
|
|
231
237
|
routeItems: step.routeItems,
|
|
238
|
+
isApiStep: template.source === "api",
|
|
232
239
|
};
|
|
233
240
|
|
|
234
241
|
implementedSteps.push(implementedStep);
|
|
@@ -254,6 +261,7 @@ export const getImplementedSteps = async (projectDir) => {
|
|
|
254
261
|
mjsFile: template.mjsFile,
|
|
255
262
|
pattern: template.pattern,
|
|
256
263
|
paths: template.paths,
|
|
264
|
+
isApiStep: template.source === "api",
|
|
257
265
|
};
|
|
258
266
|
// reconstruct the parameters
|
|
259
267
|
const parameters = [];
|