@dev-blinq/cucumber_client 1.0.1377-dev → 1.0.1377-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 +32 -3
- 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 +8 -2
- package/bin/client/cucumber/steps_definitions.js +6 -3
- package/bin/client/cucumber_selector.js +17 -1
- package/bin/client/local_agent.js +3 -2
- 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_init.js +325 -0
- package/bin/client/recorderv3/bvt_recorder.js +301 -102
- package/bin/client/recorderv3/implemented_steps.js +8 -0
- package/bin/client/recorderv3/index.js +4 -303
- package/bin/client/recorderv3/scriptTest.js +1 -1
- package/bin/client/recorderv3/services.js +430 -154
- package/bin/client/recorderv3/step_runner.js +315 -206
- package/bin/client/recorderv3/step_utils.js +479 -25
- package/bin/client/recorderv3/update_feature.js +9 -5
- package/bin/client/recorderv3/wbr_entry.js +61 -0
- 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 -1
- 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, RemoteBrowserService } 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,20 @@ 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
|
-
|
|
218
|
-
this.world = { attach: () => {} };
|
|
204
|
+
this.world = { attach: () => { } };
|
|
219
205
|
this.shouldTakeScreenshot = true;
|
|
220
206
|
this.watcher = null;
|
|
207
|
+
this.networkEventsFolder = path.join(tmpdir(), "blinq_network_events");
|
|
208
|
+
this.tempProjectFolder = `${tmpdir()}/bvt_temp_project_${Math.floor(Math.random() * 1000000)}`;
|
|
209
|
+
this.tempSnapshotsFolder = path.join(this.tempProjectFolder, "data/snapshots");
|
|
210
|
+
|
|
211
|
+
if (existsSync(this.networkEventsFolder)) {
|
|
212
|
+
rmSync(this.networkEventsFolder, { recursive: true, force: true });
|
|
213
|
+
}
|
|
221
214
|
}
|
|
222
215
|
events = {
|
|
223
216
|
onFrameNavigate: "BVTRecorder.onFrameNavigate",
|
|
@@ -232,12 +225,16 @@ export class BVTRecorder {
|
|
|
232
225
|
cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
|
|
233
226
|
cmdExecutionError: "BVTRecorder.cmdExecutionError",
|
|
234
227
|
interceptResults: "BVTRecorder.interceptResults",
|
|
228
|
+
onDebugURLChange: "BVTRecorder.onDebugURLChange",
|
|
229
|
+
updateCommand: "BVTRecorder.updateCommand",
|
|
230
|
+
browserStateSync: "BrowserService.stateSync",
|
|
231
|
+
browserStateError: "BrowserService.stateError",
|
|
235
232
|
};
|
|
236
233
|
bindings = {
|
|
237
234
|
__bvt_recordCommand: async ({ frame, page, context }, event) => {
|
|
238
235
|
this.#activeFrame = frame;
|
|
239
236
|
const nestFrmLoc = await findNestedFrameSelector(frame);
|
|
240
|
-
|
|
237
|
+
this.logger.info(`Time taken for action: ${event.statistics.time}`);
|
|
241
238
|
await this.onAction({ ...event, nestFrmLoc });
|
|
242
239
|
},
|
|
243
240
|
__bvt_getMode: async () => {
|
|
@@ -256,8 +253,7 @@ export class BVTRecorder {
|
|
|
256
253
|
await this.onClosePopup();
|
|
257
254
|
},
|
|
258
255
|
__bvt_log: async (src, message) => {
|
|
259
|
-
|
|
260
|
-
console.log(`Inside Browser: ${message}`);
|
|
256
|
+
this.logger.info(`Inside Browser: ${message}`);
|
|
261
257
|
},
|
|
262
258
|
__bvt_getObject: (_src, obj) => {
|
|
263
259
|
this.processObject(obj);
|
|
@@ -319,11 +315,12 @@ export class BVTRecorder {
|
|
|
319
315
|
}
|
|
320
316
|
|
|
321
317
|
async _initBrowser({ url }) {
|
|
318
|
+
socketLogger.info("Only present in 1.0.1293-stage");
|
|
322
319
|
this.#remoteDebuggerPort = await findAvailablePort();
|
|
323
320
|
process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
|
|
324
321
|
|
|
325
|
-
this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
326
|
-
this.world = { attach: () => {} };
|
|
322
|
+
// this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
323
|
+
this.world = { attach: () => { } };
|
|
327
324
|
|
|
328
325
|
const ai_config_file = path.join(this.projectDir, "ai_config.json");
|
|
329
326
|
let ai_config = {};
|
|
@@ -331,7 +328,7 @@ export class BVTRecorder {
|
|
|
331
328
|
try {
|
|
332
329
|
ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
|
|
333
330
|
} catch (error) {
|
|
334
|
-
|
|
331
|
+
this.logger.error("Error reading ai_config.json", error);
|
|
335
332
|
}
|
|
336
333
|
}
|
|
337
334
|
this.config = ai_config;
|
|
@@ -343,18 +340,47 @@ export class BVTRecorder {
|
|
|
343
340
|
],
|
|
344
341
|
};
|
|
345
342
|
|
|
346
|
-
let startTime = Date.now();
|
|
347
343
|
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
344
|
this.bvtContext = bvtContext;
|
|
351
|
-
|
|
352
|
-
|
|
345
|
+
this.stepRunner = new BVTStepRunner({
|
|
346
|
+
projectDir: this.projectDir,
|
|
347
|
+
sendExecutionStatus: (data) => {
|
|
348
|
+
if (data && data.type) {
|
|
349
|
+
switch (data.type) {
|
|
350
|
+
case "cmdExecutionStart":
|
|
351
|
+
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
352
|
+
break;
|
|
353
|
+
case "cmdExecutionSuccess":
|
|
354
|
+
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
355
|
+
break;
|
|
356
|
+
case "cmdExecutionError":
|
|
357
|
+
this.sendEvent(this.events.cmdExecutionError, data);
|
|
358
|
+
break;
|
|
359
|
+
case "interceptResults":
|
|
360
|
+
this.sendEvent(this.events.interceptResults, data);
|
|
361
|
+
break;
|
|
362
|
+
default:
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
bvtContext: this.bvtContext,
|
|
368
|
+
});
|
|
369
|
+
this.context = bvtContext.playContext;
|
|
353
370
|
this.web = bvtContext.stable || bvtContext.web;
|
|
354
371
|
this.web.tryAllStrategies = true;
|
|
355
372
|
this.page = bvtContext.page;
|
|
356
|
-
|
|
357
373
|
this.pageSet.add(this.page);
|
|
374
|
+
if (process.env.REMOTE_RECORDER === "true") {
|
|
375
|
+
this.browserEmitter = new RemoteBrowserService({
|
|
376
|
+
CDP_CONNECT_URL: `http://localhost:${this.#remoteDebuggerPort}`,
|
|
377
|
+
context: this.context,
|
|
378
|
+
});
|
|
379
|
+
this.browserEmitter.on(this.events.browserStateSync, (state) => {
|
|
380
|
+
this.page = this.browserEmitter.getSelectedPage();
|
|
381
|
+
this.sendEvent(this.events.browserStateSync, state);
|
|
382
|
+
});
|
|
383
|
+
}
|
|
358
384
|
this.lastKnownUrlPath = this._updateUrlPath();
|
|
359
385
|
const browser = await this.context.browser();
|
|
360
386
|
this.browser = browser;
|
|
@@ -367,6 +393,14 @@ export class BVTRecorder {
|
|
|
367
393
|
this.web.onRestoreSaveState = (url) => {
|
|
368
394
|
this._initBrowser({ url });
|
|
369
395
|
};
|
|
396
|
+
|
|
397
|
+
// create a second browser for locator generation
|
|
398
|
+
this.backgroundBrowser = await chromium.launch({
|
|
399
|
+
headless: true,
|
|
400
|
+
});
|
|
401
|
+
this.backgroundContext = await this.backgroundBrowser.newContext({});
|
|
402
|
+
await this.backgroundContext.addInitScript({ content: this.getInitScripts(this.config) });
|
|
403
|
+
await this.backgroundContext.newPage();
|
|
370
404
|
}
|
|
371
405
|
async onClosePopup() {
|
|
372
406
|
// console.log("close popups");
|
|
@@ -381,13 +415,15 @@ export class BVTRecorder {
|
|
|
381
415
|
}
|
|
382
416
|
return;
|
|
383
417
|
} catch (error) {
|
|
384
|
-
console.error("Error evaluting in context:", error);
|
|
418
|
+
// console.error("Error evaluting in context:", error);
|
|
419
|
+
this.logger.error("Error evaluating in context:", error);
|
|
385
420
|
}
|
|
386
421
|
}
|
|
387
422
|
}
|
|
388
423
|
|
|
389
424
|
getMode() {
|
|
390
|
-
console.log("getMode", this.#mode);
|
|
425
|
+
// console.log("getMode", this.#mode);
|
|
426
|
+
this.logger.info("Current mode:", this.#mode);
|
|
391
427
|
return this.#mode;
|
|
392
428
|
}
|
|
393
429
|
|
|
@@ -412,7 +448,7 @@ export class BVTRecorder {
|
|
|
412
448
|
|
|
413
449
|
// eval init script on current tab
|
|
414
450
|
// await this._initPage(this.page);
|
|
415
|
-
this.#currentURL =
|
|
451
|
+
this.#currentURL = url;
|
|
416
452
|
|
|
417
453
|
await this.page.dispatchEvent("html", "scroll");
|
|
418
454
|
await delay(1000);
|
|
@@ -429,6 +465,8 @@ export class BVTRecorder {
|
|
|
429
465
|
this.sendEvent(this.events.onBrowserClose);
|
|
430
466
|
}
|
|
431
467
|
} catch (error) {
|
|
468
|
+
this.logger.error("Error in page close event");
|
|
469
|
+
this.logger.error(error);
|
|
432
470
|
console.error("Error in page close event");
|
|
433
471
|
console.error(error);
|
|
434
472
|
}
|
|
@@ -439,8 +477,10 @@ export class BVTRecorder {
|
|
|
439
477
|
if (frame !== page.mainFrame()) return;
|
|
440
478
|
this.handlePageTransition();
|
|
441
479
|
} catch (error) {
|
|
480
|
+
this.logger.error("Error in handlePageTransition event");
|
|
481
|
+
this.logger.error(error);
|
|
442
482
|
console.error("Error in handlePageTransition event");
|
|
443
|
-
|
|
483
|
+
console.error(error);
|
|
444
484
|
}
|
|
445
485
|
try {
|
|
446
486
|
if (frame !== this.#activeFrame) return;
|
|
@@ -450,15 +490,18 @@ export class BVTRecorder {
|
|
|
450
490
|
element: { inputID: "frame" },
|
|
451
491
|
});
|
|
452
492
|
|
|
453
|
-
const
|
|
493
|
+
const newUrl = frame.url();
|
|
494
|
+
const newPath = new URL(newUrl).pathname;
|
|
454
495
|
const newTitle = await frame.title();
|
|
455
|
-
|
|
496
|
+
const changed = diffPaths(this.#currentURL, newUrl);
|
|
497
|
+
|
|
498
|
+
if (changed) {
|
|
456
499
|
this.sendEvent(this.events.onFrameNavigate, { url: newPath, title: newTitle });
|
|
457
|
-
this.#currentURL =
|
|
500
|
+
this.#currentURL = newUrl;
|
|
458
501
|
}
|
|
459
|
-
// await this._setRecordingMode(frame);
|
|
460
|
-
// await this._initPage(page);
|
|
461
502
|
} catch (error) {
|
|
503
|
+
this.logger.error("Error in frame navigate event");
|
|
504
|
+
this.logger.error(error);
|
|
462
505
|
console.error("Error in frame navigate event");
|
|
463
506
|
// console.error(error);
|
|
464
507
|
}
|
|
@@ -541,13 +584,9 @@ export class BVTRecorder {
|
|
|
541
584
|
|
|
542
585
|
try {
|
|
543
586
|
const result = await client.send("Page.getNavigationHistory");
|
|
544
|
-
// console.log("Navigation History:", result);
|
|
545
587
|
const entries = result.entries;
|
|
546
588
|
const currentIndex = result.currentIndex;
|
|
547
589
|
|
|
548
|
-
// ignore if currentIndex is not the last entry
|
|
549
|
-
// if (currentIndex !== entries.length - 1) return;
|
|
550
|
-
|
|
551
590
|
const currentEntry = entries[currentIndex];
|
|
552
591
|
const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
|
|
553
592
|
this.previousIndex = currentIndex;
|
|
@@ -560,6 +599,8 @@ export class BVTRecorder {
|
|
|
560
599
|
navigationAction: transitionInfo.action,
|
|
561
600
|
};
|
|
562
601
|
} catch (error) {
|
|
602
|
+
this.logger.error("Error in getCurrentTransition event");
|
|
603
|
+
this.logger.error(error);
|
|
563
604
|
console.error("Error in getTransistionType event", error);
|
|
564
605
|
} finally {
|
|
565
606
|
await client.detach();
|
|
@@ -628,6 +669,8 @@ export class BVTRecorder {
|
|
|
628
669
|
// add listener for frame navigation on new tab
|
|
629
670
|
this._addFrameNavigateListener(page);
|
|
630
671
|
} catch (error) {
|
|
672
|
+
this.logger.error("Error in page event");
|
|
673
|
+
this.logger.error(error);
|
|
631
674
|
console.error("Error in page event");
|
|
632
675
|
console.error(error);
|
|
633
676
|
}
|
|
@@ -669,6 +712,7 @@ export class BVTRecorder {
|
|
|
669
712
|
const { data } = await client.send("Page.captureScreenshot", { format: "png" });
|
|
670
713
|
return data;
|
|
671
714
|
} catch (error) {
|
|
715
|
+
this.logger.error("Error in taking browser screenshot");
|
|
672
716
|
console.error("Error in taking browser screenshot", error);
|
|
673
717
|
} finally {
|
|
674
718
|
await client.detach();
|
|
@@ -684,6 +728,52 @@ export class BVTRecorder {
|
|
|
684
728
|
console.error("Error in saving screenshot: ", error);
|
|
685
729
|
}
|
|
686
730
|
}
|
|
731
|
+
async generateLocators(event) {
|
|
732
|
+
const snapshotDetails = event.snapshotDetails;
|
|
733
|
+
if (!snapshotDetails) {
|
|
734
|
+
throw new Error("No snapshot details found");
|
|
735
|
+
}
|
|
736
|
+
const mode = event.mode;
|
|
737
|
+
const inputID = event.element.inputID;
|
|
738
|
+
|
|
739
|
+
const { id, contextId, doc } = snapshotDetails;
|
|
740
|
+
// const selector = `[data-blinq-id="${id}"]`;
|
|
741
|
+
const newPage = await this.backgroundContext.newPage();
|
|
742
|
+
await newPage.setContent(doc, { waitUntil: "domcontentloaded" });
|
|
743
|
+
const locatorsObj = await newPage.evaluate(
|
|
744
|
+
([id, contextId, mode]) => {
|
|
745
|
+
const recorder = window.__bvt_Recorder;
|
|
746
|
+
const contextElement = document.querySelector(`[data-blinq-context-id="${contextId}"]`);
|
|
747
|
+
const el = document.querySelector(`[data-blinq-id="${id}"]`);
|
|
748
|
+
if (contextElement) {
|
|
749
|
+
const result = recorder.locatorGenerator.toContextLocators(el, contextElement);
|
|
750
|
+
return result;
|
|
751
|
+
}
|
|
752
|
+
const isRecordingText = mode === "recordingText";
|
|
753
|
+
return recorder.locatorGenerator.getElementLocators(el, {
|
|
754
|
+
excludeText: isRecordingText,
|
|
755
|
+
});
|
|
756
|
+
},
|
|
757
|
+
[id, contextId, mode]
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
console.log(`Generated locators: for ${inputID}: `, JSON.stringify(locatorsObj));
|
|
761
|
+
await newPage.close();
|
|
762
|
+
if (event.nestFrmLoc?.children) {
|
|
763
|
+
locatorsObj.nestFrmLoc = event.nestFrmLoc.children;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
this.sendEvent(this.events.updateCommand, {
|
|
767
|
+
locators: {
|
|
768
|
+
locators: locatorsObj.locators,
|
|
769
|
+
nestFrmLoc: locatorsObj.nestFrmLoc,
|
|
770
|
+
iframe_src: !event.frame.isTop ? event.frame.url : undefined,
|
|
771
|
+
},
|
|
772
|
+
allStrategyLocators: locatorsObj.allStrategyLocators,
|
|
773
|
+
inputID,
|
|
774
|
+
});
|
|
775
|
+
// const
|
|
776
|
+
}
|
|
687
777
|
async onAction(event) {
|
|
688
778
|
this._updateUrlPath();
|
|
689
779
|
// const locators = this.overlayLocators(event);
|
|
@@ -697,25 +787,26 @@ export class BVTRecorder {
|
|
|
697
787
|
event.mode === "recordingHover",
|
|
698
788
|
event.mode === "multiInspecting"
|
|
699
789
|
),
|
|
700
|
-
locators: {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
},
|
|
704
|
-
allStrategyLocators: event.allStrategyLocators,
|
|
790
|
+
// locators: {
|
|
791
|
+
// locators: event.locators,
|
|
792
|
+
// iframe_src: !event.frame.isTop ? event.frame.url : undefined,
|
|
793
|
+
// },
|
|
794
|
+
// allStrategyLocators: event.allStrategyLocators,
|
|
705
795
|
url: event.frame.url,
|
|
706
796
|
title: event.frame.title,
|
|
707
797
|
extract: {},
|
|
708
798
|
lastKnownUrlPath: this.lastKnownUrlPath,
|
|
709
799
|
};
|
|
710
|
-
if (event.nestFrmLoc?.children) {
|
|
711
|
-
|
|
712
|
-
}
|
|
800
|
+
// if (event.nestFrmLoc?.children) {
|
|
801
|
+
// cmdEvent.locators.nestFrmLoc = event.nestFrmLoc.children;
|
|
802
|
+
// }
|
|
713
803
|
// this.logger.info({ event });
|
|
714
804
|
if (this.shouldTakeScreenshot) {
|
|
715
805
|
await this.storeScreenshot(event);
|
|
716
806
|
}
|
|
717
807
|
this.sendEvent(this.events.onNewCommand, cmdEvent);
|
|
718
808
|
this._updateUrlPath();
|
|
809
|
+
await this.generateLocators(event);
|
|
719
810
|
}
|
|
720
811
|
_updateUrlPath() {
|
|
721
812
|
try {
|
|
@@ -731,13 +822,12 @@ export class BVTRecorder {
|
|
|
731
822
|
}
|
|
732
823
|
async closeBrowser() {
|
|
733
824
|
delete process.env.TEMP_RUN;
|
|
734
|
-
await this.watcher.close().then(() => {});
|
|
825
|
+
await this.watcher.close().then(() => { });
|
|
735
826
|
this.watcher = null;
|
|
736
827
|
this.previousIndex = null;
|
|
737
828
|
this.previousHistoryLength = null;
|
|
738
829
|
this.previousUrl = null;
|
|
739
830
|
this.previousEntries = null;
|
|
740
|
-
|
|
741
831
|
await closeContext();
|
|
742
832
|
this.pageSet.clear();
|
|
743
833
|
}
|
|
@@ -789,7 +879,6 @@ export class BVTRecorder {
|
|
|
789
879
|
}
|
|
790
880
|
|
|
791
881
|
async startRecordingInput() {
|
|
792
|
-
console.log("startRecordingInput");
|
|
793
882
|
await this.setMode("recordingInput");
|
|
794
883
|
}
|
|
795
884
|
async stopRecordingInput() {
|
|
@@ -813,9 +902,17 @@ export class BVTRecorder {
|
|
|
813
902
|
}
|
|
814
903
|
|
|
815
904
|
async abortExecution() {
|
|
816
|
-
this.bvtContext.web.abortedExecution = true;
|
|
817
905
|
await this.stepRunner.abortExecution();
|
|
818
906
|
}
|
|
907
|
+
|
|
908
|
+
async pauseExecution({ cmdId }) {
|
|
909
|
+
await this.stepRunner.pauseExecution(cmdId);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
async resumeExecution({ cmdId }) {
|
|
913
|
+
await this.stepRunner.resumeExecution(cmdId);
|
|
914
|
+
}
|
|
915
|
+
|
|
819
916
|
async dealyedRevertMode() {
|
|
820
917
|
const timerId = setTimeout(async () => {
|
|
821
918
|
await this.revertMode();
|
|
@@ -829,13 +926,17 @@ export class BVTRecorder {
|
|
|
829
926
|
TEMP_RUN: true,
|
|
830
927
|
REPORT_FOLDER: this.bvtContext.reportFolder,
|
|
831
928
|
BLINQ_ENV: this.envName,
|
|
832
|
-
|
|
833
|
-
|
|
929
|
+
DEBUG: "blinq:route",
|
|
930
|
+
BVT_TEMP_SNAPSHOTS_FOLDER: this.tempSnapshotsFolder,
|
|
834
931
|
};
|
|
835
932
|
|
|
836
933
|
this.bvtContext.navigate = true;
|
|
837
934
|
this.bvtContext.loadedRoutes = null;
|
|
838
|
-
|
|
935
|
+
if (listenNetwork) {
|
|
936
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = true;
|
|
937
|
+
} else {
|
|
938
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
|
|
939
|
+
}
|
|
839
940
|
for (const [key, value] of Object.entries(_env)) {
|
|
840
941
|
process.env[key] = value;
|
|
841
942
|
}
|
|
@@ -871,13 +972,25 @@ export class BVTRecorder {
|
|
|
871
972
|
delete process.env[key];
|
|
872
973
|
}
|
|
873
974
|
this.bvtContext.navigate = false;
|
|
874
|
-
this.bvtContext.web.abortedExecution = false;
|
|
875
975
|
}
|
|
876
976
|
}
|
|
877
|
-
async saveScenario({ scenario, featureName, override, isSingleStep }) {
|
|
878
|
-
await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
|
|
879
|
-
if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
|
|
880
|
-
await this.
|
|
977
|
+
async saveScenario({ scenario, featureName, override, isSingleStep, branch, isEditing }) {
|
|
978
|
+
// await updateStepDefinitions({ scenario, featureName, projectDir: this.projectDir }); // updates mjs files
|
|
979
|
+
// if (!isSingleStep) await updateFeatureFile({ featureName, scenario, override, projectDir: this.projectDir }); // updates gherkin files
|
|
980
|
+
const res = await this.workspaceService.saveScenario({
|
|
981
|
+
scenario,
|
|
982
|
+
featureName,
|
|
983
|
+
override,
|
|
984
|
+
isSingleStep,
|
|
985
|
+
branch,
|
|
986
|
+
isEditing,
|
|
987
|
+
projectId: path.basename(this.projectDir),
|
|
988
|
+
});
|
|
989
|
+
if (res.success) {
|
|
990
|
+
await this.cleanup({ tags: scenario.tags });
|
|
991
|
+
} else {
|
|
992
|
+
throw new Error(res.message || "Error saving scenario");
|
|
993
|
+
}
|
|
881
994
|
}
|
|
882
995
|
async getImplementedSteps() {
|
|
883
996
|
const stepsAndScenarios = await getImplementedSteps(this.projectDir);
|
|
@@ -961,10 +1074,11 @@ export class BVTRecorder {
|
|
|
961
1074
|
if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
|
|
962
1075
|
try {
|
|
963
1076
|
const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
964
|
-
this.logger.info("Test data", testData);
|
|
1077
|
+
// this.logger.info("Test data", testData);
|
|
965
1078
|
this.sendEvent(this.events.getTestData, testData);
|
|
966
1079
|
} catch (e) {
|
|
967
|
-
this.logger.error("Error reading test data file", e);
|
|
1080
|
+
// this.logger.error("Error reading test data file", e);
|
|
1081
|
+
console.log("Error reading test data file", e);
|
|
968
1082
|
}
|
|
969
1083
|
}
|
|
970
1084
|
|
|
@@ -973,10 +1087,12 @@ export class BVTRecorder {
|
|
|
973
1087
|
this.watcher.on("all", async (event, path) => {
|
|
974
1088
|
try {
|
|
975
1089
|
const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
976
|
-
this.logger.info("Test data", testData);
|
|
1090
|
+
// this.logger.info("Test data", testData);
|
|
1091
|
+
console.log("Test data changed", testData);
|
|
977
1092
|
this.sendEvent(this.events.getTestData, testData);
|
|
978
1093
|
} catch (e) {
|
|
979
|
-
this.logger.error("Error reading test data file", e);
|
|
1094
|
+
// this.logger.error("Error reading test data file", e);
|
|
1095
|
+
console.log("Error reading test data file", e);
|
|
980
1096
|
}
|
|
981
1097
|
});
|
|
982
1098
|
}
|
|
@@ -1009,7 +1125,7 @@ export class BVTRecorder {
|
|
|
1009
1125
|
.filter((file) => file.endsWith(".feature"))
|
|
1010
1126
|
.map((file) => path.join(this.projectDir, "features", file));
|
|
1011
1127
|
try {
|
|
1012
|
-
const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
|
|
1128
|
+
const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
|
|
1013
1129
|
const output = {};
|
|
1014
1130
|
parsedFiles.forEach((file) => {
|
|
1015
1131
|
if (!file.feature) return;
|
|
@@ -1037,7 +1153,7 @@ export class BVTRecorder {
|
|
|
1037
1153
|
loadExistingScenario({ featureName, scenarioName }) {
|
|
1038
1154
|
const step_definitions = loadStepDefinitions(this.projectDir);
|
|
1039
1155
|
const featureFilePath = path.join(this.projectDir, "features", featureName);
|
|
1040
|
-
const gherkinDoc = parseFeatureFile(featureFilePath);
|
|
1156
|
+
const gherkinDoc = this.parseFeatureFile(featureFilePath);
|
|
1041
1157
|
const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
|
|
1042
1158
|
|
|
1043
1159
|
const steps = [];
|
|
@@ -1196,20 +1312,103 @@ export class BVTRecorder {
|
|
|
1196
1312
|
await this.cleanupExecution({ tags });
|
|
1197
1313
|
await this.initExecution({ tags });
|
|
1198
1314
|
}
|
|
1199
|
-
}
|
|
1200
1315
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1316
|
+
parseFeatureFile(featureFilePath) {
|
|
1317
|
+
try {
|
|
1318
|
+
let id = 0;
|
|
1319
|
+
const uuidFn = () => (++id).toString(16);
|
|
1320
|
+
const builder = new AstBuilder(uuidFn);
|
|
1321
|
+
const matcher = new GherkinClassicTokenMatcher();
|
|
1322
|
+
const parser = new Parser(builder, matcher);
|
|
1323
|
+
const source = readFileSync(featureFilePath, "utf8");
|
|
1324
|
+
const gherkinDocument = parser.parse(source);
|
|
1325
|
+
return gherkinDocument;
|
|
1326
|
+
} catch (e) {
|
|
1327
|
+
this.logger.error(`Error parsing feature file: ${featureFilePath}`);
|
|
1328
|
+
console.log(e);
|
|
1329
|
+
}
|
|
1330
|
+
return {};
|
|
1213
1331
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1332
|
+
|
|
1333
|
+
stopRecordingNetwork(input) {
|
|
1334
|
+
if (this.bvtContext) {
|
|
1335
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
async fakeParams(params) {
|
|
1340
|
+
const newFakeParams = {};
|
|
1341
|
+
Object.keys(params).forEach((key) => {
|
|
1342
|
+
if (!params[key].startsWith("{{") || !params[key].endsWith("}}")) {
|
|
1343
|
+
newFakeParams[key] = params[key];
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
try {
|
|
1348
|
+
const value = params[key].substring(2, params[key].length - 2).trim();
|
|
1349
|
+
const faking = value.split("(")[0].split(".");
|
|
1350
|
+
let argument = value.substring(value.indexOf("(") + 1, value.lastIndexOf(")"));
|
|
1351
|
+
argument = isNaN(Number(argument)) || argument === "" ? argument : Number(argument);
|
|
1352
|
+
let fakeFunc = faker;
|
|
1353
|
+
faking.forEach((f) => {
|
|
1354
|
+
fakeFunc = fakeFunc[f];
|
|
1355
|
+
});
|
|
1356
|
+
const newValue = fakeFunc(argument);
|
|
1357
|
+
newFakeParams[key] = newValue;
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
newFakeParams[key] = params[key];
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
return newFakeParams;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
async getBrowserState() {
|
|
1367
|
+
try {
|
|
1368
|
+
const state = await this.browserEmitter?.getState();
|
|
1369
|
+
this.sendEvent(this.events.browserStateSync, state);
|
|
1370
|
+
} catch (error) {
|
|
1371
|
+
this.logger.error("Error getting browser state:", error);
|
|
1372
|
+
this.sendEvent(this.events.browserStateError, {
|
|
1373
|
+
message: "Error getting browser state",
|
|
1374
|
+
code: "GET_STATE_ERROR",
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
async createTab({ url }) {
|
|
1380
|
+
try {
|
|
1381
|
+
await this.browserEmitter?.createTab(url);
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
this.logger.error("Error creating tab:", error);
|
|
1384
|
+
this.sendEvent(this.events.browserStateError, {
|
|
1385
|
+
message: "Error creating tab",
|
|
1386
|
+
code: "CREATE_TAB_ERROR",
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
async closeTab({ pageId }) {
|
|
1392
|
+
try {
|
|
1393
|
+
await this.browserEmitter?.closeTab(pageId);
|
|
1394
|
+
} catch (error) {
|
|
1395
|
+
this.logger.error("Error closing tab:", error);
|
|
1396
|
+
this.sendEvent(this.events.browserStateError, {
|
|
1397
|
+
message: "Error closing tab",
|
|
1398
|
+
code: "CLOSE_TAB_ERROR",
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
async selectTab({ pageId }) {
|
|
1404
|
+
try {
|
|
1405
|
+
await this.browserEmitter?.selectTab(pageId);
|
|
1406
|
+
} catch (error) {
|
|
1407
|
+
this.logger.error("Error selecting tab:", error);
|
|
1408
|
+
this.sendEvent(this.events.browserStateError, {
|
|
1409
|
+
message: "Error selecting tab",
|
|
1410
|
+
code: "SELECT_TAB_ERROR",
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|