@dev-blinq/cucumber_client 1.0.1456-dev → 1.0.1456-stage
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/assets/bundled_scripts/recorder.js +73 -73
- package/bin/assets/scripts/recorder.js +87 -49
- package/bin/assets/scripts/snapshot_capturer.js +10 -17
- package/bin/assets/scripts/unique_locators.js +169 -47
- package/bin/assets/templates/_hooks_template.txt +6 -2
- package/bin/assets/templates/utils_template.txt +16 -16
- package/bin/client/code_cleanup/utils.js +16 -7
- package/bin/client/code_gen/code_inversion.js +115 -0
- package/bin/client/code_gen/duplication_analysis.js +2 -1
- package/bin/client/code_gen/function_signature.js +4 -0
- package/bin/client/code_gen/page_reflection.js +52 -11
- package/bin/client/code_gen/playwright_codeget.js +164 -75
- package/bin/client/cucumber/feature.js +4 -17
- package/bin/client/cucumber/steps_definitions.js +13 -0
- package/bin/client/local_agent.js +1 -0
- package/bin/client/recorderv3/bvt_init.js +305 -0
- package/bin/client/recorderv3/bvt_recorder.js +1031 -61
- package/bin/client/recorderv3/implemented_steps.js +2 -0
- package/bin/client/recorderv3/index.js +3 -286
- package/bin/client/recorderv3/services.js +818 -142
- package/bin/client/recorderv3/step_runner.js +21 -4
- package/bin/client/recorderv3/step_utils.js +194 -118
- package/bin/client/recorderv3/update_feature.js +87 -39
- package/bin/client/recorderv3/wbr_entry.js +61 -0
- package/bin/client/recording.js +1 -0
- package/bin/client/upload-service.js +2 -0
- package/bin/client/utils/app_dir.js +21 -0
- package/bin/client/utils/socket_logger.js +87 -125
- package/bin/index.js +4 -1
- package/package.json +11 -5
- package/bin/client/recorderv3/app_dir.js +0 -23
- package/bin/client/recorderv3/network.js +0 -299
- package/bin/client/recorderv3/scriptTest.js +0 -5
- package/bin/client/recorderv3/ws_server.js +0 -72
|
@@ -67,8 +67,7 @@ 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
|
}
|
|
@@ -284,6 +283,20 @@ export class BVTStepRunner {
|
|
|
284
283
|
return cId;
|
|
285
284
|
};
|
|
286
285
|
}
|
|
286
|
+
if (bvtContext.api) {
|
|
287
|
+
bvtContext.api.getCmdId = () => {
|
|
288
|
+
if (cmdIDs.length === 0) {
|
|
289
|
+
cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId ?? cmd.id);
|
|
290
|
+
}
|
|
291
|
+
const cId = cmdIDs.shift();
|
|
292
|
+
this.sendExecutionStatus({
|
|
293
|
+
type: "cmdExecutionStart",
|
|
294
|
+
cmdId: cId,
|
|
295
|
+
});
|
|
296
|
+
this.#lastAttemptedCmdId = cId;
|
|
297
|
+
return cId;
|
|
298
|
+
};
|
|
299
|
+
}
|
|
287
300
|
|
|
288
301
|
const __temp_features_FolderName = "__temp_features" + Math.random().toString(36).substring(2, 7);
|
|
289
302
|
const tempFolderPath = path.join(this.projectDir, __temp_features_FolderName);
|
|
@@ -306,7 +319,10 @@ export class BVTStepRunner {
|
|
|
306
319
|
});
|
|
307
320
|
}
|
|
308
321
|
|
|
309
|
-
if (
|
|
322
|
+
if (
|
|
323
|
+
step.isUtilStep ||
|
|
324
|
+
((!(step.isImplemented && !step.shouldOverride) || step.shouldMakeStepTextUnique) && step.commands.length > 0)
|
|
325
|
+
) {
|
|
310
326
|
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
311
327
|
const stepDefinitionFolderPath = path.join(tempFolderPath, "step_definitions");
|
|
312
328
|
if (!existsSync(stepDefinitionFolderPath)) {
|
|
@@ -320,7 +336,8 @@ export class BVTStepRunner {
|
|
|
320
336
|
codePage,
|
|
321
337
|
projectDir: this.projectDir,
|
|
322
338
|
stepsDefinitions,
|
|
323
|
-
parametersMap
|
|
339
|
+
parametersMap,
|
|
340
|
+
logger: socketLogger,
|
|
324
341
|
});
|
|
325
342
|
if (codePage) {
|
|
326
343
|
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
|
+
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: {
|
|
@@ -309,14 +313,7 @@ const _toRecordingStep = (cmd) => {
|
|
|
309
313
|
};
|
|
310
314
|
|
|
311
315
|
function getBestStrategy(allStrategyLocators) {
|
|
312
|
-
const orderedPriorities = [
|
|
313
|
-
"custom",
|
|
314
|
-
"context",
|
|
315
|
-
"basic",
|
|
316
|
-
"text_with_index",
|
|
317
|
-
"ignore_digit",
|
|
318
|
-
"no_text",
|
|
319
|
-
];
|
|
316
|
+
const orderedPriorities = ["custom", "context", "basic", "text_with_index", "ignore_digit", "no_text"];
|
|
320
317
|
for (const strategy of orderedPriorities) {
|
|
321
318
|
if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
|
|
322
319
|
return strategy;
|
|
@@ -325,7 +322,6 @@ function getBestStrategy(allStrategyLocators) {
|
|
|
325
322
|
return null;
|
|
326
323
|
}
|
|
327
324
|
|
|
328
|
-
|
|
329
325
|
const _parameterizeLocators = (locators, replacementFromValue, replacementToValue) => {
|
|
330
326
|
for (const loc of locators) {
|
|
331
327
|
if (loc?.css?.includes(replacementFromValue)) {
|
|
@@ -339,57 +335,70 @@ const _parameterizeLocators = (locators, replacementFromValue, replacementToValu
|
|
|
339
335
|
}
|
|
340
336
|
}
|
|
341
337
|
return locators;
|
|
342
|
-
}
|
|
343
|
-
const parameterizeLocators = ({
|
|
344
|
-
cmd, locs, isValueVariable, isTextVariable,
|
|
345
|
-
parametersMap
|
|
346
|
-
}) => {
|
|
338
|
+
};
|
|
339
|
+
const parameterizeLocators = ({ cmd, locs, isValueVariable, isTargetValueVariable, parametersMap }) => {
|
|
347
340
|
if (isValueVariable) {
|
|
348
341
|
const variable = cmd.value.slice(1, -1);
|
|
349
|
-
const val = parametersMap[variable];
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return locs;
|
|
363
|
+
};
|
|
363
364
|
|
|
364
365
|
//TODO: IMPORTAN
|
|
365
|
-
export const toRecordingStep = (cmd, parametersMap) => {
|
|
366
|
+
export const toRecordingStep = (cmd, parametersMap, stepText) => {
|
|
366
367
|
if (cmd.type === "api") {
|
|
367
368
|
return {
|
|
368
369
|
type: "api",
|
|
369
370
|
value: cmd.value,
|
|
370
371
|
};
|
|
371
372
|
}
|
|
372
|
-
const step = _toRecordingStep(cmd);
|
|
373
|
+
const step = _toRecordingStep(cmd, stepText);
|
|
373
374
|
const cmdID = {
|
|
374
375
|
cmdId: cmd.id,
|
|
376
|
+
options: cmd.options ?? null,
|
|
375
377
|
};
|
|
376
378
|
Object.assign(step, cmdID);
|
|
377
379
|
|
|
378
380
|
const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
|
|
379
381
|
|
|
380
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
|
+
|
|
381
387
|
const isValueVariable = isVariable(cmd.value);
|
|
382
|
-
const
|
|
388
|
+
const isTargetValueVariable = isVariable(cmd.targetValue);
|
|
389
|
+
const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
|
|
390
|
+
step.locators = locatorsObject;
|
|
391
|
+
step.allStrategyLocators = allStrategyLocators;
|
|
392
|
+
step.isLocatorsAssigned = true;
|
|
383
393
|
|
|
384
|
-
if (!isValueVariable && !
|
|
394
|
+
if (!isValueVariable && !isTargetValueVariable) {
|
|
385
395
|
return step;
|
|
386
396
|
}
|
|
387
397
|
|
|
388
398
|
if (isValueVariable) {
|
|
389
399
|
step.dataSource = "parameters";
|
|
390
|
-
step.dataKey = convertToIdentifier(cmd.value.slice(1, -1))
|
|
400
|
+
step.dataKey = convertToIdentifier(cmd.value.slice(1, -1));
|
|
391
401
|
}
|
|
392
|
-
const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
|
|
393
402
|
|
|
394
403
|
if (!allStrategyLocators) {
|
|
395
404
|
let locs = locatorsObject.locators;
|
|
@@ -397,13 +406,13 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
397
406
|
cmd,
|
|
398
407
|
locs,
|
|
399
408
|
isValueVariable,
|
|
400
|
-
|
|
401
|
-
parametersMap
|
|
409
|
+
isTargetValueVariable,
|
|
410
|
+
parametersMap,
|
|
402
411
|
});
|
|
403
412
|
locatorsObject.locators = locs;
|
|
404
413
|
return {
|
|
405
414
|
...step,
|
|
406
|
-
locators: locatorsObject
|
|
415
|
+
locators: locatorsObject,
|
|
407
416
|
};
|
|
408
417
|
}
|
|
409
418
|
|
|
@@ -416,8 +425,8 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
416
425
|
cmd,
|
|
417
426
|
locs: locators,
|
|
418
427
|
isValueVariable,
|
|
419
|
-
|
|
420
|
-
parametersMap
|
|
428
|
+
isTargetValueVariable,
|
|
429
|
+
parametersMap,
|
|
421
430
|
});
|
|
422
431
|
}
|
|
423
432
|
|
|
@@ -427,9 +436,9 @@ export const toRecordingStep = (cmd, parametersMap) => {
|
|
|
427
436
|
...step,
|
|
428
437
|
locators: locatorsObject,
|
|
429
438
|
allStrategyLocators,
|
|
430
|
-
isLocatorsAssigned: true
|
|
439
|
+
isLocatorsAssigned: true,
|
|
431
440
|
};
|
|
432
|
-
}
|
|
441
|
+
};
|
|
433
442
|
|
|
434
443
|
export const toMethodName = (str) => {
|
|
435
444
|
// Remove any non-word characters (excluding underscore) and trim spaces
|
|
@@ -453,7 +462,7 @@ export function getCodePage(stepDefsFilePath) {
|
|
|
453
462
|
export function getCucumberStep({ step }) {
|
|
454
463
|
const cucumberStep = new Step();
|
|
455
464
|
cucumberStep.loadFromJson({
|
|
456
|
-
text: step.text,
|
|
465
|
+
text: step.renamedText ? step.renamedText : step.text,
|
|
457
466
|
keyword: step.keyword,
|
|
458
467
|
keywordType: step.keywordType,
|
|
459
468
|
parameters: [],
|
|
@@ -471,12 +480,16 @@ export function getCucumberStep({ step }) {
|
|
|
471
480
|
|
|
472
481
|
function makeStepTextUnique(step, stepsDefinitions) {
|
|
473
482
|
// const utilsFilePath = path.join("features", "step_definitions", "utils.mjs");
|
|
474
|
-
let stepText = step.text;
|
|
483
|
+
let stepText = step.renamedText ? step.renamedText : step.text;
|
|
475
484
|
let stepIndex = 1;
|
|
476
485
|
// console.log("makeStepTextUnique", step.text);
|
|
477
486
|
let stepDef = stepsDefinitions.findMatchingStep(stepText);
|
|
478
487
|
// console.log({ stepDef });
|
|
479
|
-
if (
|
|
488
|
+
if (
|
|
489
|
+
stepDef &&
|
|
490
|
+
(stepDef?.file.endsWith("utils.mjs") || stepDef?.file.endsWith("default_page.mjs")) &&
|
|
491
|
+
!step.shouldMakeStepTextUnique
|
|
492
|
+
) {
|
|
480
493
|
return true;
|
|
481
494
|
}
|
|
482
495
|
while (stepDef) {
|
|
@@ -487,7 +500,18 @@ function makeStepTextUnique(step, stepsDefinitions) {
|
|
|
487
500
|
step.text = stepText;
|
|
488
501
|
}
|
|
489
502
|
|
|
490
|
-
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
|
+
}
|
|
491
515
|
let routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
492
516
|
|
|
493
517
|
if (process.env.TEMP_RUN === "true") {
|
|
@@ -495,21 +519,19 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
495
519
|
rmSync(routesPath, { recursive: true });
|
|
496
520
|
}
|
|
497
521
|
mkdirSync(routesPath, { recursive: true });
|
|
498
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
522
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
499
523
|
} else {
|
|
500
524
|
if (existsSync(routesPath)) {
|
|
501
|
-
// remove the folder
|
|
502
525
|
try {
|
|
503
526
|
rmSync(routesPath, { recursive: true });
|
|
504
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
505
527
|
} catch (error) {
|
|
506
|
-
|
|
528
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
507
529
|
}
|
|
508
530
|
routesPath = path.join(projectDir, "data", "routes");
|
|
509
531
|
if (!existsSync(routesPath)) {
|
|
510
532
|
mkdirSync(routesPath, { recursive: true });
|
|
511
533
|
}
|
|
512
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
534
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
513
535
|
}
|
|
514
536
|
}
|
|
515
537
|
|
|
@@ -517,49 +539,63 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
517
539
|
return;
|
|
518
540
|
}
|
|
519
541
|
|
|
542
|
+
let isUtilStep = false;
|
|
543
|
+
let isChangedUtilStepName = false;
|
|
544
|
+
|
|
520
545
|
if (step.isImplemented && step.shouldOverride) {
|
|
521
|
-
|
|
546
|
+
const stepDef = stepsDefinitions.findMatchingStep(step.text);
|
|
522
547
|
codePage = getCodePage(stepDef.file);
|
|
523
548
|
} else {
|
|
524
|
-
|
|
549
|
+
isUtilStep = makeStepTextUnique(step, stepsDefinitions);
|
|
525
550
|
|
|
526
551
|
if (isUtilStep) {
|
|
527
|
-
|
|
552
|
+
isChangedUtilStepName =
|
|
553
|
+
step.renamedText && stepsDefinitions.findMatchingStep(step.renamedText)?.file.endsWith("default_page.mjs");
|
|
554
|
+
|
|
555
|
+
if (!isChangedUtilStepName) {
|
|
556
|
+
const isUtilStep = stepsDefinitions.findMatchingStep(step.text)?.file.endsWith("utils.mjs");
|
|
557
|
+
if (!step.renamedText || step.renamedText === step.text || isUtilStep) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
step.text = step.text.trim();
|
|
561
|
+
const { functionName } = stepsDefinitions.findMatchingStep(step.renamedText);
|
|
562
|
+
step.renamedText = functionName;
|
|
563
|
+
const newImportLine = `import { ${functionName} } from "./utils.mjs";\n`;
|
|
564
|
+
|
|
565
|
+
if (!codePage.fileContent.includes(newImportLine)) {
|
|
566
|
+
codePage.fileContent = newImportLine + codePage.fileContent;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
528
569
|
}
|
|
529
570
|
}
|
|
530
571
|
|
|
572
|
+
const renamedUtil = step.renamedText && isUtilStep;
|
|
573
|
+
|
|
531
574
|
routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
532
575
|
if (process.env.TEMP_RUN === "true") {
|
|
533
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
534
576
|
if (existsSync(routesPath)) {
|
|
535
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
536
577
|
rmSync(routesPath, { recursive: true });
|
|
537
578
|
}
|
|
538
579
|
mkdirSync(routesPath, { recursive: true });
|
|
539
|
-
|
|
540
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
580
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
541
581
|
} else {
|
|
542
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
543
582
|
if (existsSync(routesPath)) {
|
|
544
|
-
// remove the folder
|
|
545
583
|
try {
|
|
546
584
|
rmSync(routesPath, { recursive: true });
|
|
547
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
548
585
|
} catch (error) {
|
|
549
|
-
|
|
586
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
550
587
|
}
|
|
551
588
|
}
|
|
552
589
|
routesPath = path.join(projectDir, "data", "routes");
|
|
553
|
-
console.log("Saving routes to:", routesPath);
|
|
554
590
|
if (!existsSync(routesPath)) {
|
|
555
591
|
mkdirSync(routesPath, { recursive: true });
|
|
556
592
|
}
|
|
557
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
593
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
558
594
|
}
|
|
559
595
|
|
|
560
596
|
cucumberStep.text = step.text;
|
|
561
597
|
const recording = new Recording();
|
|
562
|
-
|
|
598
|
+
|
|
563
599
|
const steps = step.commands;
|
|
564
600
|
|
|
565
601
|
recording.loadFromObject({ steps, step: cucumberStep });
|
|
@@ -610,7 +646,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
610
646
|
stepsDefinitions
|
|
611
647
|
);
|
|
612
648
|
|
|
613
|
-
if (!step.isImplemented) {
|
|
649
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
614
650
|
stepsDefinitions.addStep({
|
|
615
651
|
name: step.text,
|
|
616
652
|
file: result.codePage.sourceFileName,
|
|
@@ -622,13 +658,16 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
622
658
|
return result.codePage;
|
|
623
659
|
} else {
|
|
624
660
|
const generateCodeResult = generateCode(recording, codePage, userData, projectDir, methodName);
|
|
625
|
-
|
|
626
|
-
|
|
661
|
+
console.log("Generated code for step:", step.text);
|
|
662
|
+
if (!isUtilStep && generateCodeResult.noCode === true) {
|
|
627
663
|
return generateCodeResult.page;
|
|
628
664
|
}
|
|
629
665
|
codePage = generateCodeResult.page;
|
|
630
666
|
methodName = generateCodeResult.methodName;
|
|
631
|
-
|
|
667
|
+
|
|
668
|
+
if (!renamedUtil) {
|
|
669
|
+
codePage.insertElements(generateCodeResult.elements);
|
|
670
|
+
}
|
|
632
671
|
|
|
633
672
|
const description = cucumberStep.text;
|
|
634
673
|
let path = null;
|
|
@@ -641,15 +680,43 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
641
680
|
protect = true;
|
|
642
681
|
}
|
|
643
682
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
+
|
|
653
720
|
const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
|
|
654
721
|
const stepResult = codePage.addCucumberStep(
|
|
655
722
|
keyword,
|
|
@@ -659,7 +726,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
659
726
|
step.finalTimeout
|
|
660
727
|
);
|
|
661
728
|
|
|
662
|
-
if (!step.isImplemented) {
|
|
729
|
+
if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
|
|
663
730
|
stepsDefinitions.addStep({
|
|
664
731
|
name: step.text,
|
|
665
732
|
file: codePage.sourceFileName,
|
|
@@ -668,6 +735,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
668
735
|
}
|
|
669
736
|
|
|
670
737
|
codePage.removeUnusedElements();
|
|
738
|
+
codePage.mergeSimilarElements();
|
|
671
739
|
cucumberStep.methodName = methodName;
|
|
672
740
|
if (generateCodeResult.locatorsMetadata) {
|
|
673
741
|
codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
|
|
@@ -711,7 +779,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
711
779
|
const file = step?.file;
|
|
712
780
|
const locatorsJson = getLocatorsJson(file);
|
|
713
781
|
if (!step) {
|
|
714
|
-
throw new Error(
|
|
782
|
+
throw new Error(`Step definition not found: ${stepName}`);
|
|
715
783
|
}
|
|
716
784
|
isImplemented = true;
|
|
717
785
|
const { codeCommands, codePage, elements, parametersNames, error } =
|
|
@@ -719,26 +787,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
719
787
|
if (error) {
|
|
720
788
|
throw new Error(error);
|
|
721
789
|
}
|
|
790
|
+
isUtilStep = codePage.sourceFileName.endsWith("utils.mjs") || codePage.sourceFileName.endsWith("default_page.mjs");
|
|
722
791
|
|
|
723
792
|
if (parametersNames.length !== stepParams.length) {
|
|
724
793
|
// console.log("Parameters mismatch", parametersNames, stepParams);
|
|
725
794
|
throw new Error("Parameters mismatch");
|
|
726
795
|
}
|
|
727
|
-
for (let i = 0; i < parametersNames.length; i++) {
|
|
728
|
-
stepParams[i].argumentName = parametersNames[i];
|
|
729
|
-
}
|
|
730
796
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
+
}
|
|
739
816
|
}
|
|
817
|
+
commands.push(command);
|
|
740
818
|
}
|
|
741
|
-
commands.push(command);
|
|
742
819
|
}
|
|
743
820
|
} catch (error) {
|
|
744
821
|
console.error(error);
|
|
@@ -786,7 +863,7 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
|
|
|
786
863
|
}
|
|
787
864
|
}
|
|
788
865
|
|
|
789
|
-
export async function updateStepDefinitions({ scenario, featureName, projectDir }) {
|
|
866
|
+
export async function updateStepDefinitions({ scenario, featureName, projectDir, logger }) {
|
|
790
867
|
// set the candidate step definition file name
|
|
791
868
|
// set the utils file path
|
|
792
869
|
const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
|
|
@@ -817,44 +894,46 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
817
894
|
step.isImplementedWhileRecording = true;
|
|
818
895
|
}
|
|
819
896
|
}
|
|
820
|
-
if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
|
|
897
|
+
if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
|
|
821
898
|
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
822
899
|
if (process.env.TEMP_RUN === "true") {
|
|
823
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
824
900
|
if (existsSync(routesPath)) {
|
|
825
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
826
901
|
routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
827
902
|
rmSync(routesPath, { recursive: true });
|
|
828
903
|
}
|
|
829
904
|
mkdirSync(routesPath, { recursive: true });
|
|
830
|
-
|
|
831
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
905
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
832
906
|
} else {
|
|
833
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
834
907
|
if (existsSync(routesPath)) {
|
|
835
|
-
// remove the folder
|
|
836
908
|
try {
|
|
837
909
|
rmSync(routesPath, { recursive: true });
|
|
838
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
839
910
|
} catch (error) {
|
|
840
|
-
|
|
911
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
841
912
|
}
|
|
842
913
|
}
|
|
843
914
|
routesPath = path.join(projectDir, "data", "routes");
|
|
844
|
-
console.log("Saving routes to:", routesPath);
|
|
845
915
|
if (!existsSync(routesPath)) {
|
|
846
916
|
mkdirSync(routesPath, { recursive: true });
|
|
847
917
|
}
|
|
848
|
-
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));
|
|
849
922
|
}
|
|
850
923
|
continue;
|
|
851
924
|
}
|
|
852
925
|
const cucumberStep = getCucumberStep({ step });
|
|
853
926
|
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
854
927
|
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
855
|
-
// path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
856
928
|
let codePage = getCodePage(stepDefsFilePath);
|
|
857
|
-
codePage = await saveRecording({
|
|
929
|
+
codePage = await saveRecording({
|
|
930
|
+
step,
|
|
931
|
+
cucumberStep,
|
|
932
|
+
codePage,
|
|
933
|
+
projectDir,
|
|
934
|
+
stepsDefinitions,
|
|
935
|
+
parametersMap: scenario.parametersMap,
|
|
936
|
+
});
|
|
858
937
|
if (!codePage) {
|
|
859
938
|
continue;
|
|
860
939
|
}
|
|
@@ -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
|
}
|