@dev-blinq/cucumber_client 1.0.1457-dev → 1.0.1457-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 +73 -73
- package/bin/assets/scripts/recorder.js +87 -49
- package/bin/assets/scripts/snapshot_capturer.js +10 -17
- package/bin/assets/scripts/unique_locators.js +169 -47
- package/bin/assets/templates/_hooks_template.txt +6 -2
- package/bin/assets/templates/utils_template.txt +16 -16
- package/bin/client/code_cleanup/utils.js +16 -7
- package/bin/client/code_gen/code_inversion.js +115 -0
- package/bin/client/code_gen/duplication_analysis.js +2 -1
- package/bin/client/code_gen/function_signature.js +4 -0
- package/bin/client/code_gen/page_reflection.js +92 -11
- package/bin/client/code_gen/playwright_codeget.js +165 -76
- package/bin/client/cucumber/feature.js +4 -17
- package/bin/client/cucumber/steps_definitions.js +13 -0
- package/bin/client/local_agent.js +1 -0
- package/bin/client/recorderv3/bvt_init.js +320 -0
- package/bin/client/recorderv3/bvt_recorder.js +1312 -63
- package/bin/client/recorderv3/implemented_steps.js +2 -0
- package/bin/client/recorderv3/index.js +3 -293
- package/bin/client/recorderv3/services.js +819 -142
- package/bin/client/recorderv3/step_runner.js +35 -6
- package/bin/client/recorderv3/step_utils.js +175 -95
- package/bin/client/recorderv3/update_feature.js +87 -39
- package/bin/client/recorderv3/wbr_entry.js +61 -0
- package/bin/client/recording.js +1 -0
- package/bin/client/upload-service.js +2 -0
- package/bin/client/utils/app_dir.js +21 -0
- package/bin/client/utils/socket_logger.js +87 -125
- package/bin/index.js +4 -1
- package/package.json +11 -5
- package/bin/client/recorderv3/app_dir.js +0 -23
- package/bin/client/recorderv3/network.js +0 -299
- package/bin/client/recorderv3/scriptTest.js +0 -5
- package/bin/client/recorderv3/ws_server.js +0 -72
|
@@ -67,13 +67,12 @@ export class BVTStepRunner {
|
|
|
67
67
|
resolve();
|
|
68
68
|
}
|
|
69
69
|
} else {
|
|
70
|
-
|
|
71
|
-
socketLogger.error(`No paused command found for cmdId: ${cmdId}`);
|
|
70
|
+
socketLogger.error(`No paused command found for cmdId: ${cmdId}`, undefined, "BVTStepRunner.resumeExecution");
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
async copyCodetoTempFolder({ step, parametersMap, tempFolderPath }) {
|
|
75
|
+
async copyCodetoTempFolder({ step, parametersMap, tempFolderPath, AICode }) {
|
|
77
76
|
if (!fs.existsSync(tempFolderPath)) {
|
|
78
77
|
fs.mkdirSync(tempFolderPath);
|
|
79
78
|
}
|
|
@@ -84,6 +83,18 @@ export class BVTStepRunner {
|
|
|
84
83
|
overwrite: true,
|
|
85
84
|
recursive: true,
|
|
86
85
|
});
|
|
86
|
+
|
|
87
|
+
// If AICode is provided, save it as well
|
|
88
|
+
if (AICode) {
|
|
89
|
+
for (const { mjsFileContent, mjsFile } of AICode) {
|
|
90
|
+
const mjsPath = path
|
|
91
|
+
.normalize(mjsFile)
|
|
92
|
+
.split(path.sep)
|
|
93
|
+
.filter((part) => part !== "features")
|
|
94
|
+
.join(path.sep);
|
|
95
|
+
writeFileSync(path.join(tempFolderPath, mjsPath), mjsFileContent);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
async writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags }) {
|
|
@@ -250,7 +261,7 @@ export class BVTStepRunner {
|
|
|
250
261
|
return { result, info };
|
|
251
262
|
}
|
|
252
263
|
|
|
253
|
-
async runStep({ step, parametersMap, envPath, tags, config }, bvtContext, options) {
|
|
264
|
+
async runStep({ step, parametersMap, envPath, tags, config, AICode }, bvtContext, options) {
|
|
254
265
|
// Create a new AbortController for this specific step execution
|
|
255
266
|
this.#currentStepController = new AbortController();
|
|
256
267
|
const { signal } = this.#currentStepController;
|
|
@@ -284,13 +295,27 @@ export class BVTStepRunner {
|
|
|
284
295
|
return cId;
|
|
285
296
|
};
|
|
286
297
|
}
|
|
298
|
+
if (bvtContext.api) {
|
|
299
|
+
bvtContext.api.getCmdId = () => {
|
|
300
|
+
if (cmdIDs.length === 0) {
|
|
301
|
+
cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId ?? cmd.id);
|
|
302
|
+
}
|
|
303
|
+
const cId = cmdIDs.shift();
|
|
304
|
+
this.sendExecutionStatus({
|
|
305
|
+
type: "cmdExecutionStart",
|
|
306
|
+
cmdId: cId,
|
|
307
|
+
});
|
|
308
|
+
this.#lastAttemptedCmdId = cId;
|
|
309
|
+
return cId;
|
|
310
|
+
};
|
|
311
|
+
}
|
|
287
312
|
|
|
288
313
|
const __temp_features_FolderName = "__temp_features" + Math.random().toString(36).substring(2, 7);
|
|
289
314
|
const tempFolderPath = path.join(this.projectDir, __temp_features_FolderName);
|
|
290
315
|
process.env.tempFeaturesFolderPath = __temp_features_FolderName;
|
|
291
316
|
process.env.TESTCASE_REPORT_FOLDER_PATH = tempFolderPath;
|
|
292
317
|
|
|
293
|
-
await this.copyCodetoTempFolder({ step, parametersMap, tempFolderPath });
|
|
318
|
+
await this.copyCodetoTempFolder({ step, parametersMap, tempFolderPath, AICode });
|
|
294
319
|
|
|
295
320
|
// Write abort wrapper code with this step's signal
|
|
296
321
|
await this.writeWrapperCode(tempFolderPath, signal);
|
|
@@ -306,7 +331,10 @@ export class BVTStepRunner {
|
|
|
306
331
|
});
|
|
307
332
|
}
|
|
308
333
|
|
|
309
|
-
if (
|
|
334
|
+
if (
|
|
335
|
+
step.isUtilStep ||
|
|
336
|
+
((!(step.isImplemented && !step.shouldOverride) || step.shouldMakeStepTextUnique) && step.commands.length > 0)
|
|
337
|
+
) {
|
|
310
338
|
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
311
339
|
const stepDefinitionFolderPath = path.join(tempFolderPath, "step_definitions");
|
|
312
340
|
if (!existsSync(stepDefinitionFolderPath)) {
|
|
@@ -321,6 +349,7 @@ export class BVTStepRunner {
|
|
|
321
349
|
projectDir: this.projectDir,
|
|
322
350
|
stepsDefinitions,
|
|
323
351
|
parametersMap,
|
|
352
|
+
logger: socketLogger,
|
|
324
353
|
});
|
|
325
354
|
if (codePage) {
|
|
326
355
|
await codePage.save(stepDefsFilePath);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import url from "url";
|
|
4
|
-
import logger from "../../logger.js";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import url from "node:url";
|
|
5
4
|
import { CodePage, getAiConfig } from "../code_gen/page_reflection.js";
|
|
6
5
|
import { generateCode, generatePageName } from "../code_gen/playwright_codeget.js";
|
|
7
6
|
import { invertCodeToCommand } from "../code_gen/code_inversion.js";
|
|
@@ -9,8 +8,9 @@ import { Step } from "../cucumber/feature.js";
|
|
|
9
8
|
import { locateDefinitionPath, StepsDefinitions } from "../cucumber/steps_definitions.js";
|
|
10
9
|
import { Recording } from "../recording.js";
|
|
11
10
|
import { generateApiCode } from "../code_gen/api_codegen.js";
|
|
12
|
-
import { tmpdir } from "os";
|
|
13
|
-
import { createHash } from "crypto";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { getErrorMessage } from "../utils/socket_logger.js";
|
|
14
14
|
|
|
15
15
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
16
16
|
|
|
@@ -48,7 +48,7 @@ const replaceLastOccurence = (str, search, replacement) => {
|
|
|
48
48
|
return str.substring(0, lastIndex) + replacement + str.substring(lastIndex + search.length);
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
const _toRecordingStep = (cmd) => {
|
|
51
|
+
export const _toRecordingStep = (cmd, stepText) => {
|
|
52
52
|
switch (cmd.type) {
|
|
53
53
|
case "hover_element": {
|
|
54
54
|
return {
|
|
@@ -250,6 +250,7 @@ const _toRecordingStep = (cmd) => {
|
|
|
250
250
|
},
|
|
251
251
|
parameters: [cmd.selectedField, cmd.value],
|
|
252
252
|
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
253
|
+
valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
|
|
253
254
|
};
|
|
254
255
|
}
|
|
255
256
|
case "conditional_wait": {
|
|
@@ -261,6 +262,7 @@ const _toRecordingStep = (cmd) => {
|
|
|
261
262
|
},
|
|
262
263
|
parameters: [cmd.timeout, cmd.selectedField, cmd.value],
|
|
263
264
|
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
265
|
+
valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
|
|
264
266
|
};
|
|
265
267
|
}
|
|
266
268
|
case "navigate": {
|
|
@@ -296,6 +298,8 @@ const _toRecordingStep = (cmd) => {
|
|
|
296
298
|
type: "verify_page_snapshot",
|
|
297
299
|
parameters: [cmd.value],
|
|
298
300
|
selectors: cmd.selectors,
|
|
301
|
+
data: cmd.data,
|
|
302
|
+
valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
|
|
299
303
|
};
|
|
300
304
|
}
|
|
301
305
|
default: {
|
|
@@ -332,45 +336,62 @@ const _parameterizeLocators = (locators, replacementFromValue, replacementToValu
|
|
|
332
336
|
}
|
|
333
337
|
return locators;
|
|
334
338
|
};
|
|
335
|
-
const parameterizeLocators = ({ cmd, locs, isValueVariable,
|
|
339
|
+
const parameterizeLocators = ({ cmd, locs, isValueVariable, isTargetValueVariable, parametersMap }) => {
|
|
336
340
|
if (isValueVariable) {
|
|
337
341
|
const variable = cmd.value.slice(1, -1);
|
|
338
|
-
const val = parametersMap[variable];
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
342
|
+
// const val = parametersMap[variable];
|
|
343
|
+
if (typeof cmd.text === "string") {
|
|
344
|
+
const replacementFromValue = cmd.text.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
|
|
345
|
+
if (replacementFromValue.length > 0) {
|
|
346
|
+
const replacementToValue = `{${variable}}`;
|
|
347
|
+
locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (isTargetValueVariable) {
|
|
352
|
+
const variable = cmd.targetValue.slice(1, -1);
|
|
353
|
+
// const val = parametersMap[variable];
|
|
354
|
+
if (typeof cmd.targetText === "string") {
|
|
355
|
+
const replacementFromValue = cmd.targetText.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
|
|
356
|
+
if (replacementFromValue.length > 0) {
|
|
357
|
+
const replacementToValue = `{${variable}}`;
|
|
358
|
+
locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
349
361
|
}
|
|
350
362
|
return locs;
|
|
351
363
|
};
|
|
352
364
|
|
|
353
365
|
//TODO: IMPORTAN
|
|
354
|
-
export const toRecordingStep = (cmd, parametersMap) => {
|
|
366
|
+
export const toRecordingStep = (cmd, parametersMap, stepText) => {
|
|
355
367
|
if (cmd.type === "api") {
|
|
356
368
|
return {
|
|
357
369
|
type: "api",
|
|
358
370
|
value: cmd.value,
|
|
359
371
|
};
|
|
360
372
|
}
|
|
361
|
-
const step = _toRecordingStep(cmd);
|
|
373
|
+
const step = _toRecordingStep(cmd, stepText);
|
|
362
374
|
const cmdID = {
|
|
363
375
|
cmdId: cmd.id,
|
|
376
|
+
options: cmd.options ?? null,
|
|
364
377
|
};
|
|
365
378
|
Object.assign(step, cmdID);
|
|
366
379
|
|
|
367
380
|
const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
|
|
368
381
|
|
|
369
382
|
if (!locatorsObject) return step;
|
|
383
|
+
|
|
384
|
+
const element_name = cmd?.locators?.element_name ?? `${cmd.label} ${cmd.role ?? "Text"}`;
|
|
385
|
+
locatorsObject.element_name = element_name;
|
|
386
|
+
|
|
370
387
|
const isValueVariable = isVariable(cmd.value);
|
|
371
|
-
const
|
|
388
|
+
const isTargetValueVariable = isVariable(cmd.targetValue);
|
|
389
|
+
const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
|
|
390
|
+
step.locators = locatorsObject;
|
|
391
|
+
step.allStrategyLocators = allStrategyLocators;
|
|
392
|
+
step.isLocatorsAssigned = true;
|
|
372
393
|
|
|
373
|
-
if (!isValueVariable && !
|
|
394
|
+
if (!isValueVariable && !isTargetValueVariable) {
|
|
374
395
|
return step;
|
|
375
396
|
}
|
|
376
397
|
|
|
@@ -378,7 +399,6 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
378
399
|
step.dataSource = "parameters";
|
|
379
400
|
step.dataKey = convertToIdentifier(cmd.value.slice(1, -1));
|
|
380
401
|
}
|
|
381
|
-
const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
|
|
382
402
|
|
|
383
403
|
if (!allStrategyLocators) {
|
|
384
404
|
let locs = locatorsObject.locators;
|
|
@@ -386,7 +406,7 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
386
406
|
cmd,
|
|
387
407
|
locs,
|
|
388
408
|
isValueVariable,
|
|
389
|
-
|
|
409
|
+
isTargetValueVariable,
|
|
390
410
|
parametersMap,
|
|
391
411
|
});
|
|
392
412
|
locatorsObject.locators = locs;
|
|
@@ -405,7 +425,7 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
405
425
|
cmd,
|
|
406
426
|
locs: locators,
|
|
407
427
|
isValueVariable,
|
|
408
|
-
|
|
428
|
+
isTargetValueVariable,
|
|
409
429
|
parametersMap,
|
|
410
430
|
});
|
|
411
431
|
}
|
|
@@ -442,7 +462,7 @@ export function getCodePage(stepDefsFilePath) {
|
|
|
442
462
|
export function getCucumberStep({ step }) {
|
|
443
463
|
const cucumberStep = new Step();
|
|
444
464
|
cucumberStep.loadFromJson({
|
|
445
|
-
text: step.text,
|
|
465
|
+
text: step.renamedText ? step.renamedText : step.text,
|
|
446
466
|
keyword: step.keyword,
|
|
447
467
|
keywordType: step.keywordType,
|
|
448
468
|
parameters: [],
|
|
@@ -460,12 +480,16 @@ export function getCucumberStep({ step }) {
|
|
|
460
480
|
|
|
461
481
|
function makeStepTextUnique(step, stepsDefinitions) {
|
|
462
482
|
// const utilsFilePath = path.join("features", "step_definitions", "utils.mjs");
|
|
463
|
-
let stepText = step.text;
|
|
483
|
+
let stepText = step.renamedText ? step.renamedText : step.text;
|
|
464
484
|
let stepIndex = 1;
|
|
465
485
|
// console.log("makeStepTextUnique", step.text);
|
|
466
486
|
let stepDef = stepsDefinitions.findMatchingStep(stepText);
|
|
467
487
|
// console.log({ stepDef });
|
|
468
|
-
if (
|
|
488
|
+
if (
|
|
489
|
+
stepDef &&
|
|
490
|
+
(stepDef?.file.endsWith("utils.mjs") || stepDef?.file.endsWith("default_page.mjs")) &&
|
|
491
|
+
!step.shouldMakeStepTextUnique
|
|
492
|
+
) {
|
|
469
493
|
return true;
|
|
470
494
|
}
|
|
471
495
|
while (stepDef) {
|
|
@@ -476,7 +500,18 @@ function makeStepTextUnique(step, stepsDefinitions) {
|
|
|
476
500
|
step.text = stepText;
|
|
477
501
|
}
|
|
478
502
|
|
|
479
|
-
export async function saveRecording({
|
|
503
|
+
export async function saveRecording({
|
|
504
|
+
step,
|
|
505
|
+
cucumberStep,
|
|
506
|
+
codePage,
|
|
507
|
+
projectDir,
|
|
508
|
+
stepsDefinitions,
|
|
509
|
+
parametersMap,
|
|
510
|
+
logger,
|
|
511
|
+
}) {
|
|
512
|
+
if (step.commands && Array.isArray(step.commands)) {
|
|
513
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap, step.text));
|
|
514
|
+
}
|
|
480
515
|
let routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
481
516
|
|
|
482
517
|
if (process.env.TEMP_RUN === "true") {
|
|
@@ -484,21 +519,19 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
484
519
|
rmSync(routesPath, { recursive: true });
|
|
485
520
|
}
|
|
486
521
|
mkdirSync(routesPath, { recursive: true });
|
|
487
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
522
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
488
523
|
} else {
|
|
489
524
|
if (existsSync(routesPath)) {
|
|
490
|
-
// remove the folder
|
|
491
525
|
try {
|
|
492
526
|
rmSync(routesPath, { recursive: true });
|
|
493
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
494
527
|
} catch (error) {
|
|
495
|
-
|
|
528
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
496
529
|
}
|
|
497
530
|
routesPath = path.join(projectDir, "data", "routes");
|
|
498
531
|
if (!existsSync(routesPath)) {
|
|
499
532
|
mkdirSync(routesPath, { recursive: true });
|
|
500
533
|
}
|
|
501
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
534
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
502
535
|
}
|
|
503
536
|
}
|
|
504
537
|
|
|
@@ -506,49 +539,63 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
506
539
|
return;
|
|
507
540
|
}
|
|
508
541
|
|
|
542
|
+
let isUtilStep = false;
|
|
543
|
+
let isChangedUtilStepName = false;
|
|
544
|
+
|
|
509
545
|
if (step.isImplemented && step.shouldOverride) {
|
|
510
|
-
|
|
546
|
+
const stepDef = stepsDefinitions.findMatchingStep(step.text);
|
|
511
547
|
codePage = getCodePage(stepDef.file);
|
|
512
548
|
} else {
|
|
513
|
-
|
|
549
|
+
isUtilStep = makeStepTextUnique(step, stepsDefinitions);
|
|
514
550
|
|
|
515
551
|
if (isUtilStep) {
|
|
516
|
-
|
|
552
|
+
isChangedUtilStepName =
|
|
553
|
+
step.renamedText && stepsDefinitions.findMatchingStep(step.renamedText)?.file.endsWith("default_page.mjs");
|
|
554
|
+
|
|
555
|
+
if (!isChangedUtilStepName) {
|
|
556
|
+
const isUtilStep = stepsDefinitions.findMatchingStep(step.text)?.file.endsWith("utils.mjs");
|
|
557
|
+
if (!step.renamedText || step.renamedText === step.text || isUtilStep) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
step.text = step.text.trim();
|
|
561
|
+
const { functionName } = stepsDefinitions.findMatchingStep(step.renamedText);
|
|
562
|
+
step.renamedText = functionName;
|
|
563
|
+
const newImportLine = `import { ${functionName} } from "./utils.mjs";\n`;
|
|
564
|
+
|
|
565
|
+
if (!codePage.fileContent.includes(newImportLine)) {
|
|
566
|
+
codePage.fileContent = newImportLine + codePage.fileContent;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
517
569
|
}
|
|
518
570
|
}
|
|
519
571
|
|
|
572
|
+
const renamedUtil = step.renamedText && isUtilStep;
|
|
573
|
+
|
|
520
574
|
routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
521
575
|
if (process.env.TEMP_RUN === "true") {
|
|
522
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
523
576
|
if (existsSync(routesPath)) {
|
|
524
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
525
577
|
rmSync(routesPath, { recursive: true });
|
|
526
578
|
}
|
|
527
579
|
mkdirSync(routesPath, { recursive: true });
|
|
528
|
-
|
|
529
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
580
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
530
581
|
} else {
|
|
531
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
532
582
|
if (existsSync(routesPath)) {
|
|
533
|
-
// remove the folder
|
|
534
583
|
try {
|
|
535
584
|
rmSync(routesPath, { recursive: true });
|
|
536
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
537
585
|
} catch (error) {
|
|
538
|
-
|
|
586
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
539
587
|
}
|
|
540
588
|
}
|
|
541
589
|
routesPath = path.join(projectDir, "data", "routes");
|
|
542
|
-
console.log("Saving routes to:", routesPath);
|
|
543
590
|
if (!existsSync(routesPath)) {
|
|
544
591
|
mkdirSync(routesPath, { recursive: true });
|
|
545
592
|
}
|
|
546
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
593
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
547
594
|
}
|
|
548
595
|
|
|
549
596
|
cucumberStep.text = step.text;
|
|
550
597
|
const recording = new Recording();
|
|
551
|
-
|
|
598
|
+
|
|
552
599
|
const steps = step.commands;
|
|
553
600
|
|
|
554
601
|
recording.loadFromObject({ steps, step: cucumberStep });
|
|
@@ -599,7 +646,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
599
646
|
stepsDefinitions
|
|
600
647
|
);
|
|
601
648
|
|
|
602
|
-
if (!step.isImplemented) {
|
|
649
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
603
650
|
stepsDefinitions.addStep({
|
|
604
651
|
name: step.text,
|
|
605
652
|
file: result.codePage.sourceFileName,
|
|
@@ -611,13 +658,16 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
611
658
|
return result.codePage;
|
|
612
659
|
} else {
|
|
613
660
|
const generateCodeResult = generateCode(recording, codePage, userData, projectDir, methodName);
|
|
614
|
-
|
|
615
|
-
|
|
661
|
+
console.log("Generated code for step:", step.text);
|
|
662
|
+
if (!isUtilStep && generateCodeResult.noCode === true) {
|
|
616
663
|
return generateCodeResult.page;
|
|
617
664
|
}
|
|
618
665
|
codePage = generateCodeResult.page;
|
|
619
666
|
methodName = generateCodeResult.methodName;
|
|
620
|
-
|
|
667
|
+
|
|
668
|
+
if (!renamedUtil) {
|
|
669
|
+
codePage.insertElements(generateCodeResult.elements);
|
|
670
|
+
}
|
|
621
671
|
|
|
622
672
|
const description = cucumberStep.text;
|
|
623
673
|
let path = null;
|
|
@@ -630,15 +680,43 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
630
680
|
protect = true;
|
|
631
681
|
}
|
|
632
682
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
683
|
+
|
|
684
|
+
if (renamedUtil) {
|
|
685
|
+
if (isChangedUtilStepName) {
|
|
686
|
+
const newFileContent = codePage.fileContent
|
|
687
|
+
.replace(`async function ${step.renamedText}`, `async function ${methodName}`)
|
|
688
|
+
.replace(`Then("${step.renamedText}"`, `// Then("${step.renamedText}"`)
|
|
689
|
+
.replace(`When("${step.renamedText}"`, `// When("${step.renamedText}"`)
|
|
690
|
+
.replace(`Given("${step.renamedText}"`, `// Given("${step.renamedText}"`);
|
|
691
|
+
|
|
692
|
+
codePage._init();
|
|
693
|
+
codePage.generateModel(newFileContent);
|
|
694
|
+
} else {
|
|
695
|
+
codePage.addInfraCommandUtil(
|
|
696
|
+
methodName,
|
|
697
|
+
description,
|
|
698
|
+
cucumberStep.parameters,
|
|
699
|
+
generateCodeResult.codeLines,
|
|
700
|
+
step.renamedText,
|
|
701
|
+
step.text,
|
|
702
|
+
parametersMap,
|
|
703
|
+
protect,
|
|
704
|
+
"recorder",
|
|
705
|
+
path
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
} else {
|
|
709
|
+
codePage.addInfraCommand(
|
|
710
|
+
methodName,
|
|
711
|
+
description,
|
|
712
|
+
cucumberStep.getVariablesList(),
|
|
713
|
+
generateCodeResult.codeLines,
|
|
714
|
+
protect,
|
|
715
|
+
"recorder",
|
|
716
|
+
path
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
642
720
|
const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
|
|
643
721
|
const stepResult = codePage.addCucumberStep(
|
|
644
722
|
keyword,
|
|
@@ -648,7 +726,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
648
726
|
step.finalTimeout
|
|
649
727
|
);
|
|
650
728
|
|
|
651
|
-
if (!step.isImplemented) {
|
|
729
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
652
730
|
stepsDefinitions.addStep({
|
|
653
731
|
name: step.text,
|
|
654
732
|
file: codePage.sourceFileName,
|
|
@@ -657,6 +735,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
657
735
|
}
|
|
658
736
|
|
|
659
737
|
codePage.removeUnusedElements();
|
|
738
|
+
codePage.mergeSimilarElements();
|
|
660
739
|
cucumberStep.methodName = methodName;
|
|
661
740
|
if (generateCodeResult.locatorsMetadata) {
|
|
662
741
|
codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
|
|
@@ -700,7 +779,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
700
779
|
const file = step?.file;
|
|
701
780
|
const locatorsJson = getLocatorsJson(file);
|
|
702
781
|
if (!step) {
|
|
703
|
-
throw new Error(
|
|
782
|
+
throw new Error(`Step definition not found: ${stepName}`);
|
|
704
783
|
}
|
|
705
784
|
isImplemented = true;
|
|
706
785
|
const { codeCommands, codePage, elements, parametersNames, error } =
|
|
@@ -708,26 +787,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
708
787
|
if (error) {
|
|
709
788
|
throw new Error(error);
|
|
710
789
|
}
|
|
790
|
+
isUtilStep = codePage.sourceFileName.endsWith("utils.mjs") || codePage.sourceFileName.endsWith("default_page.mjs");
|
|
711
791
|
|
|
712
792
|
if (parametersNames.length !== stepParams.length) {
|
|
713
793
|
// console.log("Parameters mismatch", parametersNames, stepParams);
|
|
714
794
|
throw new Error("Parameters mismatch");
|
|
715
795
|
}
|
|
716
|
-
for (let i = 0; i < parametersNames.length; i++) {
|
|
717
|
-
stepParams[i].argumentName = parametersNames[i];
|
|
718
|
-
}
|
|
719
796
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
797
|
+
const pattern = step.name;
|
|
798
|
+
if (isUtilStep && pattern === "Verify the file {string} exists") {
|
|
799
|
+
commands.push({
|
|
800
|
+
type: "verify_file_exists",
|
|
801
|
+
parameters: [stepParams[0].text],
|
|
802
|
+
});
|
|
803
|
+
} else {
|
|
804
|
+
for (let i = 0; i < parametersNames.length; i++) {
|
|
805
|
+
stepParams[i].argumentName = parametersNames[i];
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
for (const { code } of codeCommands) {
|
|
809
|
+
const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
|
|
810
|
+
if (command === undefined || command.type === null) continue;
|
|
811
|
+
if (command.element) {
|
|
812
|
+
const key = command.element.key;
|
|
813
|
+
if (key && locatorsJson[key]) {
|
|
814
|
+
command.allStrategyLocators = locatorsJson[key];
|
|
815
|
+
}
|
|
728
816
|
}
|
|
817
|
+
commands.push(command);
|
|
729
818
|
}
|
|
730
|
-
commands.push(command);
|
|
731
819
|
}
|
|
732
820
|
} catch (error) {
|
|
733
821
|
console.error(error);
|
|
@@ -775,7 +863,7 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
|
|
|
775
863
|
}
|
|
776
864
|
}
|
|
777
865
|
|
|
778
|
-
export async function updateStepDefinitions({ scenario, featureName, projectDir }) {
|
|
866
|
+
export async function updateStepDefinitions({ scenario, featureName, projectDir, logger }) {
|
|
779
867
|
// set the candidate step definition file name
|
|
780
868
|
// set the utils file path
|
|
781
869
|
const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
|
|
@@ -806,42 +894,37 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
806
894
|
step.isImplementedWhileRecording = true;
|
|
807
895
|
}
|
|
808
896
|
}
|
|
809
|
-
if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
|
|
897
|
+
if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
|
|
810
898
|
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
811
899
|
if (process.env.TEMP_RUN === "true") {
|
|
812
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
813
900
|
if (existsSync(routesPath)) {
|
|
814
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
815
901
|
routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
816
902
|
rmSync(routesPath, { recursive: true });
|
|
817
903
|
}
|
|
818
904
|
mkdirSync(routesPath, { recursive: true });
|
|
819
|
-
|
|
820
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
905
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
821
906
|
} else {
|
|
822
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
823
907
|
if (existsSync(routesPath)) {
|
|
824
|
-
// remove the folder
|
|
825
908
|
try {
|
|
826
909
|
rmSync(routesPath, { recursive: true });
|
|
827
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
828
910
|
} catch (error) {
|
|
829
|
-
|
|
911
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
830
912
|
}
|
|
831
913
|
}
|
|
832
914
|
routesPath = path.join(projectDir, "data", "routes");
|
|
833
|
-
console.log("Saving routes to:", routesPath);
|
|
834
915
|
if (!existsSync(routesPath)) {
|
|
835
916
|
mkdirSync(routesPath, { recursive: true });
|
|
836
917
|
}
|
|
837
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
918
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
919
|
+
}
|
|
920
|
+
if (step.commands && Array.isArray(step.commands)) {
|
|
921
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
|
|
838
922
|
}
|
|
839
923
|
continue;
|
|
840
924
|
}
|
|
841
925
|
const cucumberStep = getCucumberStep({ step });
|
|
842
926
|
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
843
927
|
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
844
|
-
// path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
845
928
|
let codePage = getCodePage(stepDefsFilePath);
|
|
846
929
|
codePage = await saveRecording({
|
|
847
930
|
step,
|
|
@@ -862,7 +945,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
862
945
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
863
946
|
}
|
|
864
947
|
|
|
865
|
-
export function saveRoutes({ step, folderPath }) {
|
|
948
|
+
export function saveRoutes({ step, folderPath }, logger) {
|
|
866
949
|
const routeItems = step.routeItems;
|
|
867
950
|
if (!routeItems || routeItems.length === 0) {
|
|
868
951
|
return;
|
|
@@ -885,21 +968,18 @@ export function saveRoutes({ step, folderPath }) {
|
|
|
885
968
|
};
|
|
886
969
|
});
|
|
887
970
|
|
|
888
|
-
const routesFilePath = path.join(folderPath, stepNameHash
|
|
889
|
-
console.log("Routes file path:", routesFilePath);
|
|
971
|
+
const routesFilePath = path.join(folderPath, `${stepNameHash}.json`);
|
|
890
972
|
const routesData = {
|
|
891
973
|
template,
|
|
892
974
|
routes: routeItemsWithFilters,
|
|
893
975
|
};
|
|
894
|
-
console.log("Routes data to save:", routesData);
|
|
895
976
|
|
|
896
977
|
if (!existsSync(folderPath)) {
|
|
897
978
|
mkdirSync(folderPath, { recursive: true });
|
|
898
979
|
}
|
|
899
980
|
try {
|
|
900
981
|
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
|
|
901
|
-
console.log("Saved routes to", routesFilePath);
|
|
902
982
|
} catch (error) {
|
|
903
|
-
|
|
983
|
+
logger.error(`Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
|
|
904
984
|
}
|
|
905
985
|
}
|