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