@dev-blinq/cucumber_client 1.0.1313-dev → 1.0.1313-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 +108 -108
- package/bin/assets/preload/css_gen.js +10 -10
- package/bin/assets/preload/recorderv3.js +3 -1
- package/bin/assets/preload/toolbar.js +27 -29
- package/bin/assets/preload/unique_locators.js +1 -1
- package/bin/assets/preload/yaml.js +288 -275
- package/bin/assets/scripts/aria_snapshot.js +223 -220
- package/bin/assets/scripts/dom_attr.js +329 -329
- package/bin/assets/scripts/dom_parent.js +169 -174
- package/bin/assets/scripts/event_utils.js +94 -94
- package/bin/assets/scripts/pw.js +2050 -1949
- package/bin/assets/scripts/recorder.js +5 -17
- package/bin/assets/scripts/snapshot_capturer.js +153 -146
- package/bin/assets/scripts/unique_locators.js +923 -817
- package/bin/assets/scripts/yaml.js +796 -783
- package/bin/assets/templates/_hooks_template.txt +41 -0
- package/bin/assets/templates/utils_template.txt +1 -44
- package/bin/client/apiTest/apiTest.js +6 -0
- package/bin/client/cli_helpers.js +11 -13
- package/bin/client/code_cleanup/utils.js +5 -1
- package/bin/client/code_gen/api_codegen.js +2 -2
- package/bin/client/code_gen/code_inversion.js +53 -4
- package/bin/client/code_gen/page_reflection.js +839 -906
- package/bin/client/code_gen/playwright_codeget.js +32 -17
- package/bin/client/cucumber/feature.js +89 -27
- package/bin/client/cucumber/feature_data.js +2 -2
- package/bin/client/cucumber/project_to_document.js +9 -3
- package/bin/client/cucumber/steps_definitions.js +90 -87
- package/bin/client/cucumber_selector.js +17 -1
- package/bin/client/local_agent.js +6 -5
- package/bin/client/parse_feature_file.js +23 -26
- package/bin/client/playground/projects/env.json +2 -2
- package/bin/client/project.js +186 -196
- package/bin/client/recorderv3/bvt_recorder.js +159 -75
- package/bin/client/recorderv3/implemented_steps.js +74 -16
- package/bin/client/recorderv3/index.js +65 -54
- package/bin/client/recorderv3/scriptTest.js +1 -1
- package/bin/client/recorderv3/services.js +4 -16
- package/bin/client/recorderv3/step_runner.js +313 -170
- package/bin/client/recorderv3/step_utils.js +508 -4
- package/bin/client/recorderv3/update_feature.js +32 -30
- 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/upload-service.js +2 -2
- package/bin/client/utils/socket_logger.js +132 -0
- package/bin/index.js +1 -0
- package/bin/logger.js +3 -2
- package/bin/min/consoleApi.min.cjs +2 -3
- package/bin/min/injectedScript.min.cjs +16 -16
- package/package.json +20 -11
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// define the jsdoc type for the input
|
|
2
2
|
import { closeContext, initContext, _getDataFile, resetTestData } from "automation_model";
|
|
3
|
-
import { existsSync,
|
|
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,11 +12,10 @@ 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";
|
|
20
19
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
21
20
|
|
|
22
21
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -45,15 +44,15 @@ async function evaluate(frame, script) {
|
|
|
45
44
|
async function findNestedFrameSelector(frame, obj) {
|
|
46
45
|
try {
|
|
47
46
|
const parent = frame.parentFrame();
|
|
48
|
-
if (parent) console.log(`Parent frame: ${JSON.stringify(parent)}`);
|
|
49
47
|
if (!parent) return { children: obj };
|
|
50
48
|
const frameElement = await frame.frameElement();
|
|
51
49
|
if (!frameElement) return;
|
|
52
50
|
const selectors = await parent.evaluate((element) => {
|
|
53
|
-
return window.
|
|
51
|
+
return window.__bvt_Recorder.locatorGenerator.getElementLocators(element, { excludeText: true }).locators;
|
|
54
52
|
}, frameElement);
|
|
55
53
|
return findNestedFrameSelector(parent, { children: obj, selectors });
|
|
56
54
|
} catch (e) {
|
|
55
|
+
socketLogger.error(`Error in findNestedFrameSelector: ${e}`);
|
|
57
56
|
console.error(e);
|
|
58
57
|
}
|
|
59
58
|
}
|
|
@@ -150,6 +149,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
|
|
|
150
149
|
};
|
|
151
150
|
}
|
|
152
151
|
default: {
|
|
152
|
+
socketLogger.error(`Action not supported: ${action.name}`);
|
|
153
153
|
console.log("action not supported", action);
|
|
154
154
|
throw new Error("action not supported");
|
|
155
155
|
}
|
|
@@ -161,6 +161,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
|
|
|
161
161
|
* @property {string} projectDir
|
|
162
162
|
* @property {string} TOKEN
|
|
163
163
|
* @property {(name:string, data:any)=> void} sendEvent
|
|
164
|
+
* @property {Object} logger
|
|
164
165
|
*/
|
|
165
166
|
export class BVTRecorder {
|
|
166
167
|
#currentURL = "";
|
|
@@ -174,7 +175,6 @@ export class BVTRecorder {
|
|
|
174
175
|
*/
|
|
175
176
|
constructor(initialState) {
|
|
176
177
|
Object.assign(this, initialState);
|
|
177
|
-
this.logger = logger;
|
|
178
178
|
this.screenshotMap = new Map();
|
|
179
179
|
this.snapshotMap = new Map();
|
|
180
180
|
this.scenariosStepsMap = new Map();
|
|
@@ -184,37 +184,16 @@ export class BVTRecorder {
|
|
|
184
184
|
projectDir: this.projectDir,
|
|
185
185
|
logger: this.logger,
|
|
186
186
|
});
|
|
187
|
-
this.stepRunner = new BVTStepRunner({
|
|
188
|
-
projectDir: this.projectDir,
|
|
189
|
-
sendExecutionStatus: (data) => {
|
|
190
|
-
if (data && data.type) {
|
|
191
|
-
switch (data.type) {
|
|
192
|
-
case "cmdExecutionStart":
|
|
193
|
-
console.log("Sending cmdExecutionStart event for cmdId:", data.cmdId);
|
|
194
|
-
this.sendEvent(this.events.cmdExecutionStart, data.cmdId);
|
|
195
|
-
break;
|
|
196
|
-
case "cmdExecutionSuccess":
|
|
197
|
-
console.log("Sending cmdExecutionSuccess event for cmdId:", data.cmdId);
|
|
198
|
-
this.sendEvent(this.events.cmdExecutionSuccess, data.cmdId);
|
|
199
|
-
break;
|
|
200
|
-
case "cmdExecutionFailure":
|
|
201
|
-
console.log("Sending cmdExecutionFailure event for cmdId:", data.cmdId);
|
|
202
|
-
this.sendEvent(this.events.cmdExecutionFailure, data.cmdId);
|
|
203
|
-
break;
|
|
204
|
-
default:
|
|
205
|
-
console.warn("Unknown command execution status type:", data.type);
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
187
|
this.pageSet = new Set();
|
|
212
|
-
this.
|
|
188
|
+
this.pageMetaDataSet = new Set();
|
|
213
189
|
this.lastKnownUrlPath = "";
|
|
214
|
-
// TODO: what is world?
|
|
215
190
|
this.world = { attach: () => {} };
|
|
216
191
|
this.shouldTakeScreenshot = true;
|
|
217
192
|
this.watcher = null;
|
|
193
|
+
this.networkEventsFolder = path.join(tmpdir(), "blinq_network_events");
|
|
194
|
+
if (existsSync(this.networkEventsFolder)) {
|
|
195
|
+
rmSync(this.networkEventsFolder, { recursive: true, force: true });
|
|
196
|
+
}
|
|
218
197
|
}
|
|
219
198
|
events = {
|
|
220
199
|
onFrameNavigate: "BVTRecorder.onFrameNavigate",
|
|
@@ -227,13 +206,14 @@ export class BVTRecorder {
|
|
|
227
206
|
onGoto: "BVTRecorder.onGoto",
|
|
228
207
|
cmdExecutionStart: "BVTRecorder.cmdExecutionStart",
|
|
229
208
|
cmdExecutionSuccess: "BVTRecorder.cmdExecutionSuccess",
|
|
230
|
-
|
|
209
|
+
cmdExecutionError: "BVTRecorder.cmdExecutionError",
|
|
210
|
+
interceptResults: "BVTRecorder.interceptResults",
|
|
231
211
|
};
|
|
232
212
|
bindings = {
|
|
233
213
|
__bvt_recordCommand: async ({ frame, page, context }, event) => {
|
|
234
214
|
this.#activeFrame = frame;
|
|
235
215
|
const nestFrmLoc = await findNestedFrameSelector(frame);
|
|
236
|
-
|
|
216
|
+
this.logger.info(`Time taken for action: ${event.statistics.time}`);
|
|
237
217
|
await this.onAction({ ...event, nestFrmLoc });
|
|
238
218
|
},
|
|
239
219
|
__bvt_getMode: async () => {
|
|
@@ -252,8 +232,7 @@ export class BVTRecorder {
|
|
|
252
232
|
await this.onClosePopup();
|
|
253
233
|
},
|
|
254
234
|
__bvt_log: async (src, message) => {
|
|
255
|
-
|
|
256
|
-
console.log(`Inside Browser: ${message}`);
|
|
235
|
+
this.logger.info(`Inside Browser: ${message}`);
|
|
257
236
|
},
|
|
258
237
|
__bvt_getObject: (_src, obj) => {
|
|
259
238
|
this.processObject(obj);
|
|
@@ -315,10 +294,11 @@ export class BVTRecorder {
|
|
|
315
294
|
}
|
|
316
295
|
|
|
317
296
|
async _initBrowser({ url }) {
|
|
297
|
+
socketLogger.info("Only present in 1.0.1293-stage");
|
|
318
298
|
this.#remoteDebuggerPort = await findAvailablePort();
|
|
319
299
|
process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
|
|
320
300
|
|
|
321
|
-
this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
301
|
+
// this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
322
302
|
this.world = { attach: () => {} };
|
|
323
303
|
|
|
324
304
|
const ai_config_file = path.join(this.projectDir, "ai_config.json");
|
|
@@ -327,9 +307,10 @@ export class BVTRecorder {
|
|
|
327
307
|
try {
|
|
328
308
|
ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
|
|
329
309
|
} catch (error) {
|
|
330
|
-
|
|
310
|
+
this.logger.error("Error reading ai_config.json", error);
|
|
331
311
|
}
|
|
332
312
|
}
|
|
313
|
+
this.config = ai_config;
|
|
333
314
|
const initScripts = {
|
|
334
315
|
// recorderCjs: injectedScriptSource,
|
|
335
316
|
scripts: [
|
|
@@ -338,16 +319,37 @@ export class BVTRecorder {
|
|
|
338
319
|
],
|
|
339
320
|
};
|
|
340
321
|
|
|
341
|
-
let startTime = Date.now();
|
|
342
322
|
const bvtContext = await initContext(url, false, false, this.world, 450, initScripts, this.envName);
|
|
343
|
-
let stopTime = Date.now();
|
|
344
|
-
this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
|
|
345
323
|
this.bvtContext = bvtContext;
|
|
324
|
+
this.stepRunner = new BVTStepRunner({
|
|
325
|
+
projectDir: this.projectDir,
|
|
326
|
+
sendExecutionStatus: (data) => {
|
|
327
|
+
if (data && data.type) {
|
|
328
|
+
switch (data.type) {
|
|
329
|
+
case "cmdExecutionStart":
|
|
330
|
+
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
331
|
+
break;
|
|
332
|
+
case "cmdExecutionSuccess":
|
|
333
|
+
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
334
|
+
break;
|
|
335
|
+
case "cmdExecutionError":
|
|
336
|
+
this.sendEvent(this.events.cmdExecutionError, data);
|
|
337
|
+
break;
|
|
338
|
+
case "interceptResults":
|
|
339
|
+
this.sendEvent(this.events.interceptResults, data);
|
|
340
|
+
break;
|
|
341
|
+
default:
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
bvtContext: this.bvtContext,
|
|
347
|
+
});
|
|
346
348
|
const context = bvtContext.playContext;
|
|
347
349
|
this.context = context;
|
|
348
350
|
this.web = bvtContext.stable || bvtContext.web;
|
|
351
|
+
this.web.tryAllStrategies = true;
|
|
349
352
|
this.page = bvtContext.page;
|
|
350
|
-
|
|
351
353
|
this.pageSet.add(this.page);
|
|
352
354
|
this.lastKnownUrlPath = this._updateUrlPath();
|
|
353
355
|
const browser = await this.context.browser();
|
|
@@ -375,13 +377,15 @@ export class BVTRecorder {
|
|
|
375
377
|
}
|
|
376
378
|
return;
|
|
377
379
|
} catch (error) {
|
|
378
|
-
console.error("Error evaluting in context:", error);
|
|
380
|
+
// console.error("Error evaluting in context:", error);
|
|
381
|
+
this.logger.error("Error evaluating in context:", error);
|
|
379
382
|
}
|
|
380
383
|
}
|
|
381
384
|
}
|
|
382
385
|
|
|
383
386
|
getMode() {
|
|
384
|
-
console.log("getMode", this.#mode);
|
|
387
|
+
// console.log("getMode", this.#mode);
|
|
388
|
+
this.logger.info("Current mode:", this.#mode);
|
|
385
389
|
return this.#mode;
|
|
386
390
|
}
|
|
387
391
|
|
|
@@ -423,6 +427,8 @@ export class BVTRecorder {
|
|
|
423
427
|
this.sendEvent(this.events.onBrowserClose);
|
|
424
428
|
}
|
|
425
429
|
} catch (error) {
|
|
430
|
+
this.logger.error("Error in page close event");
|
|
431
|
+
this.logger.error(error);
|
|
426
432
|
console.error("Error in page close event");
|
|
427
433
|
console.error(error);
|
|
428
434
|
}
|
|
@@ -433,8 +439,10 @@ export class BVTRecorder {
|
|
|
433
439
|
if (frame !== page.mainFrame()) return;
|
|
434
440
|
this.handlePageTransition();
|
|
435
441
|
} catch (error) {
|
|
442
|
+
this.logger.error("Error in handlePageTransition event");
|
|
443
|
+
this.logger.error(error);
|
|
436
444
|
console.error("Error in handlePageTransition event");
|
|
437
|
-
|
|
445
|
+
console.error(error);
|
|
438
446
|
}
|
|
439
447
|
try {
|
|
440
448
|
if (frame !== this.#activeFrame) return;
|
|
@@ -453,6 +461,8 @@ export class BVTRecorder {
|
|
|
453
461
|
// await this._setRecordingMode(frame);
|
|
454
462
|
// await this._initPage(page);
|
|
455
463
|
} catch (error) {
|
|
464
|
+
this.logger.error("Error in frame navigate event");
|
|
465
|
+
this.logger.error(error);
|
|
456
466
|
console.error("Error in frame navigate event");
|
|
457
467
|
// console.error(error);
|
|
458
468
|
}
|
|
@@ -535,13 +545,9 @@ export class BVTRecorder {
|
|
|
535
545
|
|
|
536
546
|
try {
|
|
537
547
|
const result = await client.send("Page.getNavigationHistory");
|
|
538
|
-
// console.log("Navigation History:", result);
|
|
539
548
|
const entries = result.entries;
|
|
540
549
|
const currentIndex = result.currentIndex;
|
|
541
550
|
|
|
542
|
-
// ignore if currentIndex is not the last entry
|
|
543
|
-
// if (currentIndex !== entries.length - 1) return;
|
|
544
|
-
|
|
545
551
|
const currentEntry = entries[currentIndex];
|
|
546
552
|
const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
|
|
547
553
|
this.previousIndex = currentIndex;
|
|
@@ -554,6 +560,8 @@ export class BVTRecorder {
|
|
|
554
560
|
navigationAction: transitionInfo.action,
|
|
555
561
|
};
|
|
556
562
|
} catch (error) {
|
|
563
|
+
this.logger.error("Error in getCurrentTransition event");
|
|
564
|
+
this.logger.error(error);
|
|
557
565
|
console.error("Error in getTransistionType event", error);
|
|
558
566
|
} finally {
|
|
559
567
|
await client.detach();
|
|
@@ -622,6 +630,8 @@ export class BVTRecorder {
|
|
|
622
630
|
// add listener for frame navigation on new tab
|
|
623
631
|
this._addFrameNavigateListener(page);
|
|
624
632
|
} catch (error) {
|
|
633
|
+
this.logger.error("Error in page event");
|
|
634
|
+
this.logger.error(error);
|
|
625
635
|
console.error("Error in page event");
|
|
626
636
|
console.error(error);
|
|
627
637
|
}
|
|
@@ -663,6 +673,7 @@ export class BVTRecorder {
|
|
|
663
673
|
const { data } = await client.send("Page.captureScreenshot", { format: "png" });
|
|
664
674
|
return data;
|
|
665
675
|
} catch (error) {
|
|
676
|
+
this.logger.error("Error in taking browser screenshot");
|
|
666
677
|
console.error("Error in taking browser screenshot", error);
|
|
667
678
|
} finally {
|
|
668
679
|
await client.detach();
|
|
@@ -808,6 +819,15 @@ export class BVTRecorder {
|
|
|
808
819
|
async abortExecution() {
|
|
809
820
|
await this.stepRunner.abortExecution();
|
|
810
821
|
}
|
|
822
|
+
|
|
823
|
+
async pauseExecution({ cmdId }) {
|
|
824
|
+
await this.stepRunner.pauseExecution(cmdId);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
async resumeExecution({ cmdId }) {
|
|
828
|
+
await this.stepRunner.resumeExecution(cmdId);
|
|
829
|
+
}
|
|
830
|
+
|
|
811
831
|
async dealyedRevertMode() {
|
|
812
832
|
const timerId = setTimeout(async () => {
|
|
813
833
|
await this.revertMode();
|
|
@@ -815,16 +835,21 @@ export class BVTRecorder {
|
|
|
815
835
|
this.timerId = timerId;
|
|
816
836
|
}
|
|
817
837
|
async runStep({ step, parametersMap, tags, isFirstStep, listenNetwork }, options) {
|
|
838
|
+
const { skipAfter = true, skipBefore = !isFirstStep } = options || {};
|
|
818
839
|
const _env = {
|
|
819
840
|
TOKEN: this.TOKEN,
|
|
820
841
|
TEMP_RUN: true,
|
|
821
842
|
REPORT_FOLDER: this.bvtContext.reportFolder,
|
|
822
843
|
BLINQ_ENV: this.envName,
|
|
823
|
-
STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
|
|
824
|
-
CURRENT_STEP_ID: step.id,
|
|
825
844
|
};
|
|
826
845
|
|
|
827
846
|
this.bvtContext.navigate = true;
|
|
847
|
+
this.bvtContext.loadedRoutes = null;
|
|
848
|
+
if (listenNetwork) {
|
|
849
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = true;
|
|
850
|
+
} else {
|
|
851
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
|
|
852
|
+
}
|
|
828
853
|
for (const [key, value] of Object.entries(_env)) {
|
|
829
854
|
process.env[key] = value;
|
|
830
855
|
}
|
|
@@ -842,9 +867,13 @@ export class BVTRecorder {
|
|
|
842
867
|
parametersMap,
|
|
843
868
|
envPath: this.envName,
|
|
844
869
|
tags,
|
|
870
|
+
config: this.config,
|
|
845
871
|
},
|
|
846
872
|
this.bvtContext,
|
|
847
|
-
|
|
873
|
+
{
|
|
874
|
+
skipAfter,
|
|
875
|
+
skipBefore,
|
|
876
|
+
}
|
|
848
877
|
);
|
|
849
878
|
await this.revertMode();
|
|
850
879
|
return { info };
|
|
@@ -885,6 +914,7 @@ export class BVTRecorder {
|
|
|
885
914
|
step.commands = [];
|
|
886
915
|
}
|
|
887
916
|
}
|
|
917
|
+
return steps;
|
|
888
918
|
// return getStepsAndCommandsForScenario({
|
|
889
919
|
// name,
|
|
890
920
|
// featureName,
|
|
@@ -892,6 +922,7 @@ export class BVTRecorder {
|
|
|
892
922
|
// map: this.scenariosStepsMap,
|
|
893
923
|
// });
|
|
894
924
|
}
|
|
925
|
+
|
|
895
926
|
async generateStepName({ commands, stepsNames, parameters, map }) {
|
|
896
927
|
return await this.namesService.generateStepName({ commands, stepsNames, parameters, map });
|
|
897
928
|
}
|
|
@@ -943,10 +974,11 @@ export class BVTRecorder {
|
|
|
943
974
|
if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
|
|
944
975
|
try {
|
|
945
976
|
const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
946
|
-
this.logger.info("Test data", testData);
|
|
977
|
+
// this.logger.info("Test data", testData);
|
|
947
978
|
this.sendEvent(this.events.getTestData, testData);
|
|
948
979
|
} catch (e) {
|
|
949
|
-
this.logger.error("Error reading test data file", e);
|
|
980
|
+
// this.logger.error("Error reading test data file", e);
|
|
981
|
+
console.log("Error reading test data file", e);
|
|
950
982
|
}
|
|
951
983
|
}
|
|
952
984
|
|
|
@@ -955,10 +987,12 @@ export class BVTRecorder {
|
|
|
955
987
|
this.watcher.on("all", async (event, path) => {
|
|
956
988
|
try {
|
|
957
989
|
const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
958
|
-
this.logger.info("Test data", testData);
|
|
990
|
+
// this.logger.info("Test data", testData);
|
|
991
|
+
console.log("Test data changed", testData);
|
|
959
992
|
this.sendEvent(this.events.getTestData, testData);
|
|
960
993
|
} catch (e) {
|
|
961
|
-
this.logger.error("Error reading test data file", e);
|
|
994
|
+
// this.logger.error("Error reading test data file", e);
|
|
995
|
+
console.log("Error reading test data file", e);
|
|
962
996
|
}
|
|
963
997
|
});
|
|
964
998
|
}
|
|
@@ -991,7 +1025,7 @@ export class BVTRecorder {
|
|
|
991
1025
|
.filter((file) => file.endsWith(".feature"))
|
|
992
1026
|
.map((file) => path.join(this.projectDir, "features", file));
|
|
993
1027
|
try {
|
|
994
|
-
const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
|
|
1028
|
+
const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
|
|
995
1029
|
const output = {};
|
|
996
1030
|
parsedFiles.forEach((file) => {
|
|
997
1031
|
if (!file.feature) return;
|
|
@@ -1015,10 +1049,11 @@ export class BVTRecorder {
|
|
|
1015
1049
|
const stepParams = parseStepTextParameters(stepName);
|
|
1016
1050
|
return getCommandsForImplementedStep(stepName, step_definitions, stepParams).commands;
|
|
1017
1051
|
}
|
|
1052
|
+
|
|
1018
1053
|
loadExistingScenario({ featureName, scenarioName }) {
|
|
1019
1054
|
const step_definitions = loadStepDefinitions(this.projectDir);
|
|
1020
1055
|
const featureFilePath = path.join(this.projectDir, "features", featureName);
|
|
1021
|
-
const gherkinDoc = parseFeatureFile(featureFilePath);
|
|
1056
|
+
const gherkinDoc = this.parseFeatureFile(featureFilePath);
|
|
1022
1057
|
const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
|
|
1023
1058
|
|
|
1024
1059
|
const steps = [];
|
|
@@ -1043,6 +1078,7 @@ export class BVTRecorder {
|
|
|
1043
1078
|
..._s,
|
|
1044
1079
|
keyword: step.keyword.trim(),
|
|
1045
1080
|
};
|
|
1081
|
+
parseRouteFiles(this.projectDir, _step);
|
|
1046
1082
|
steps.push(_step);
|
|
1047
1083
|
}
|
|
1048
1084
|
return {
|
|
@@ -1135,20 +1171,68 @@ export class BVTRecorder {
|
|
|
1135
1171
|
return false;
|
|
1136
1172
|
}
|
|
1137
1173
|
}
|
|
1138
|
-
}
|
|
1174
|
+
async initExecution({ tags = [] }) {
|
|
1175
|
+
// run before hooks
|
|
1176
|
+
const noopStep = {
|
|
1177
|
+
text: "Noop",
|
|
1178
|
+
isImplemented: true,
|
|
1179
|
+
};
|
|
1180
|
+
await this.runStep(
|
|
1181
|
+
{
|
|
1182
|
+
step: noopStep,
|
|
1183
|
+
parametersMap: {},
|
|
1184
|
+
tags,
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
skipBefore: false,
|
|
1188
|
+
skipAfter: true,
|
|
1189
|
+
}
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
async cleanupExecution({ tags = [] }) {
|
|
1193
|
+
// run after hooks
|
|
1194
|
+
const noopStep = {
|
|
1195
|
+
text: "Noop",
|
|
1196
|
+
isImplemented: true,
|
|
1197
|
+
};
|
|
1198
|
+
await this.runStep(
|
|
1199
|
+
{
|
|
1200
|
+
step: noopStep,
|
|
1201
|
+
parametersMap: {},
|
|
1202
|
+
tags,
|
|
1203
|
+
},
|
|
1204
|
+
{
|
|
1205
|
+
skipBefore: true,
|
|
1206
|
+
skipAfter: false,
|
|
1207
|
+
}
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
async resetExecution({ tags = [] }) {
|
|
1211
|
+
// run after hooks followed by before hooks
|
|
1212
|
+
await this.cleanupExecution({ tags });
|
|
1213
|
+
await this.initExecution({ tags });
|
|
1214
|
+
}
|
|
1139
1215
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1216
|
+
parseFeatureFile(featureFilePath) {
|
|
1217
|
+
try {
|
|
1218
|
+
let id = 0;
|
|
1219
|
+
const uuidFn = () => (++id).toString(16);
|
|
1220
|
+
const builder = new AstBuilder(uuidFn);
|
|
1221
|
+
const matcher = new GherkinClassicTokenMatcher();
|
|
1222
|
+
const parser = new Parser(builder, matcher);
|
|
1223
|
+
const source = readFileSync(featureFilePath, "utf8");
|
|
1224
|
+
const gherkinDocument = parser.parse(source);
|
|
1225
|
+
return gherkinDocument;
|
|
1226
|
+
} catch (e) {
|
|
1227
|
+
this.logger.error(`Error parsing feature file: ${featureFilePath}`);
|
|
1228
|
+
console.log(e);
|
|
1229
|
+
}
|
|
1230
|
+
return {};
|
|
1152
1231
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1232
|
+
|
|
1233
|
+
stopRecordingNetwork(input) {
|
|
1234
|
+
if (this.bvtContext) {
|
|
1235
|
+
this.bvtContext.STORE_DETAILED_NETWORK_DATA = false;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
|
|
2
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import url from "url";
|
|
5
5
|
import { findFilesWithExtension, StepsDefinitions } from "../cucumber/steps_definitions.js";
|
|
6
|
-
import { Feature } from "../cucumber/feature.js";
|
|
6
|
+
import { Feature, Step } from "../cucumber/feature.js";
|
|
7
7
|
import { CodePage } from "../code_gen/page_reflection.js";
|
|
8
8
|
import { getCommandsForImplementedStep, loadStepDefinitions } from "./step_utils.js";
|
|
9
9
|
import { parseStepTextParameters } from "../cucumber/utils.js";
|
|
@@ -13,7 +13,6 @@ const uuidFn = () => (++id).toString(16);
|
|
|
13
13
|
const builder = new AstBuilder(uuidFn);
|
|
14
14
|
const matcher = new GherkinClassicTokenMatcher();
|
|
15
15
|
const parser = new Parser(builder, matcher);
|
|
16
|
-
|
|
17
16
|
let i = 0;
|
|
18
17
|
const getImplId = () => {
|
|
19
18
|
return `I-${i++}`;
|
|
@@ -57,6 +56,53 @@ function memorySizeOf(obj) {
|
|
|
57
56
|
return sizeOf(obj);
|
|
58
57
|
}
|
|
59
58
|
|
|
59
|
+
export function parseRouteFiles(projectDir, step) {
|
|
60
|
+
const routeFolder = path.join(projectDir, "data", "routes");
|
|
61
|
+
const templateRouteMap = new Map();
|
|
62
|
+
|
|
63
|
+
if (!existsSync(routeFolder)) {
|
|
64
|
+
step.routeItems = null;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Go over all the files in the route folder and parse them
|
|
69
|
+
const routeFiles = readdirSync(routeFolder).filter((file) => file.endsWith(".json"));
|
|
70
|
+
for (const file of routeFiles) {
|
|
71
|
+
const filePath = path.join(routeFolder, file);
|
|
72
|
+
const routeData = JSON.parse(readFileSync(filePath, "utf8"));
|
|
73
|
+
if (routeData && routeData.template) {
|
|
74
|
+
const template = routeData.template;
|
|
75
|
+
const routes = routeData.routes;
|
|
76
|
+
|
|
77
|
+
templateRouteMap.set(template, routes);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!existsSync(routeFolder)) {
|
|
82
|
+
return null;
|
|
83
|
+
} else if (step && step.text) {
|
|
84
|
+
// Convert the step text to cucumber template
|
|
85
|
+
const cucumberStep = new Step();
|
|
86
|
+
cucumberStep.text = step.text;
|
|
87
|
+
const template = cucumberStep.getTemplate();
|
|
88
|
+
if (templateRouteMap.has(template)) {
|
|
89
|
+
const routeItems = templateRouteMap.get(template);
|
|
90
|
+
routeItems.forEach((item) => {
|
|
91
|
+
const filters = item.filters || {};
|
|
92
|
+
const queryParams = filters?.queryParams || {};
|
|
93
|
+
const queryParamsArray = Object.keys(queryParams).map((key) => ({
|
|
94
|
+
key: key,
|
|
95
|
+
value: queryParams[key],
|
|
96
|
+
}));
|
|
97
|
+
filters.queryParams = queryParamsArray || [];
|
|
98
|
+
});
|
|
99
|
+
step.routeItems = routeItems;
|
|
100
|
+
} else {
|
|
101
|
+
step.routeItems = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
60
106
|
export const getImplementedSteps = async (projectDir) => {
|
|
61
107
|
const foundErrors = [];
|
|
62
108
|
try {
|
|
@@ -70,6 +116,12 @@ export const getImplementedSteps = async (projectDir) => {
|
|
|
70
116
|
const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
|
|
71
117
|
//console.log({ utilsContent });
|
|
72
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
|
+
}
|
|
73
125
|
} catch (error) {
|
|
74
126
|
foundErrors.push({ error });
|
|
75
127
|
}
|
|
@@ -168,7 +220,10 @@ export const getImplementedSteps = async (projectDir) => {
|
|
|
168
220
|
}
|
|
169
221
|
stepLineSet.add(stepLine);
|
|
170
222
|
step.templateIndex = implementedSteps.length;
|
|
171
|
-
|
|
223
|
+
|
|
224
|
+
parseRouteFiles(projectDir, step);
|
|
225
|
+
|
|
226
|
+
const implementedStep = {
|
|
172
227
|
keyword: step.keyword.trim(),
|
|
173
228
|
keywordAlias: step.keywordAlias?.trim(),
|
|
174
229
|
text: updateStepText(template.pattern, step.parameters),
|
|
@@ -179,7 +234,10 @@ export const getImplementedSteps = async (projectDir) => {
|
|
|
179
234
|
templateIndex: step.templateIndex,
|
|
180
235
|
pattern: template.pattern,
|
|
181
236
|
paths: template.paths,
|
|
182
|
-
|
|
237
|
+
routeItems: step.routeItems,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
implementedSteps.push(implementedStep);
|
|
183
241
|
}
|
|
184
242
|
}
|
|
185
243
|
|
|
@@ -236,17 +294,17 @@ export const getImplementedSteps = async (projectDir) => {
|
|
|
236
294
|
for (const tag of scenario.tags) {
|
|
237
295
|
delete tag.location;
|
|
238
296
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
297
|
+
for (const scenario of scenarios) {
|
|
298
|
+
for (const step of scenario.steps) {
|
|
299
|
+
if (step.templateIndex === undefined) {
|
|
300
|
+
const cleanStepName = stepsDefinitions._stepNameToTemplate(step.text);
|
|
301
|
+
const index = implementedSteps.findIndex((istep) => {
|
|
302
|
+
return cleanStepName === istep.pattern;
|
|
303
|
+
});
|
|
304
|
+
step.templateIndex = index;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
250
308
|
}
|
|
251
309
|
if (foundErrors.length > 0) {
|
|
252
310
|
console.log("foundErrors", foundErrors);
|