@dev-blinq/cucumber_client 1.0.1469-dev → 1.0.1469-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 +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 +168 -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 +5 -1
- package/package.json +13 -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,9 +500,17 @@ 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
|
+
}) {
|
|
483
512
|
if (step.commands && Array.isArray(step.commands)) {
|
|
484
|
-
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
|
|
513
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap, step.text));
|
|
485
514
|
}
|
|
486
515
|
let routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
487
516
|
|
|
@@ -490,21 +519,19 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
490
519
|
rmSync(routesPath, { recursive: true });
|
|
491
520
|
}
|
|
492
521
|
mkdirSync(routesPath, { recursive: true });
|
|
493
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
522
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
494
523
|
} else {
|
|
495
524
|
if (existsSync(routesPath)) {
|
|
496
|
-
// remove the folder
|
|
497
525
|
try {
|
|
498
526
|
rmSync(routesPath, { recursive: true });
|
|
499
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
500
527
|
} catch (error) {
|
|
501
|
-
|
|
528
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
502
529
|
}
|
|
503
530
|
routesPath = path.join(projectDir, "data", "routes");
|
|
504
531
|
if (!existsSync(routesPath)) {
|
|
505
532
|
mkdirSync(routesPath, { recursive: true });
|
|
506
533
|
}
|
|
507
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
534
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
508
535
|
}
|
|
509
536
|
}
|
|
510
537
|
|
|
@@ -512,44 +539,58 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
512
539
|
return;
|
|
513
540
|
}
|
|
514
541
|
|
|
542
|
+
let isUtilStep = false;
|
|
543
|
+
let isChangedUtilStepName = false;
|
|
544
|
+
|
|
515
545
|
if (step.isImplemented && step.shouldOverride) {
|
|
516
|
-
|
|
546
|
+
const stepDef = stepsDefinitions.findMatchingStep(step.text);
|
|
517
547
|
codePage = getCodePage(stepDef.file);
|
|
518
548
|
} else {
|
|
519
|
-
|
|
549
|
+
isUtilStep = makeStepTextUnique(step, stepsDefinitions);
|
|
520
550
|
|
|
521
551
|
if (isUtilStep) {
|
|
522
|
-
|
|
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
|
+
}
|
|
523
569
|
}
|
|
524
570
|
}
|
|
525
571
|
|
|
572
|
+
const renamedUtil = step.renamedText && isUtilStep;
|
|
573
|
+
|
|
526
574
|
routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
527
575
|
if (process.env.TEMP_RUN === "true") {
|
|
528
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
529
576
|
if (existsSync(routesPath)) {
|
|
530
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
531
577
|
rmSync(routesPath, { recursive: true });
|
|
532
578
|
}
|
|
533
579
|
mkdirSync(routesPath, { recursive: true });
|
|
534
|
-
|
|
535
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
580
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
536
581
|
} else {
|
|
537
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
538
582
|
if (existsSync(routesPath)) {
|
|
539
|
-
// remove the folder
|
|
540
583
|
try {
|
|
541
584
|
rmSync(routesPath, { recursive: true });
|
|
542
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
543
585
|
} catch (error) {
|
|
544
|
-
|
|
586
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
545
587
|
}
|
|
546
588
|
}
|
|
547
589
|
routesPath = path.join(projectDir, "data", "routes");
|
|
548
|
-
console.log("Saving routes to:", routesPath);
|
|
549
590
|
if (!existsSync(routesPath)) {
|
|
550
591
|
mkdirSync(routesPath, { recursive: true });
|
|
551
592
|
}
|
|
552
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
593
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
553
594
|
}
|
|
554
595
|
|
|
555
596
|
cucumberStep.text = step.text;
|
|
@@ -605,7 +646,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
605
646
|
stepsDefinitions
|
|
606
647
|
);
|
|
607
648
|
|
|
608
|
-
if (!step.isImplemented) {
|
|
649
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
609
650
|
stepsDefinitions.addStep({
|
|
610
651
|
name: step.text,
|
|
611
652
|
file: result.codePage.sourceFileName,
|
|
@@ -617,13 +658,16 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
617
658
|
return result.codePage;
|
|
618
659
|
} else {
|
|
619
660
|
const generateCodeResult = generateCode(recording, codePage, userData, projectDir, methodName);
|
|
620
|
-
|
|
621
|
-
|
|
661
|
+
console.log("Generated code for step:", step.text);
|
|
662
|
+
if (!isUtilStep && generateCodeResult.noCode === true) {
|
|
622
663
|
return generateCodeResult.page;
|
|
623
664
|
}
|
|
624
665
|
codePage = generateCodeResult.page;
|
|
625
666
|
methodName = generateCodeResult.methodName;
|
|
626
|
-
|
|
667
|
+
|
|
668
|
+
if (!renamedUtil) {
|
|
669
|
+
codePage.insertElements(generateCodeResult.elements);
|
|
670
|
+
}
|
|
627
671
|
|
|
628
672
|
const description = cucumberStep.text;
|
|
629
673
|
let path = null;
|
|
@@ -636,15 +680,43 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
636
680
|
protect = true;
|
|
637
681
|
}
|
|
638
682
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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
|
+
|
|
648
720
|
const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
|
|
649
721
|
const stepResult = codePage.addCucumberStep(
|
|
650
722
|
keyword,
|
|
@@ -654,7 +726,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
654
726
|
step.finalTimeout
|
|
655
727
|
);
|
|
656
728
|
|
|
657
|
-
if (!step.isImplemented) {
|
|
729
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
658
730
|
stepsDefinitions.addStep({
|
|
659
731
|
name: step.text,
|
|
660
732
|
file: codePage.sourceFileName,
|
|
@@ -707,7 +779,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
707
779
|
const file = step?.file;
|
|
708
780
|
const locatorsJson = getLocatorsJson(file);
|
|
709
781
|
if (!step) {
|
|
710
|
-
throw new Error(
|
|
782
|
+
throw new Error(`Step definition not found: ${stepName}`);
|
|
711
783
|
}
|
|
712
784
|
isImplemented = true;
|
|
713
785
|
const { codeCommands, codePage, elements, parametersNames, error } =
|
|
@@ -715,26 +787,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
715
787
|
if (error) {
|
|
716
788
|
throw new Error(error);
|
|
717
789
|
}
|
|
790
|
+
isUtilStep = codePage.sourceFileName.endsWith("utils.mjs") || codePage.sourceFileName.endsWith("renamed_util.mjs");
|
|
718
791
|
|
|
719
792
|
if (parametersNames.length !== stepParams.length) {
|
|
720
793
|
// console.log("Parameters mismatch", parametersNames, stepParams);
|
|
721
794
|
throw new Error("Parameters mismatch");
|
|
722
795
|
}
|
|
723
|
-
for (let i = 0; i < parametersNames.length; i++) {
|
|
724
|
-
stepParams[i].argumentName = parametersNames[i];
|
|
725
|
-
}
|
|
726
796
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
+
}
|
|
735
816
|
}
|
|
817
|
+
commands.push(command);
|
|
736
818
|
}
|
|
737
|
-
commands.push(command);
|
|
738
819
|
}
|
|
739
820
|
} catch (error) {
|
|
740
821
|
console.error(error);
|
|
@@ -782,7 +863,7 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
|
|
|
782
863
|
}
|
|
783
864
|
}
|
|
784
865
|
|
|
785
|
-
export async function updateStepDefinitions({ scenario, featureName, projectDir }) {
|
|
866
|
+
export async function updateStepDefinitions({ scenario, featureName, projectDir, logger }) {
|
|
786
867
|
// set the candidate step definition file name
|
|
787
868
|
// set the utils file path
|
|
788
869
|
const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
|
|
@@ -813,42 +894,37 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
813
894
|
step.isImplementedWhileRecording = true;
|
|
814
895
|
}
|
|
815
896
|
}
|
|
816
|
-
if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
|
|
897
|
+
if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
|
|
817
898
|
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
818
899
|
if (process.env.TEMP_RUN === "true") {
|
|
819
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
820
900
|
if (existsSync(routesPath)) {
|
|
821
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
822
901
|
routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
823
902
|
rmSync(routesPath, { recursive: true });
|
|
824
903
|
}
|
|
825
904
|
mkdirSync(routesPath, { recursive: true });
|
|
826
|
-
|
|
827
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
905
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
828
906
|
} else {
|
|
829
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
830
907
|
if (existsSync(routesPath)) {
|
|
831
|
-
// remove the folder
|
|
832
908
|
try {
|
|
833
909
|
rmSync(routesPath, { recursive: true });
|
|
834
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
835
910
|
} catch (error) {
|
|
836
|
-
|
|
911
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
837
912
|
}
|
|
838
913
|
}
|
|
839
914
|
routesPath = path.join(projectDir, "data", "routes");
|
|
840
|
-
console.log("Saving routes to:", routesPath);
|
|
841
915
|
if (!existsSync(routesPath)) {
|
|
842
916
|
mkdirSync(routesPath, { recursive: true });
|
|
843
917
|
}
|
|
844
|
-
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));
|
|
845
922
|
}
|
|
846
923
|
continue;
|
|
847
924
|
}
|
|
848
925
|
const cucumberStep = getCucumberStep({ step });
|
|
849
|
-
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
926
|
+
const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
|
|
850
927
|
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
851
|
-
// path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
852
928
|
let codePage = getCodePage(stepDefsFilePath);
|
|
853
929
|
codePage = await saveRecording({
|
|
854
930
|
step,
|
|
@@ -869,7 +945,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
869
945
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
870
946
|
}
|
|
871
947
|
|
|
872
|
-
export function saveRoutes({ step, folderPath }) {
|
|
948
|
+
export function saveRoutes({ step, folderPath }, logger) {
|
|
873
949
|
const routeItems = step.routeItems;
|
|
874
950
|
if (!routeItems || routeItems.length === 0) {
|
|
875
951
|
return;
|
|
@@ -892,21 +968,18 @@ export function saveRoutes({ step, folderPath }) {
|
|
|
892
968
|
};
|
|
893
969
|
});
|
|
894
970
|
|
|
895
|
-
const routesFilePath = path.join(folderPath, stepNameHash
|
|
896
|
-
console.log("Routes file path:", routesFilePath);
|
|
971
|
+
const routesFilePath = path.join(folderPath, `${stepNameHash}.json`);
|
|
897
972
|
const routesData = {
|
|
898
973
|
template,
|
|
899
974
|
routes: routeItemsWithFilters,
|
|
900
975
|
};
|
|
901
|
-
console.log("Routes data to save:", routesData);
|
|
902
976
|
|
|
903
977
|
if (!existsSync(folderPath)) {
|
|
904
978
|
mkdirSync(folderPath, { recursive: true });
|
|
905
979
|
}
|
|
906
980
|
try {
|
|
907
981
|
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
|
|
908
|
-
console.log("Saved routes to", routesFilePath);
|
|
909
982
|
} catch (error) {
|
|
910
|
-
|
|
983
|
+
logger.error(`Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
|
|
911
984
|
}
|
|
912
985
|
}
|