@dev-blinq/cucumber_client 1.0.1475-dev → 1.0.1475-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 +49 -49
- package/bin/assets/scripts/recorder.js +87 -34
- package/bin/assets/scripts/snapshot_capturer.js +10 -17
- package/bin/assets/scripts/unique_locators.js +78 -28
- package/bin/assets/templates/_hooks_template.txt +6 -2
- package/bin/assets/templates/utils_template.txt +16 -16
- package/bin/client/code_cleanup/codemod/find_harcoded_locators.js +173 -0
- package/bin/client/code_cleanup/codemod/fix_hardcoded_locators.js +247 -0
- package/bin/client/code_cleanup/utils.js +16 -7
- package/bin/client/code_gen/code_inversion.js +125 -1
- package/bin/client/code_gen/duplication_analysis.js +2 -1
- package/bin/client/code_gen/function_signature.js +8 -0
- package/bin/client/code_gen/index.js +4 -0
- package/bin/client/code_gen/page_reflection.js +90 -9
- package/bin/client/code_gen/playwright_codeget.js +173 -77
- package/bin/client/codemod/find_harcoded_locators.js +173 -0
- package/bin/client/codemod/fix_hardcoded_locators.js +247 -0
- package/bin/client/codemod/index.js +8 -0
- package/bin/client/codemod/locators_array/find_misstructured_elements.js +148 -0
- package/bin/client/codemod/locators_array/fix_misstructured_elements.js +144 -0
- package/bin/client/codemod/locators_array/index.js +114 -0
- package/bin/client/codemod/types.js +1 -0
- package/bin/client/cucumber/feature.js +4 -17
- package/bin/client/cucumber/steps_definitions.js +17 -12
- package/bin/client/recorderv3/bvt_init.js +310 -0
- package/bin/client/recorderv3/bvt_recorder.js +1560 -1183
- package/bin/client/recorderv3/constants.js +45 -0
- package/bin/client/recorderv3/implemented_steps.js +2 -0
- package/bin/client/recorderv3/index.js +3 -293
- package/bin/client/recorderv3/mixpanel.js +39 -0
- package/bin/client/recorderv3/services.js +839 -142
- package/bin/client/recorderv3/step_runner.js +36 -7
- package/bin/client/recorderv3/step_utils.js +316 -98
- package/bin/client/recorderv3/update_feature.js +85 -37
- package/bin/client/recorderv3/utils.js +80 -0
- package/bin/client/recorderv3/wbr_entry.js +61 -0
- package/bin/client/recording.js +1 -0
- package/bin/client/types/locators.js +2 -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 +100 -125
- package/bin/index.js +5 -0
- package/package.json +21 -6
- 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
|
@@ -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,11 @@ 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
|
+
import { FIXED_FOLDER_NAMES, SaveJobErrorType, UpdateStepDefinitionsError, UTF8_ENCODING } from "./constants.js";
|
|
15
|
+
import { handleFileInitOps, potentialErrorWrapper } from "./utils.js";
|
|
14
16
|
|
|
15
17
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
16
18
|
|
|
@@ -48,7 +50,7 @@ const replaceLastOccurence = (str, search, replacement) => {
|
|
|
48
50
|
return str.substring(0, lastIndex) + replacement + str.substring(lastIndex + search.length);
|
|
49
51
|
};
|
|
50
52
|
|
|
51
|
-
const _toRecordingStep = (cmd) => {
|
|
53
|
+
export const _toRecordingStep = (cmd, stepText) => {
|
|
52
54
|
switch (cmd.type) {
|
|
53
55
|
case "hover_element": {
|
|
54
56
|
return {
|
|
@@ -250,6 +252,7 @@ const _toRecordingStep = (cmd) => {
|
|
|
250
252
|
},
|
|
251
253
|
parameters: [cmd.selectedField, cmd.value],
|
|
252
254
|
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
255
|
+
valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
|
|
253
256
|
};
|
|
254
257
|
}
|
|
255
258
|
case "conditional_wait": {
|
|
@@ -261,6 +264,7 @@ const _toRecordingStep = (cmd) => {
|
|
|
261
264
|
},
|
|
262
265
|
parameters: [cmd.timeout, cmd.selectedField, cmd.value],
|
|
263
266
|
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
267
|
+
valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
|
|
264
268
|
};
|
|
265
269
|
}
|
|
266
270
|
case "navigate": {
|
|
@@ -296,6 +300,8 @@ const _toRecordingStep = (cmd) => {
|
|
|
296
300
|
type: "verify_page_snapshot",
|
|
297
301
|
parameters: [cmd.value],
|
|
298
302
|
selectors: cmd.selectors,
|
|
303
|
+
data: cmd.data,
|
|
304
|
+
valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
|
|
299
305
|
};
|
|
300
306
|
}
|
|
301
307
|
default: {
|
|
@@ -332,49 +338,62 @@ const _parameterizeLocators = (locators, replacementFromValue, replacementToValu
|
|
|
332
338
|
}
|
|
333
339
|
return locators;
|
|
334
340
|
};
|
|
335
|
-
const parameterizeLocators = ({ cmd, locs, isValueVariable,
|
|
341
|
+
const parameterizeLocators = ({ cmd, locs, isValueVariable, isTargetValueVariable, parametersMap }) => {
|
|
336
342
|
if (isValueVariable) {
|
|
337
343
|
const variable = cmd.value.slice(1, -1);
|
|
338
|
-
const val = parametersMap[variable];
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
344
|
+
// const val = parametersMap[variable];
|
|
345
|
+
if (typeof cmd.text === "string") {
|
|
346
|
+
const replacementFromValue = cmd.text.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
|
|
347
|
+
if (replacementFromValue.length > 0) {
|
|
348
|
+
const replacementToValue = `{${variable}}`;
|
|
349
|
+
locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (isTargetValueVariable) {
|
|
354
|
+
const variable = cmd.targetValue.slice(1, -1);
|
|
355
|
+
// const val = parametersMap[variable];
|
|
356
|
+
if (typeof cmd.targetText === "string") {
|
|
357
|
+
const replacementFromValue = cmd.targetText.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
|
|
358
|
+
if (replacementFromValue.length > 0) {
|
|
359
|
+
const replacementToValue = `{${variable}}`;
|
|
360
|
+
locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
349
363
|
}
|
|
350
364
|
return locs;
|
|
351
365
|
};
|
|
352
366
|
|
|
353
367
|
//TODO: IMPORTAN
|
|
354
|
-
export const toRecordingStep = (cmd, parametersMap) => {
|
|
368
|
+
export const toRecordingStep = (cmd, parametersMap, stepText) => {
|
|
355
369
|
if (cmd.type === "api") {
|
|
356
370
|
return {
|
|
357
371
|
type: "api",
|
|
358
372
|
value: cmd.value,
|
|
359
373
|
};
|
|
360
374
|
}
|
|
361
|
-
const step = _toRecordingStep(cmd);
|
|
375
|
+
const step = _toRecordingStep(cmd, stepText);
|
|
362
376
|
const cmdID = {
|
|
363
377
|
cmdId: cmd.id,
|
|
378
|
+
options: cmd.options ?? null,
|
|
364
379
|
};
|
|
365
380
|
Object.assign(step, cmdID);
|
|
366
381
|
|
|
367
382
|
const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
|
|
368
383
|
|
|
369
384
|
if (!locatorsObject) return step;
|
|
385
|
+
|
|
386
|
+
const element_name = cmd?.locators?.element_name ?? `${cmd.label} ${cmd.role ?? "Text"}`;
|
|
387
|
+
locatorsObject.element_name = element_name;
|
|
388
|
+
|
|
370
389
|
const isValueVariable = isVariable(cmd.value);
|
|
371
|
-
const
|
|
390
|
+
const isTargetValueVariable = isVariable(cmd.targetValue);
|
|
372
391
|
const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
|
|
373
392
|
step.locators = locatorsObject;
|
|
374
393
|
step.allStrategyLocators = allStrategyLocators;
|
|
375
394
|
step.isLocatorsAssigned = true;
|
|
376
395
|
|
|
377
|
-
if (!isValueVariable && !
|
|
396
|
+
if (!isValueVariable && !isTargetValueVariable) {
|
|
378
397
|
return step;
|
|
379
398
|
}
|
|
380
399
|
|
|
@@ -389,7 +408,7 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
389
408
|
cmd,
|
|
390
409
|
locs,
|
|
391
410
|
isValueVariable,
|
|
392
|
-
|
|
411
|
+
isTargetValueVariable,
|
|
393
412
|
parametersMap,
|
|
394
413
|
});
|
|
395
414
|
locatorsObject.locators = locs;
|
|
@@ -408,7 +427,7 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
408
427
|
cmd,
|
|
409
428
|
locs: locators,
|
|
410
429
|
isValueVariable,
|
|
411
|
-
|
|
430
|
+
isTargetValueVariable,
|
|
412
431
|
parametersMap,
|
|
413
432
|
});
|
|
414
433
|
}
|
|
@@ -437,7 +456,7 @@ export function getCodePage(stepDefsFilePath) {
|
|
|
437
456
|
return codePage;
|
|
438
457
|
}
|
|
439
458
|
// const templateFile = path.join(__dirname, "../../assets", "templates", "page_template.txt");
|
|
440
|
-
// const initialCode = readFileSync(templateFile,
|
|
459
|
+
// const initialCode = readFileSync(templateFile, UTF8_ENCODING);
|
|
441
460
|
const codePage = new CodePage();
|
|
442
461
|
codePage.generateModel();
|
|
443
462
|
return codePage;
|
|
@@ -445,7 +464,7 @@ export function getCodePage(stepDefsFilePath) {
|
|
|
445
464
|
export function getCucumberStep({ step }) {
|
|
446
465
|
const cucumberStep = new Step();
|
|
447
466
|
cucumberStep.loadFromJson({
|
|
448
|
-
text: step.text,
|
|
467
|
+
text: step.renamedText ? step.renamedText : step.text,
|
|
449
468
|
keyword: step.keyword,
|
|
450
469
|
keywordType: step.keywordType,
|
|
451
470
|
parameters: [],
|
|
@@ -463,12 +482,16 @@ export function getCucumberStep({ step }) {
|
|
|
463
482
|
|
|
464
483
|
function makeStepTextUnique(step, stepsDefinitions) {
|
|
465
484
|
// const utilsFilePath = path.join("features", "step_definitions", "utils.mjs");
|
|
466
|
-
let stepText = step.text;
|
|
485
|
+
let stepText = step.renamedText ? step.renamedText : step.text;
|
|
467
486
|
let stepIndex = 1;
|
|
468
487
|
// console.log("makeStepTextUnique", step.text);
|
|
469
488
|
let stepDef = stepsDefinitions.findMatchingStep(stepText);
|
|
470
489
|
// console.log({ stepDef });
|
|
471
|
-
if (
|
|
490
|
+
if (
|
|
491
|
+
stepDef &&
|
|
492
|
+
(stepDef?.file.endsWith("utils.mjs") || stepDef?.file.endsWith("renamed_util.mjs")) &&
|
|
493
|
+
!step.shouldMakeStepTextUnique
|
|
494
|
+
) {
|
|
472
495
|
return true;
|
|
473
496
|
}
|
|
474
497
|
while (stepDef) {
|
|
@@ -479,9 +502,18 @@ function makeStepTextUnique(step, stepsDefinitions) {
|
|
|
479
502
|
step.text = stepText;
|
|
480
503
|
}
|
|
481
504
|
|
|
482
|
-
export async function saveRecording({
|
|
505
|
+
export async function saveRecording({
|
|
506
|
+
step,
|
|
507
|
+
cucumberStep,
|
|
508
|
+
codePage,
|
|
509
|
+
projectDir,
|
|
510
|
+
stepsDefinitions,
|
|
511
|
+
parametersMap,
|
|
512
|
+
logger,
|
|
513
|
+
bvtSnapshotsDir,
|
|
514
|
+
}) {
|
|
483
515
|
if (step.commands && Array.isArray(step.commands)) {
|
|
484
|
-
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
|
|
516
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap, step.text));
|
|
485
517
|
}
|
|
486
518
|
let routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
487
519
|
|
|
@@ -490,21 +522,19 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
490
522
|
rmSync(routesPath, { recursive: true });
|
|
491
523
|
}
|
|
492
524
|
mkdirSync(routesPath, { recursive: true });
|
|
493
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
525
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
494
526
|
} else {
|
|
495
527
|
if (existsSync(routesPath)) {
|
|
496
|
-
// remove the folder
|
|
497
528
|
try {
|
|
498
529
|
rmSync(routesPath, { recursive: true });
|
|
499
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
500
530
|
} catch (error) {
|
|
501
|
-
|
|
531
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
502
532
|
}
|
|
503
533
|
routesPath = path.join(projectDir, "data", "routes");
|
|
504
534
|
if (!existsSync(routesPath)) {
|
|
505
535
|
mkdirSync(routesPath, { recursive: true });
|
|
506
536
|
}
|
|
507
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
537
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
508
538
|
}
|
|
509
539
|
}
|
|
510
540
|
|
|
@@ -512,44 +542,58 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
512
542
|
return;
|
|
513
543
|
}
|
|
514
544
|
|
|
545
|
+
let isUtilStep = false;
|
|
546
|
+
let isChangedUtilStepName = false;
|
|
547
|
+
|
|
515
548
|
if (step.isImplemented && step.shouldOverride) {
|
|
516
|
-
|
|
549
|
+
const stepDef = stepsDefinitions.findMatchingStep(step.text);
|
|
517
550
|
codePage = getCodePage(stepDef.file);
|
|
518
551
|
} else {
|
|
519
|
-
|
|
552
|
+
isUtilStep = makeStepTextUnique(step, stepsDefinitions);
|
|
520
553
|
|
|
521
554
|
if (isUtilStep) {
|
|
522
|
-
|
|
555
|
+
isChangedUtilStepName =
|
|
556
|
+
step.renamedText && stepsDefinitions.findMatchingStep(step.renamedText)?.file.endsWith("renamed_util.mjs");
|
|
557
|
+
|
|
558
|
+
if (!isChangedUtilStepName) {
|
|
559
|
+
const isUtilStep = stepsDefinitions.findMatchingStep(step.text)?.file.endsWith("utils.mjs");
|
|
560
|
+
if (!step.renamedText || step.renamedText === step.text || isUtilStep) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
step.text = step.text.trim();
|
|
564
|
+
const { functionName } = stepsDefinitions.findMatchingStep(step.renamedText);
|
|
565
|
+
step.renamedText = functionName;
|
|
566
|
+
const newImportLine = `import { ${functionName} } from "./utils.mjs";\n`;
|
|
567
|
+
|
|
568
|
+
if (!codePage.fileContent.includes(newImportLine)) {
|
|
569
|
+
codePage.fileContent = newImportLine + codePage.fileContent;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
523
572
|
}
|
|
524
573
|
}
|
|
525
574
|
|
|
575
|
+
const renamedUtil = step.renamedText && isUtilStep;
|
|
576
|
+
|
|
526
577
|
routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
527
578
|
if (process.env.TEMP_RUN === "true") {
|
|
528
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
529
579
|
if (existsSync(routesPath)) {
|
|
530
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
531
580
|
rmSync(routesPath, { recursive: true });
|
|
532
581
|
}
|
|
533
582
|
mkdirSync(routesPath, { recursive: true });
|
|
534
|
-
|
|
535
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
583
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
536
584
|
} else {
|
|
537
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
538
585
|
if (existsSync(routesPath)) {
|
|
539
|
-
// remove the folder
|
|
540
586
|
try {
|
|
541
587
|
rmSync(routesPath, { recursive: true });
|
|
542
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
543
588
|
} catch (error) {
|
|
544
|
-
|
|
589
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
545
590
|
}
|
|
546
591
|
}
|
|
547
592
|
routesPath = path.join(projectDir, "data", "routes");
|
|
548
|
-
console.log("Saving routes to:", routesPath);
|
|
549
593
|
if (!existsSync(routesPath)) {
|
|
550
594
|
mkdirSync(routesPath, { recursive: true });
|
|
551
595
|
}
|
|
552
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
596
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
553
597
|
}
|
|
554
598
|
|
|
555
599
|
cucumberStep.text = step.text;
|
|
@@ -605,7 +649,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
605
649
|
stepsDefinitions
|
|
606
650
|
);
|
|
607
651
|
|
|
608
|
-
if (!step.isImplemented) {
|
|
652
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
609
653
|
stepsDefinitions.addStep({
|
|
610
654
|
name: step.text,
|
|
611
655
|
file: result.codePage.sourceFileName,
|
|
@@ -617,13 +661,16 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
617
661
|
return result.codePage;
|
|
618
662
|
} else {
|
|
619
663
|
const generateCodeResult = generateCode(recording, codePage, userData, projectDir, methodName);
|
|
620
|
-
|
|
621
|
-
|
|
664
|
+
console.log("Generated code for step:", step.text);
|
|
665
|
+
if (!isUtilStep && generateCodeResult.noCode === true) {
|
|
622
666
|
return generateCodeResult.page;
|
|
623
667
|
}
|
|
624
668
|
codePage = generateCodeResult.page;
|
|
625
669
|
methodName = generateCodeResult.methodName;
|
|
626
|
-
|
|
670
|
+
|
|
671
|
+
if (!renamedUtil) {
|
|
672
|
+
codePage.insertElements(generateCodeResult.elements);
|
|
673
|
+
}
|
|
627
674
|
|
|
628
675
|
const description = cucumberStep.text;
|
|
629
676
|
let path = null;
|
|
@@ -636,15 +683,43 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
636
683
|
protect = true;
|
|
637
684
|
}
|
|
638
685
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
686
|
+
|
|
687
|
+
if (renamedUtil) {
|
|
688
|
+
if (isChangedUtilStepName) {
|
|
689
|
+
const newFileContent = codePage.fileContent
|
|
690
|
+
.replace(`async function ${step.renamedText}`, `async function ${methodName}`)
|
|
691
|
+
.replace(`Then("${step.renamedText}"`, `// Then("${step.renamedText}"`)
|
|
692
|
+
.replace(`When("${step.renamedText}"`, `// When("${step.renamedText}"`)
|
|
693
|
+
.replace(`Given("${step.renamedText}"`, `// Given("${step.renamedText}"`);
|
|
694
|
+
|
|
695
|
+
codePage._init();
|
|
696
|
+
codePage.generateModel(newFileContent);
|
|
697
|
+
} else {
|
|
698
|
+
codePage.addInfraCommandUtil(
|
|
699
|
+
methodName,
|
|
700
|
+
description,
|
|
701
|
+
cucumberStep.parameters,
|
|
702
|
+
generateCodeResult.codeLines,
|
|
703
|
+
step.renamedText,
|
|
704
|
+
step.text,
|
|
705
|
+
parametersMap,
|
|
706
|
+
protect,
|
|
707
|
+
"recorder",
|
|
708
|
+
path
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
} else {
|
|
712
|
+
codePage.addInfraCommand(
|
|
713
|
+
methodName,
|
|
714
|
+
description,
|
|
715
|
+
cucumberStep.getVariablesList(),
|
|
716
|
+
generateCodeResult.codeLines,
|
|
717
|
+
protect,
|
|
718
|
+
"recorder",
|
|
719
|
+
path
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
|
|
648
723
|
const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
|
|
649
724
|
const stepResult = codePage.addCucumberStep(
|
|
650
725
|
keyword,
|
|
@@ -654,7 +729,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
654
729
|
step.finalTimeout
|
|
655
730
|
);
|
|
656
731
|
|
|
657
|
-
if (!step.isImplemented) {
|
|
732
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
658
733
|
stepsDefinitions.addStep({
|
|
659
734
|
name: step.text,
|
|
660
735
|
file: codePage.sourceFileName,
|
|
@@ -689,7 +764,7 @@ const getLocatorsJson = (file) => {
|
|
|
689
764
|
return {};
|
|
690
765
|
}
|
|
691
766
|
try {
|
|
692
|
-
const locatorsJson = readFileSync(locatorsFilePath,
|
|
767
|
+
const locatorsJson = readFileSync(locatorsFilePath, UTF8_ENCODING);
|
|
693
768
|
return JSON.parse(locatorsJson);
|
|
694
769
|
} catch (error) {
|
|
695
770
|
console.error(error);
|
|
@@ -707,7 +782,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
707
782
|
const file = step?.file;
|
|
708
783
|
const locatorsJson = getLocatorsJson(file);
|
|
709
784
|
if (!step) {
|
|
710
|
-
throw new Error(
|
|
785
|
+
throw new Error(`Step definition not found: ${stepName}`);
|
|
711
786
|
}
|
|
712
787
|
isImplemented = true;
|
|
713
788
|
const { codeCommands, codePage, elements, parametersNames, error } =
|
|
@@ -715,26 +790,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
715
790
|
if (error) {
|
|
716
791
|
throw new Error(error);
|
|
717
792
|
}
|
|
793
|
+
isUtilStep = codePage.sourceFileName.endsWith("utils.mjs") || codePage.sourceFileName.endsWith("renamed_util.mjs");
|
|
718
794
|
|
|
719
795
|
if (parametersNames.length !== stepParams.length) {
|
|
720
796
|
// console.log("Parameters mismatch", parametersNames, stepParams);
|
|
721
797
|
throw new Error("Parameters mismatch");
|
|
722
798
|
}
|
|
723
|
-
for (let i = 0; i < parametersNames.length; i++) {
|
|
724
|
-
stepParams[i].argumentName = parametersNames[i];
|
|
725
|
-
}
|
|
726
799
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
800
|
+
const pattern = step.name;
|
|
801
|
+
if (isUtilStep && pattern === "Verify the file {string} exists") {
|
|
802
|
+
commands.push({
|
|
803
|
+
type: "verify_file_exists",
|
|
804
|
+
parameters: [stepParams[0].text],
|
|
805
|
+
});
|
|
806
|
+
} else {
|
|
807
|
+
for (let i = 0; i < parametersNames.length; i++) {
|
|
808
|
+
stepParams[i].argumentName = parametersNames[i];
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
for (const { code } of codeCommands) {
|
|
812
|
+
const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
|
|
813
|
+
if (command === undefined || command.type === null) continue;
|
|
814
|
+
if (command.element) {
|
|
815
|
+
const key = command.element.key;
|
|
816
|
+
if (key && locatorsJson[key]) {
|
|
817
|
+
command.allStrategyLocators = locatorsJson[key];
|
|
818
|
+
}
|
|
735
819
|
}
|
|
820
|
+
commands.push(command);
|
|
736
821
|
}
|
|
737
|
-
commands.push(command);
|
|
738
822
|
}
|
|
739
823
|
} catch (error) {
|
|
740
824
|
console.error(error);
|
|
@@ -782,7 +866,8 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
|
|
|
782
866
|
}
|
|
783
867
|
}
|
|
784
868
|
|
|
785
|
-
|
|
869
|
+
// deprecated
|
|
870
|
+
export async function updateStepDefinitionsOld({ scenario, featureName, projectDir, logger }) {
|
|
786
871
|
// set the candidate step definition file name
|
|
787
872
|
// set the utils file path
|
|
788
873
|
const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
|
|
@@ -813,42 +898,37 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
813
898
|
step.isImplementedWhileRecording = true;
|
|
814
899
|
}
|
|
815
900
|
}
|
|
816
|
-
if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
|
|
901
|
+
if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
|
|
817
902
|
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
818
903
|
if (process.env.TEMP_RUN === "true") {
|
|
819
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
820
904
|
if (existsSync(routesPath)) {
|
|
821
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
822
905
|
routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
823
906
|
rmSync(routesPath, { recursive: true });
|
|
824
907
|
}
|
|
825
908
|
mkdirSync(routesPath, { recursive: true });
|
|
826
|
-
|
|
827
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
909
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
828
910
|
} else {
|
|
829
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
830
911
|
if (existsSync(routesPath)) {
|
|
831
|
-
// remove the folder
|
|
832
912
|
try {
|
|
833
913
|
rmSync(routesPath, { recursive: true });
|
|
834
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
835
914
|
} catch (error) {
|
|
836
|
-
|
|
915
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
837
916
|
}
|
|
838
917
|
}
|
|
839
918
|
routesPath = path.join(projectDir, "data", "routes");
|
|
840
|
-
console.log("Saving routes to:", routesPath);
|
|
841
919
|
if (!existsSync(routesPath)) {
|
|
842
920
|
mkdirSync(routesPath, { recursive: true });
|
|
843
921
|
}
|
|
844
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
922
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
923
|
+
}
|
|
924
|
+
if (step.commands && Array.isArray(step.commands)) {
|
|
925
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
|
|
845
926
|
}
|
|
846
927
|
continue;
|
|
847
928
|
}
|
|
848
929
|
const cucumberStep = getCucumberStep({ step });
|
|
849
|
-
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
930
|
+
const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
|
|
850
931
|
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
851
|
-
// path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
852
932
|
let codePage = getCodePage(stepDefsFilePath);
|
|
853
933
|
codePage = await saveRecording({
|
|
854
934
|
step,
|
|
@@ -869,7 +949,148 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
869
949
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
870
950
|
}
|
|
871
951
|
|
|
872
|
-
export function
|
|
952
|
+
export async function updateStepDefinitions({ scenario, featureName, projectDir, logger, bvtSnapshotsDir }) {
|
|
953
|
+
try {
|
|
954
|
+
logger.info("🔵 Starting updateStepDefinitions()");
|
|
955
|
+
const { featureFolder, utilsFilePath, utilsContent } = handleFileInitOps(projectDir, logger);
|
|
956
|
+
const steps = scenario.steps;
|
|
957
|
+
|
|
958
|
+
const stepsDefinitions = new StepsDefinitions(projectDir);
|
|
959
|
+
await potentialErrorWrapper(
|
|
960
|
+
() => stepsDefinitions.load(false),
|
|
961
|
+
"Failed to load step definitions",
|
|
962
|
+
SaveJobErrorType.STEP_DEFINITIONS_LOADING_FAILED,
|
|
963
|
+
logger
|
|
964
|
+
);
|
|
965
|
+
|
|
966
|
+
for (let index = 0; index < steps.length; index++) {
|
|
967
|
+
const step = steps[index];
|
|
968
|
+
try {
|
|
969
|
+
if (step.internalImplementedStepId) {
|
|
970
|
+
const si = steps.findIndex((s) => s.id === step.internalImplementedStepId);
|
|
971
|
+
if (si !== -1) {
|
|
972
|
+
step.isImplementedWhileRecording = true;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const processed = await potentialErrorWrapper(
|
|
977
|
+
() => {
|
|
978
|
+
if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
|
|
979
|
+
let routesPath = path.join(tmpdir(), FIXED_FOLDER_NAMES.BLINQ_TEMP_ROUTES);
|
|
980
|
+
if (process.env.TEMP_RUN === "true") {
|
|
981
|
+
if (existsSync(routesPath)) rmSync(routesPath, { recursive: true });
|
|
982
|
+
mkdirSync(routesPath, { recursive: true });
|
|
983
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
984
|
+
} else {
|
|
985
|
+
if (existsSync(routesPath)) {
|
|
986
|
+
try {
|
|
987
|
+
rmSync(routesPath, { recursive: true });
|
|
988
|
+
} catch (error) {
|
|
989
|
+
logger.error(`❌ Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
routesPath = path.join(projectDir, FIXED_FOLDER_NAMES.DATA, FIXED_FOLDER_NAMES.ROUTES);
|
|
993
|
+
if (!existsSync(routesPath)) mkdirSync(routesPath, { recursive: true });
|
|
994
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (step.commands && Array.isArray(step.commands)) {
|
|
998
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
return true;
|
|
1002
|
+
} else {
|
|
1003
|
+
return false;
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
"Failed to process step commands and routes",
|
|
1007
|
+
SaveJobErrorType.STEP_COMMANDS_PROCESSING_FAILED,
|
|
1008
|
+
logger
|
|
1009
|
+
);
|
|
1010
|
+
if (processed) continue;
|
|
1011
|
+
|
|
1012
|
+
const { cucumberStep, stepDefsFilePath } = await potentialErrorWrapper(
|
|
1013
|
+
() => {
|
|
1014
|
+
const cucumberStep = getCucumberStep({ step });
|
|
1015
|
+
const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
|
|
1016
|
+
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
1017
|
+
return { cucumberStep, stepDefsFilePath };
|
|
1018
|
+
},
|
|
1019
|
+
"Failed to initialize cucumber step and locate definition path",
|
|
1020
|
+
SaveJobErrorType.STEP_INITIALIZATION_FAILED,
|
|
1021
|
+
logger
|
|
1022
|
+
);
|
|
1023
|
+
|
|
1024
|
+
const codePage = await potentialErrorWrapper(
|
|
1025
|
+
async () => {
|
|
1026
|
+
let codePage = getCodePage(stepDefsFilePath);
|
|
1027
|
+
codePage = await saveRecording({
|
|
1028
|
+
step,
|
|
1029
|
+
cucumberStep,
|
|
1030
|
+
codePage,
|
|
1031
|
+
projectDir,
|
|
1032
|
+
stepsDefinitions,
|
|
1033
|
+
parametersMap: scenario.parametersMap,
|
|
1034
|
+
bvtSnapshotsDir,
|
|
1035
|
+
});
|
|
1036
|
+
return codePage;
|
|
1037
|
+
},
|
|
1038
|
+
"Failed to generate and save step definition",
|
|
1039
|
+
SaveJobErrorType.STEP_DEFINITION_UPDATE_FAILED,
|
|
1040
|
+
logger
|
|
1041
|
+
);
|
|
1042
|
+
|
|
1043
|
+
if (!codePage) continue;
|
|
1044
|
+
|
|
1045
|
+
const res = await codePage.save();
|
|
1046
|
+
if (!res) {
|
|
1047
|
+
throw new UpdateStepDefinitionsError({
|
|
1048
|
+
type: SaveJobErrorType.STEP_DEFINITION_UPDATE_FAILED,
|
|
1049
|
+
message: `Failed to save step definition for "${cucumberStep.text}" in "${codePage.sourceFileName}"`,
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
throw error instanceof UpdateStepDefinitionsError
|
|
1054
|
+
? error
|
|
1055
|
+
: new UpdateStepDefinitionsError({
|
|
1056
|
+
type: SaveJobErrorType.STEP_DEFINITION_UPDATE_FAILED,
|
|
1057
|
+
message: "Failed to update step definition",
|
|
1058
|
+
meta: {
|
|
1059
|
+
stepIndex: index,
|
|
1060
|
+
stepText: step.text,
|
|
1061
|
+
reason: getErrorMessage(error),
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
writeFileSync(utilsFilePath, utilsContent, UTF8_ENCODING);
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
logger.error(
|
|
1070
|
+
"❌ updateStepDefinitions() failed" +
|
|
1071
|
+
JSON.stringify(
|
|
1072
|
+
{
|
|
1073
|
+
message: error?.message,
|
|
1074
|
+
meta: error?.meta,
|
|
1075
|
+
stack: error?.stack,
|
|
1076
|
+
},
|
|
1077
|
+
null,
|
|
1078
|
+
2
|
|
1079
|
+
) +
|
|
1080
|
+
"\nStringified error: ❌" +
|
|
1081
|
+
getErrorMessage(error)
|
|
1082
|
+
);
|
|
1083
|
+
throw error instanceof UpdateStepDefinitionsError
|
|
1084
|
+
? error
|
|
1085
|
+
: new UpdateStepDefinitionsError({
|
|
1086
|
+
type: SaveJobErrorType.INTERNAL_ERROR,
|
|
1087
|
+
message: error?.message ?? "Unknown error",
|
|
1088
|
+
meta: error.meta,
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
export function saveRoutes({ step, folderPath }, logger) {
|
|
873
1094
|
const routeItems = step.routeItems;
|
|
874
1095
|
if (!routeItems || routeItems.length === 0) {
|
|
875
1096
|
return;
|
|
@@ -892,21 +1113,18 @@ export function saveRoutes({ step, folderPath }) {
|
|
|
892
1113
|
};
|
|
893
1114
|
});
|
|
894
1115
|
|
|
895
|
-
const routesFilePath = path.join(folderPath, stepNameHash
|
|
896
|
-
console.log("Routes file path:", routesFilePath);
|
|
1116
|
+
const routesFilePath = path.join(folderPath, `${stepNameHash}.json`);
|
|
897
1117
|
const routesData = {
|
|
898
1118
|
template,
|
|
899
1119
|
routes: routeItemsWithFilters,
|
|
900
1120
|
};
|
|
901
|
-
console.log("Routes data to save:", routesData);
|
|
902
1121
|
|
|
903
1122
|
if (!existsSync(folderPath)) {
|
|
904
1123
|
mkdirSync(folderPath, { recursive: true });
|
|
905
1124
|
}
|
|
906
1125
|
try {
|
|
907
|
-
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2),
|
|
908
|
-
console.log("Saved routes to", routesFilePath);
|
|
1126
|
+
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), UTF8_ENCODING);
|
|
909
1127
|
} catch (error) {
|
|
910
|
-
|
|
1128
|
+
logger.error(`❌ Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
|
|
911
1129
|
}
|
|
912
1130
|
}
|