@dev-blinq/cucumber_client 1.0.1467-dev → 1.0.1467-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 +127 -2
- 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 +95 -10
- package/bin/client/code_gen/playwright_codeget.js +173 -77
- package/bin/client/cucumber/feature.js +4 -17
- package/bin/client/cucumber/steps_definitions.js +13 -0
- package/bin/client/recorderv3/bvt_init.js +310 -0
- package/bin/client/recorderv3/bvt_recorder.js +1560 -1183
- 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 +838 -142
- package/bin/client/recorderv3/step_runner.js +36 -7
- package/bin/client/recorderv3/step_utils.js +171 -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/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 +4 -1
- package/package.json +12 -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,8 +331,11 @@ export class BVTStepRunner {
|
|
|
306
331
|
});
|
|
307
332
|
}
|
|
308
333
|
|
|
309
|
-
if (
|
|
310
|
-
|
|
334
|
+
if (
|
|
335
|
+
step.isUtilStep ||
|
|
336
|
+
((!(step.isImplemented && !step.shouldOverride) || step.shouldMakeStepTextUnique) && step.commands.length > 0)
|
|
337
|
+
) {
|
|
338
|
+
const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
|
|
311
339
|
const stepDefinitionFolderPath = path.join(tempFolderPath, "step_definitions");
|
|
312
340
|
if (!existsSync(stepDefinitionFolderPath)) {
|
|
313
341
|
mkdirSync(stepDefinitionFolderPath, { recursive: true });
|
|
@@ -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,49 +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);
|
|
372
389
|
const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
|
|
373
390
|
step.locators = locatorsObject;
|
|
374
391
|
step.allStrategyLocators = allStrategyLocators;
|
|
375
392
|
step.isLocatorsAssigned = true;
|
|
376
393
|
|
|
377
|
-
if (!isValueVariable && !
|
|
394
|
+
if (!isValueVariable && !isTargetValueVariable) {
|
|
378
395
|
return step;
|
|
379
396
|
}
|
|
380
397
|
|
|
@@ -389,7 +406,7 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
389
406
|
cmd,
|
|
390
407
|
locs,
|
|
391
408
|
isValueVariable,
|
|
392
|
-
|
|
409
|
+
isTargetValueVariable,
|
|
393
410
|
parametersMap,
|
|
394
411
|
});
|
|
395
412
|
locatorsObject.locators = locs;
|
|
@@ -408,7 +425,7 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
408
425
|
cmd,
|
|
409
426
|
locs: locators,
|
|
410
427
|
isValueVariable,
|
|
411
|
-
|
|
428
|
+
isTargetValueVariable,
|
|
412
429
|
parametersMap,
|
|
413
430
|
});
|
|
414
431
|
}
|
|
@@ -445,7 +462,7 @@ export function getCodePage(stepDefsFilePath) {
|
|
|
445
462
|
export function getCucumberStep({ step }) {
|
|
446
463
|
const cucumberStep = new Step();
|
|
447
464
|
cucumberStep.loadFromJson({
|
|
448
|
-
text: step.text,
|
|
465
|
+
text: step.renamedText ? step.renamedText : step.text,
|
|
449
466
|
keyword: step.keyword,
|
|
450
467
|
keywordType: step.keywordType,
|
|
451
468
|
parameters: [],
|
|
@@ -463,12 +480,16 @@ export function getCucumberStep({ step }) {
|
|
|
463
480
|
|
|
464
481
|
function makeStepTextUnique(step, stepsDefinitions) {
|
|
465
482
|
// const utilsFilePath = path.join("features", "step_definitions", "utils.mjs");
|
|
466
|
-
let stepText = step.text;
|
|
483
|
+
let stepText = step.renamedText ? step.renamedText : step.text;
|
|
467
484
|
let stepIndex = 1;
|
|
468
485
|
// console.log("makeStepTextUnique", step.text);
|
|
469
486
|
let stepDef = stepsDefinitions.findMatchingStep(stepText);
|
|
470
487
|
// console.log({ stepDef });
|
|
471
|
-
if (
|
|
488
|
+
if (
|
|
489
|
+
stepDef &&
|
|
490
|
+
(stepDef?.file.endsWith("utils.mjs") || stepDef?.file.endsWith("renamed_util.mjs")) &&
|
|
491
|
+
!step.shouldMakeStepTextUnique
|
|
492
|
+
) {
|
|
472
493
|
return true;
|
|
473
494
|
}
|
|
474
495
|
while (stepDef) {
|
|
@@ -479,7 +500,18 @@ function makeStepTextUnique(step, stepsDefinitions) {
|
|
|
479
500
|
step.text = stepText;
|
|
480
501
|
}
|
|
481
502
|
|
|
482
|
-
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
|
+
}
|
|
483
515
|
let routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
484
516
|
|
|
485
517
|
if (process.env.TEMP_RUN === "true") {
|
|
@@ -487,21 +519,19 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
487
519
|
rmSync(routesPath, { recursive: true });
|
|
488
520
|
}
|
|
489
521
|
mkdirSync(routesPath, { recursive: true });
|
|
490
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
522
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
491
523
|
} else {
|
|
492
524
|
if (existsSync(routesPath)) {
|
|
493
|
-
// remove the folder
|
|
494
525
|
try {
|
|
495
526
|
rmSync(routesPath, { recursive: true });
|
|
496
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
497
527
|
} catch (error) {
|
|
498
|
-
|
|
528
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
499
529
|
}
|
|
500
530
|
routesPath = path.join(projectDir, "data", "routes");
|
|
501
531
|
if (!existsSync(routesPath)) {
|
|
502
532
|
mkdirSync(routesPath, { recursive: true });
|
|
503
533
|
}
|
|
504
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
534
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
505
535
|
}
|
|
506
536
|
}
|
|
507
537
|
|
|
@@ -509,49 +539,63 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
509
539
|
return;
|
|
510
540
|
}
|
|
511
541
|
|
|
542
|
+
let isUtilStep = false;
|
|
543
|
+
let isChangedUtilStepName = false;
|
|
544
|
+
|
|
512
545
|
if (step.isImplemented && step.shouldOverride) {
|
|
513
|
-
|
|
546
|
+
const stepDef = stepsDefinitions.findMatchingStep(step.text);
|
|
514
547
|
codePage = getCodePage(stepDef.file);
|
|
515
548
|
} else {
|
|
516
|
-
|
|
549
|
+
isUtilStep = makeStepTextUnique(step, stepsDefinitions);
|
|
517
550
|
|
|
518
551
|
if (isUtilStep) {
|
|
519
|
-
|
|
552
|
+
isChangedUtilStepName =
|
|
553
|
+
step.renamedText && stepsDefinitions.findMatchingStep(step.renamedText)?.file.endsWith("renamed_util.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
|
+
}
|
|
520
569
|
}
|
|
521
570
|
}
|
|
522
571
|
|
|
572
|
+
const renamedUtil = step.renamedText && isUtilStep;
|
|
573
|
+
|
|
523
574
|
routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
524
575
|
if (process.env.TEMP_RUN === "true") {
|
|
525
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
526
576
|
if (existsSync(routesPath)) {
|
|
527
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
528
577
|
rmSync(routesPath, { recursive: true });
|
|
529
578
|
}
|
|
530
579
|
mkdirSync(routesPath, { recursive: true });
|
|
531
|
-
|
|
532
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
580
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
533
581
|
} else {
|
|
534
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
535
582
|
if (existsSync(routesPath)) {
|
|
536
|
-
// remove the folder
|
|
537
583
|
try {
|
|
538
584
|
rmSync(routesPath, { recursive: true });
|
|
539
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
540
585
|
} catch (error) {
|
|
541
|
-
|
|
586
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
542
587
|
}
|
|
543
588
|
}
|
|
544
589
|
routesPath = path.join(projectDir, "data", "routes");
|
|
545
|
-
console.log("Saving routes to:", routesPath);
|
|
546
590
|
if (!existsSync(routesPath)) {
|
|
547
591
|
mkdirSync(routesPath, { recursive: true });
|
|
548
592
|
}
|
|
549
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
593
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
550
594
|
}
|
|
551
595
|
|
|
552
596
|
cucumberStep.text = step.text;
|
|
553
597
|
const recording = new Recording();
|
|
554
|
-
|
|
598
|
+
|
|
555
599
|
const steps = step.commands;
|
|
556
600
|
|
|
557
601
|
recording.loadFromObject({ steps, step: cucumberStep });
|
|
@@ -602,7 +646,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
602
646
|
stepsDefinitions
|
|
603
647
|
);
|
|
604
648
|
|
|
605
|
-
if (!step.isImplemented) {
|
|
649
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
606
650
|
stepsDefinitions.addStep({
|
|
607
651
|
name: step.text,
|
|
608
652
|
file: result.codePage.sourceFileName,
|
|
@@ -614,13 +658,16 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
614
658
|
return result.codePage;
|
|
615
659
|
} else {
|
|
616
660
|
const generateCodeResult = generateCode(recording, codePage, userData, projectDir, methodName);
|
|
617
|
-
|
|
618
|
-
|
|
661
|
+
console.log("Generated code for step:", step.text);
|
|
662
|
+
if (!isUtilStep && generateCodeResult.noCode === true) {
|
|
619
663
|
return generateCodeResult.page;
|
|
620
664
|
}
|
|
621
665
|
codePage = generateCodeResult.page;
|
|
622
666
|
methodName = generateCodeResult.methodName;
|
|
623
|
-
|
|
667
|
+
|
|
668
|
+
if (!renamedUtil) {
|
|
669
|
+
codePage.insertElements(generateCodeResult.elements);
|
|
670
|
+
}
|
|
624
671
|
|
|
625
672
|
const description = cucumberStep.text;
|
|
626
673
|
let path = null;
|
|
@@ -633,15 +680,43 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
633
680
|
protect = true;
|
|
634
681
|
}
|
|
635
682
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
+
|
|
645
720
|
const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
|
|
646
721
|
const stepResult = codePage.addCucumberStep(
|
|
647
722
|
keyword,
|
|
@@ -651,7 +726,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
651
726
|
step.finalTimeout
|
|
652
727
|
);
|
|
653
728
|
|
|
654
|
-
if (!step.isImplemented) {
|
|
729
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
655
730
|
stepsDefinitions.addStep({
|
|
656
731
|
name: step.text,
|
|
657
732
|
file: codePage.sourceFileName,
|
|
@@ -704,7 +779,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
704
779
|
const file = step?.file;
|
|
705
780
|
const locatorsJson = getLocatorsJson(file);
|
|
706
781
|
if (!step) {
|
|
707
|
-
throw new Error(
|
|
782
|
+
throw new Error(`Step definition not found: ${stepName}`);
|
|
708
783
|
}
|
|
709
784
|
isImplemented = true;
|
|
710
785
|
const { codeCommands, codePage, elements, parametersNames, error } =
|
|
@@ -712,26 +787,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
712
787
|
if (error) {
|
|
713
788
|
throw new Error(error);
|
|
714
789
|
}
|
|
790
|
+
isUtilStep = codePage.sourceFileName.endsWith("utils.mjs") || codePage.sourceFileName.endsWith("renamed_util.mjs");
|
|
715
791
|
|
|
716
792
|
if (parametersNames.length !== stepParams.length) {
|
|
717
793
|
// console.log("Parameters mismatch", parametersNames, stepParams);
|
|
718
794
|
throw new Error("Parameters mismatch");
|
|
719
795
|
}
|
|
720
|
-
for (let i = 0; i < parametersNames.length; i++) {
|
|
721
|
-
stepParams[i].argumentName = parametersNames[i];
|
|
722
|
-
}
|
|
723
796
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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
|
+
}
|
|
732
816
|
}
|
|
817
|
+
commands.push(command);
|
|
733
818
|
}
|
|
734
|
-
commands.push(command);
|
|
735
819
|
}
|
|
736
820
|
} catch (error) {
|
|
737
821
|
console.error(error);
|
|
@@ -779,7 +863,7 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
|
|
|
779
863
|
}
|
|
780
864
|
}
|
|
781
865
|
|
|
782
|
-
export async function updateStepDefinitions({ scenario, featureName, projectDir }) {
|
|
866
|
+
export async function updateStepDefinitions({ scenario, featureName, projectDir, logger }) {
|
|
783
867
|
// set the candidate step definition file name
|
|
784
868
|
// set the utils file path
|
|
785
869
|
const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
|
|
@@ -810,42 +894,37 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
810
894
|
step.isImplementedWhileRecording = true;
|
|
811
895
|
}
|
|
812
896
|
}
|
|
813
|
-
if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
|
|
897
|
+
if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
|
|
814
898
|
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
815
899
|
if (process.env.TEMP_RUN === "true") {
|
|
816
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
817
900
|
if (existsSync(routesPath)) {
|
|
818
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
819
901
|
routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
820
902
|
rmSync(routesPath, { recursive: true });
|
|
821
903
|
}
|
|
822
904
|
mkdirSync(routesPath, { recursive: true });
|
|
823
|
-
|
|
824
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
905
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
825
906
|
} else {
|
|
826
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
827
907
|
if (existsSync(routesPath)) {
|
|
828
|
-
// remove the folder
|
|
829
908
|
try {
|
|
830
909
|
rmSync(routesPath, { recursive: true });
|
|
831
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
832
910
|
} catch (error) {
|
|
833
|
-
|
|
911
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
834
912
|
}
|
|
835
913
|
}
|
|
836
914
|
routesPath = path.join(projectDir, "data", "routes");
|
|
837
|
-
console.log("Saving routes to:", routesPath);
|
|
838
915
|
if (!existsSync(routesPath)) {
|
|
839
916
|
mkdirSync(routesPath, { recursive: true });
|
|
840
917
|
}
|
|
841
|
-
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));
|
|
842
922
|
}
|
|
843
923
|
continue;
|
|
844
924
|
}
|
|
845
925
|
const cucumberStep = getCucumberStep({ step });
|
|
846
|
-
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
926
|
+
const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
|
|
847
927
|
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
848
|
-
// path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
849
928
|
let codePage = getCodePage(stepDefsFilePath);
|
|
850
929
|
codePage = await saveRecording({
|
|
851
930
|
step,
|
|
@@ -866,7 +945,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
866
945
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
867
946
|
}
|
|
868
947
|
|
|
869
|
-
export function saveRoutes({ step, folderPath }) {
|
|
948
|
+
export function saveRoutes({ step, folderPath }, logger) {
|
|
870
949
|
const routeItems = step.routeItems;
|
|
871
950
|
if (!routeItems || routeItems.length === 0) {
|
|
872
951
|
return;
|
|
@@ -889,21 +968,18 @@ export function saveRoutes({ step, folderPath }) {
|
|
|
889
968
|
};
|
|
890
969
|
});
|
|
891
970
|
|
|
892
|
-
const routesFilePath = path.join(folderPath, stepNameHash
|
|
893
|
-
console.log("Routes file path:", routesFilePath);
|
|
971
|
+
const routesFilePath = path.join(folderPath, `${stepNameHash}.json`);
|
|
894
972
|
const routesData = {
|
|
895
973
|
template,
|
|
896
974
|
routes: routeItemsWithFilters,
|
|
897
975
|
};
|
|
898
|
-
console.log("Routes data to save:", routesData);
|
|
899
976
|
|
|
900
977
|
if (!existsSync(folderPath)) {
|
|
901
978
|
mkdirSync(folderPath, { recursive: true });
|
|
902
979
|
}
|
|
903
980
|
try {
|
|
904
981
|
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
|
|
905
|
-
console.log("Saved routes to", routesFilePath);
|
|
906
982
|
} catch (error) {
|
|
907
|
-
|
|
983
|
+
logger.error(`Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
|
|
908
984
|
}
|
|
909
985
|
}
|