@dev-blinq/cucumber_client 1.0.1690-dev → 1.0.1692-dev
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/client/code_cleanup/utils.js +16 -7
- package/bin/client/code_gen/duplication_analysis.js +2 -1
- package/bin/client/code_gen/page_reflection.js +40 -0
- package/bin/client/code_gen/playwright_codeget.js +19 -10
- package/bin/client/cucumber/feature.js +4 -17
- package/bin/client/recorderv3/bvt_init.js +40 -83
- package/bin/client/recorderv3/bvt_recorder.js +289 -28
- package/bin/client/recorderv3/services.js +2 -1
- package/bin/client/recorderv3/step_runner.js +17 -5
- package/bin/client/recorderv3/step_utils.js +31 -38
- package/bin/client/recorderv3/update_feature.js +45 -28
- package/bin/client/utils/app_dir.js +21 -0
- package/bin/client/utils/socket_logger.js +38 -14
- package/package.json +6 -2
- 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,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, stepText) => {
|
|
51
|
+
export const _toRecordingStep = (cmd, stepText) => {
|
|
52
52
|
switch (cmd.type) {
|
|
53
53
|
case "hover_element": {
|
|
54
54
|
return {
|
|
@@ -495,7 +495,15 @@ function makeStepTextUnique(step, stepsDefinitions) {
|
|
|
495
495
|
step.text = stepText;
|
|
496
496
|
}
|
|
497
497
|
|
|
498
|
-
export async function saveRecording({
|
|
498
|
+
export async function saveRecording({
|
|
499
|
+
step,
|
|
500
|
+
cucumberStep,
|
|
501
|
+
codePage,
|
|
502
|
+
projectDir,
|
|
503
|
+
stepsDefinitions,
|
|
504
|
+
parametersMap,
|
|
505
|
+
logger,
|
|
506
|
+
}) {
|
|
499
507
|
if (step.commands && Array.isArray(step.commands)) {
|
|
500
508
|
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap, step.text));
|
|
501
509
|
}
|
|
@@ -506,21 +514,19 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
506
514
|
rmSync(routesPath, { recursive: true });
|
|
507
515
|
}
|
|
508
516
|
mkdirSync(routesPath, { recursive: true });
|
|
509
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
517
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
510
518
|
} else {
|
|
511
519
|
if (existsSync(routesPath)) {
|
|
512
|
-
// remove the folder
|
|
513
520
|
try {
|
|
514
521
|
rmSync(routesPath, { recursive: true });
|
|
515
|
-
//
|
|
516
522
|
} catch (error) {
|
|
517
|
-
|
|
523
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
518
524
|
}
|
|
519
525
|
routesPath = path.join(projectDir, "data", "routes");
|
|
520
526
|
if (!existsSync(routesPath)) {
|
|
521
527
|
mkdirSync(routesPath, { recursive: true });
|
|
522
528
|
}
|
|
523
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
529
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
524
530
|
}
|
|
525
531
|
}
|
|
526
532
|
|
|
@@ -529,7 +535,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
529
535
|
}
|
|
530
536
|
|
|
531
537
|
if (step.isImplemented && step.shouldOverride) {
|
|
532
|
-
|
|
538
|
+
const stepDef = stepsDefinitions.findMatchingStep(step.text);
|
|
533
539
|
codePage = getCodePage(stepDef.file);
|
|
534
540
|
} else {
|
|
535
541
|
const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
|
|
@@ -555,21 +561,20 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
555
561
|
rmSync(routesPath, { recursive: true });
|
|
556
562
|
}
|
|
557
563
|
mkdirSync(routesPath, { recursive: true });
|
|
558
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
564
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
559
565
|
} else {
|
|
560
566
|
if (existsSync(routesPath)) {
|
|
561
|
-
// remove the folder
|
|
562
567
|
try {
|
|
563
568
|
rmSync(routesPath, { recursive: true });
|
|
564
569
|
} catch (error) {
|
|
565
|
-
|
|
570
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
566
571
|
}
|
|
567
572
|
}
|
|
568
573
|
routesPath = path.join(projectDir, "data", "routes");
|
|
569
574
|
if (!existsSync(routesPath)) {
|
|
570
575
|
mkdirSync(routesPath, { recursive: true });
|
|
571
576
|
}
|
|
572
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
577
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
573
578
|
}
|
|
574
579
|
|
|
575
580
|
cucumberStep.text = step.text;
|
|
@@ -639,7 +644,6 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
639
644
|
const generateCodeResult = generateCode(recording, codePage, userData, projectDir, methodName);
|
|
640
645
|
console.log("Generated code for step:", step.text);
|
|
641
646
|
if (generateCodeResult.noCode === true) {
|
|
642
|
-
logger.log("No code generated for step: " + step.text);
|
|
643
647
|
return generateCodeResult.page;
|
|
644
648
|
}
|
|
645
649
|
codePage = generateCodeResult.page;
|
|
@@ -749,7 +753,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
749
753
|
const file = step?.file;
|
|
750
754
|
const locatorsJson = getLocatorsJson(file);
|
|
751
755
|
if (!step) {
|
|
752
|
-
throw new Error(
|
|
756
|
+
throw new Error(`Step definition not found: ${stepName}`);
|
|
753
757
|
}
|
|
754
758
|
isImplemented = true;
|
|
755
759
|
const { codeCommands, codePage, elements, parametersNames, error } =
|
|
@@ -833,7 +837,7 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
|
|
|
833
837
|
}
|
|
834
838
|
}
|
|
835
839
|
|
|
836
|
-
export async function updateStepDefinitions({ scenario, featureName, projectDir }) {
|
|
840
|
+
export async function updateStepDefinitions({ scenario, featureName, projectDir, logger }) {
|
|
837
841
|
// set the candidate step definition file name
|
|
838
842
|
// set the utils file path
|
|
839
843
|
const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
|
|
@@ -867,32 +871,25 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
867
871
|
if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
|
|
868
872
|
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
869
873
|
if (process.env.TEMP_RUN === "true") {
|
|
870
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
871
874
|
if (existsSync(routesPath)) {
|
|
872
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
873
875
|
routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
874
876
|
rmSync(routesPath, { recursive: true });
|
|
875
877
|
}
|
|
876
878
|
mkdirSync(routesPath, { recursive: true });
|
|
877
|
-
|
|
878
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
879
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
879
880
|
} else {
|
|
880
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
881
881
|
if (existsSync(routesPath)) {
|
|
882
|
-
// remove the folder
|
|
883
882
|
try {
|
|
884
883
|
rmSync(routesPath, { recursive: true });
|
|
885
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
886
884
|
} catch (error) {
|
|
887
|
-
|
|
885
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
888
886
|
}
|
|
889
887
|
}
|
|
890
888
|
routesPath = path.join(projectDir, "data", "routes");
|
|
891
|
-
console.log("Saving routes to:", routesPath);
|
|
892
889
|
if (!existsSync(routesPath)) {
|
|
893
890
|
mkdirSync(routesPath, { recursive: true });
|
|
894
891
|
}
|
|
895
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
892
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
896
893
|
}
|
|
897
894
|
if (step.commands && Array.isArray(step.commands)) {
|
|
898
895
|
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
|
|
@@ -902,7 +899,6 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
902
899
|
const cucumberStep = getCucumberStep({ step });
|
|
903
900
|
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
904
901
|
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
905
|
-
// path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
906
902
|
let codePage = getCodePage(stepDefsFilePath);
|
|
907
903
|
codePage = await saveRecording({
|
|
908
904
|
step,
|
|
@@ -923,7 +919,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
923
919
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
924
920
|
}
|
|
925
921
|
|
|
926
|
-
export function saveRoutes({ step, folderPath }) {
|
|
922
|
+
export function saveRoutes({ step, folderPath }, logger) {
|
|
927
923
|
const routeItems = step.routeItems;
|
|
928
924
|
if (!routeItems || routeItems.length === 0) {
|
|
929
925
|
return;
|
|
@@ -946,21 +942,18 @@ export function saveRoutes({ step, folderPath }) {
|
|
|
946
942
|
};
|
|
947
943
|
});
|
|
948
944
|
|
|
949
|
-
const routesFilePath = path.join(folderPath, stepNameHash
|
|
950
|
-
console.log("Routes file path:", routesFilePath);
|
|
945
|
+
const routesFilePath = path.join(folderPath, `${stepNameHash}.json`);
|
|
951
946
|
const routesData = {
|
|
952
947
|
template,
|
|
953
948
|
routes: routeItemsWithFilters,
|
|
954
949
|
};
|
|
955
|
-
console.log("Routes data to save:", routesData);
|
|
956
950
|
|
|
957
951
|
if (!existsSync(folderPath)) {
|
|
958
952
|
mkdirSync(folderPath, { recursive: true });
|
|
959
953
|
}
|
|
960
954
|
try {
|
|
961
955
|
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
|
|
962
|
-
console.log("Saved routes to", routesFilePath);
|
|
963
956
|
} catch (error) {
|
|
964
|
-
|
|
957
|
+
logger.error(`Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
|
|
965
958
|
}
|
|
966
959
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
-
import path from "path";
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { getDefaultPrettierConfig } from "../code_cleanup/utils.js";
|
|
4
4
|
import prettier from "prettier";
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
function containsScenario({ featureFileContent, scenarioName }) {
|
|
6
7
|
const lines = featureFileContent.split("\n");
|
|
7
8
|
for (const line of lines) {
|
|
8
9
|
const trimmedLine = line.trim();
|
|
@@ -14,7 +15,7 @@ export function containsScenario({ featureFileContent, scenarioName }) {
|
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
function testStringForRegex(text) {
|
|
18
19
|
const regexEndPattern = /\/([gimuy]*)$/;
|
|
19
20
|
if (text.startsWith("/")) {
|
|
20
21
|
const match = regexEndPattern.test(text);
|
|
@@ -31,7 +32,7 @@ export function testStringForRegex(text) {
|
|
|
31
32
|
return false;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
function escapeNonPrintables(text) {
|
|
35
36
|
const t = text.replace(/\n/g, "\\n").replace(/\t/g, "\\t"); // .replace(/\|/g, "\\|");
|
|
36
37
|
let result = "";
|
|
37
38
|
// replace \ with \\ and | with \|
|
|
@@ -60,8 +61,9 @@ const escapeNonPrintables = (text) => {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
return result;
|
|
63
|
-
}
|
|
64
|
-
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getCommandContent(command) {
|
|
65
67
|
switch (command.type) {
|
|
66
68
|
case "click_element": {
|
|
67
69
|
return `${command.count === 2 ? "Double click" : "Click"} on ${escapeNonPrintables(command.element.name)}`;
|
|
@@ -108,15 +110,15 @@ export function getCommandContent(command) {
|
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
function getExamplesContent(parametersMap, datasets) {
|
|
112
114
|
if (datasets && datasets.length > 0) {
|
|
113
|
-
let result =
|
|
115
|
+
let result = "";
|
|
114
116
|
const keys = Object.keys(parametersMap);
|
|
115
117
|
result += "\t\tExamples:\n";
|
|
116
118
|
|
|
117
119
|
result += `\t\t| ${keys.join(" | ")} |\n`;
|
|
118
120
|
for (const dataset of datasets) {
|
|
119
|
-
result += `\t\t| ${dataset.data.map(d => escapeNonPrintables(d.value)).join(" | ")} |\n`;
|
|
121
|
+
result += `\t\t| ${dataset.data.map((d) => escapeNonPrintables(d.value)).join(" | ")} |\n`;
|
|
120
122
|
}
|
|
121
123
|
return result;
|
|
122
124
|
}
|
|
@@ -155,19 +157,19 @@ function getTagsContent(scenario, featureFileObject) {
|
|
|
155
157
|
return tagsContent;
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
|
|
160
|
+
function getScenarioContent({ scenario }, featureFileObject) {
|
|
159
161
|
const prametersMap = scenario.parametersMap ?? {};
|
|
160
162
|
const isParmatersMapEmpty = Object.keys(prametersMap).length === 0;
|
|
161
163
|
let scenarioContent = isParmatersMapEmpty ? `Scenario: ${scenario.name}\n` : `Scenario Outline: ${scenario.name}\n`;
|
|
162
164
|
|
|
163
|
-
|
|
165
|
+
const tagsContent = getTagsContent(scenario, featureFileObject);
|
|
164
166
|
|
|
165
|
-
scenarioContent =
|
|
167
|
+
scenarioContent = `\t${tagsContent}\n${scenarioContent}`;
|
|
166
168
|
|
|
167
169
|
for (const step of scenario.steps) {
|
|
168
170
|
const commands = step.commands ?? [];
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
const commentContents = commands.map(getCommandContent).map(escapeNonPrintables);
|
|
172
|
+
const commentContent = commentContents.filter((content) => content.length > 0).join(", ");
|
|
171
173
|
|
|
172
174
|
if (commentContent) {
|
|
173
175
|
scenarioContent += `\t# ${commentContent}\n`;
|
|
@@ -179,14 +181,15 @@ export function getScenarioContent({ scenario }, featureFileObject) {
|
|
|
179
181
|
}
|
|
180
182
|
return scenarioContent;
|
|
181
183
|
}
|
|
182
|
-
|
|
184
|
+
|
|
185
|
+
function escapeString(str) {
|
|
183
186
|
// Step 1: Replace all literal newline characters with a temporary placeholder
|
|
184
187
|
const placeholder = "\\n";
|
|
185
188
|
str = str.replace(/\n/g, placeholder);
|
|
186
189
|
return str;
|
|
187
|
-
}
|
|
190
|
+
}
|
|
188
191
|
|
|
189
|
-
|
|
192
|
+
function GherkinToObject(gherkin) {
|
|
190
193
|
const obj = {
|
|
191
194
|
featureName: "",
|
|
192
195
|
scenarios: [],
|
|
@@ -222,7 +225,7 @@ const GherkinToObject = (gherkin) => {
|
|
|
222
225
|
}
|
|
223
226
|
|
|
224
227
|
const getScenario = () => {
|
|
225
|
-
|
|
228
|
+
const scenario = {
|
|
226
229
|
name: "",
|
|
227
230
|
tags: [],
|
|
228
231
|
steps: [],
|
|
@@ -299,7 +302,7 @@ const GherkinToObject = (gherkin) => {
|
|
|
299
302
|
skipEmptyLines();
|
|
300
303
|
}
|
|
301
304
|
return obj;
|
|
302
|
-
}
|
|
305
|
+
}
|
|
303
306
|
|
|
304
307
|
// remove lines starting with "Scenario:" or "Scenario Outline:" that contain the scenario name until the next scenario and then append the new scenario content
|
|
305
308
|
// assumes that there are no multiple scenarios with the same name and having comments and tags in each scenario
|
|
@@ -333,14 +336,14 @@ function updateExistingScenario({
|
|
|
333
336
|
continue;
|
|
334
337
|
}
|
|
335
338
|
|
|
336
|
-
|
|
339
|
+
const scenarioHeader = `${scenario.hasParams ? "Scenario Outline" : "Scenario"}: ${scenario.name}`;
|
|
337
340
|
let tagsLine;
|
|
338
341
|
|
|
339
342
|
if (scenario.tags?.length > 0) {
|
|
340
343
|
tagsLine = scenario.tags.map((t) => `@${t}`).join(" ");
|
|
341
344
|
}
|
|
342
345
|
|
|
343
|
-
if (tagsLine) results.push(
|
|
346
|
+
if (tagsLine) results.push(`\t${tagsLine}`);
|
|
344
347
|
results.push(`\t${scenarioHeader}`);
|
|
345
348
|
|
|
346
349
|
for (const step of scenario.steps) {
|
|
@@ -359,8 +362,8 @@ function updateExistingScenario({
|
|
|
359
362
|
return results.join("\n");
|
|
360
363
|
}
|
|
361
364
|
|
|
362
|
-
|
|
363
|
-
const featureFilePath = path.join(projectDir, "features", featureName
|
|
365
|
+
async function updateFeatureFile({ featureName, scenario, override, projectDir, logger }) {
|
|
366
|
+
const featureFilePath = path.join(projectDir, "features", `${featureName}.feature`);
|
|
364
367
|
const isFeatureFileExists = existsSync(featureFilePath);
|
|
365
368
|
const scenarioContent = getScenarioContent(
|
|
366
369
|
{ scenario },
|
|
@@ -391,13 +394,13 @@ export async function updateFeatureFile({ featureName, scenario, override, proje
|
|
|
391
394
|
plugins: ["prettier-plugin-gherkin"],
|
|
392
395
|
});
|
|
393
396
|
} catch (error) {
|
|
394
|
-
|
|
397
|
+
logger.error("Error formatting feature file content with Prettier:", error);
|
|
395
398
|
}
|
|
396
399
|
writeFileSync(featureFilePath, updatedFeatureFileContent);
|
|
397
400
|
return;
|
|
398
401
|
}
|
|
399
402
|
}
|
|
400
|
-
let updatedFeatureFileContent = featureFileContent
|
|
403
|
+
let updatedFeatureFileContent = `${featureFileContent}\n${scenarioContent}`;
|
|
401
404
|
try {
|
|
402
405
|
updatedFeatureFileContent = await prettier.format(updatedFeatureFileContent, {
|
|
403
406
|
...prettierConfig,
|
|
@@ -405,7 +408,7 @@ export async function updateFeatureFile({ featureName, scenario, override, proje
|
|
|
405
408
|
plugins: ["prettier-plugin-gherkin"],
|
|
406
409
|
});
|
|
407
410
|
} catch (error) {
|
|
408
|
-
|
|
411
|
+
logger.error("Error formatting feature file content with Prettier:", error);
|
|
409
412
|
}
|
|
410
413
|
writeFileSync(featureFilePath, updatedFeatureFileContent);
|
|
411
414
|
} else {
|
|
@@ -417,8 +420,22 @@ export async function updateFeatureFile({ featureName, scenario, override, proje
|
|
|
417
420
|
plugins: ["prettier-plugin-gherkin"],
|
|
418
421
|
});
|
|
419
422
|
} catch (error) {
|
|
420
|
-
|
|
423
|
+
logger.error("Error formatting feature file content with Prettier:", error);
|
|
421
424
|
}
|
|
422
425
|
writeFileSync(featureFilePath, featureFileContent);
|
|
423
426
|
}
|
|
424
427
|
}
|
|
428
|
+
|
|
429
|
+
export {
|
|
430
|
+
containsScenario,
|
|
431
|
+
testStringForRegex,
|
|
432
|
+
escapeNonPrintables,
|
|
433
|
+
getCommandContent,
|
|
434
|
+
getExamplesContent,
|
|
435
|
+
getTagsContent,
|
|
436
|
+
getScenarioContent,
|
|
437
|
+
escapeString,
|
|
438
|
+
GherkinToObject,
|
|
439
|
+
updateExistingScenario,
|
|
440
|
+
updateFeatureFile,
|
|
441
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
function getAppDataDir(project_id) {
|
|
4
|
+
if (process.env.BLINQ_APPDATA_DIR) {
|
|
5
|
+
return path.join(process.env.BLINQ_APPDATA_DIR, "blinq.io", project_id);
|
|
6
|
+
}
|
|
7
|
+
let appDataDir;
|
|
8
|
+
switch (process.platform) {
|
|
9
|
+
case "win32":
|
|
10
|
+
appDataDir = path.join(process.env.APPDATA, "blinq.io", project_id);
|
|
11
|
+
break;
|
|
12
|
+
case "darwin":
|
|
13
|
+
appDataDir = path.join(os.homedir(), "Library", "Application Support", "blinq.io", project_id);
|
|
14
|
+
break;
|
|
15
|
+
default:
|
|
16
|
+
appDataDir = path.join(os.homedir(), ".config", "blinq.io", project_id);
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
return appDataDir;
|
|
20
|
+
}
|
|
21
|
+
export { getAppDataDir };
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
function getErrorMessage(err) {
|
|
2
|
+
if (typeof err === "string") {
|
|
3
|
+
return err;
|
|
4
|
+
}
|
|
5
|
+
else if (err instanceof Error) {
|
|
6
|
+
return err.message;
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.stringify(err);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return String(err);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
1
17
|
/**
|
|
2
18
|
* SocketLogger - Singleton for structured socket-based logging.
|
|
3
19
|
*
|
|
@@ -10,8 +26,8 @@
|
|
|
10
26
|
export class SocketLogger {
|
|
11
27
|
static instance;
|
|
12
28
|
socket = null;
|
|
13
|
-
defaultContext = "";
|
|
14
|
-
defaultEventName = "
|
|
29
|
+
defaultContext = "BVTRecorder";
|
|
30
|
+
defaultEventName = "BVTRecorder.log";
|
|
15
31
|
constructor() { }
|
|
16
32
|
static getInstance() {
|
|
17
33
|
if (!SocketLogger.instance) {
|
|
@@ -24,7 +40,7 @@ export class SocketLogger {
|
|
|
24
40
|
this.defaultContext = opts?.context || "";
|
|
25
41
|
this.defaultEventName = opts?.eventName || "recorder.log";
|
|
26
42
|
}
|
|
27
|
-
log(level, message, extra,
|
|
43
|
+
log(level, message, extra, context) {
|
|
28
44
|
if (!this.socket || typeof this.socket.emit !== "function")
|
|
29
45
|
return;
|
|
30
46
|
const data = typeof message === "object" ? message : { message };
|
|
@@ -32,39 +48,47 @@ export class SocketLogger {
|
|
|
32
48
|
try {
|
|
33
49
|
dataSize = Buffer.byteLength(JSON.stringify(data || ""), "utf8");
|
|
34
50
|
}
|
|
35
|
-
catch
|
|
51
|
+
catch {
|
|
36
52
|
dataSize = -1;
|
|
37
53
|
}
|
|
38
54
|
const eventPayload = {
|
|
39
55
|
level,
|
|
40
56
|
context: context || this.defaultContext,
|
|
41
|
-
data: JSON.stringify(
|
|
57
|
+
data: JSON.stringify({
|
|
58
|
+
...data,
|
|
59
|
+
errorMessage: level === "error" && data ? getErrorMessage(data) : undefined,
|
|
60
|
+
}),
|
|
42
61
|
timestamp: new Date().toISOString(),
|
|
43
62
|
dataSize,
|
|
44
63
|
...extra,
|
|
45
64
|
};
|
|
46
65
|
try {
|
|
47
66
|
if (this.socket) {
|
|
48
|
-
this.socket.emit(
|
|
67
|
+
this.socket.emit(this.defaultEventName, eventPayload);
|
|
49
68
|
}
|
|
69
|
+
console.log(`${context ?? this.defaultContext} [${level.toUpperCase()}]:`, {
|
|
70
|
+
...data,
|
|
71
|
+
...extra,
|
|
72
|
+
});
|
|
50
73
|
}
|
|
51
74
|
catch (e) {
|
|
52
75
|
console.error("Socket logging error:", e);
|
|
53
76
|
console.log("Socket event payload:", eventPayload);
|
|
54
77
|
}
|
|
55
78
|
}
|
|
56
|
-
info(msg, ext) {
|
|
57
|
-
this.log("info", msg, ext);
|
|
79
|
+
info(msg, ext, ctx) {
|
|
80
|
+
this.log("info", msg, ext, ctx);
|
|
58
81
|
}
|
|
59
|
-
warn(msg, ext) {
|
|
60
|
-
this.log("warn", msg, ext);
|
|
82
|
+
warn(msg, ext, ctx) {
|
|
83
|
+
this.log("warn", msg, ext, ctx);
|
|
61
84
|
}
|
|
62
|
-
debug(msg, ext) {
|
|
63
|
-
this.log("debug", msg, ext);
|
|
85
|
+
debug(msg, ext, ctx) {
|
|
86
|
+
this.log("debug", msg, ext, ctx);
|
|
64
87
|
}
|
|
65
|
-
error(msg, ext) {
|
|
66
|
-
this.log("error", msg, ext);
|
|
88
|
+
error(msg, ext, ctx) {
|
|
89
|
+
this.log("error", msg, ext, ctx);
|
|
67
90
|
}
|
|
68
91
|
}
|
|
69
92
|
const socketLoggerInstance = SocketLogger.getInstance();
|
|
70
93
|
export default socketLoggerInstance;
|
|
94
|
+
export { getErrorMessage };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev-blinq/cucumber_client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1692-dev",
|
|
4
4
|
"description": " ",
|
|
5
5
|
"main": "bin/index.js",
|
|
6
6
|
"types": "bin/index.d.ts",
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"tests_prod": "rm -rf results.json && cross-env NODE_ENV_BLINQ=prod TOKEN=xxx npx mocha --parallel --jobs=10 ./tests",
|
|
15
15
|
"tests": "node ./multi_test_runner.js",
|
|
16
16
|
"lint": "eslint ./src/**/*.js",
|
|
17
|
+
"test:unit": "vitest run",
|
|
18
|
+
"test:unit:watch": "vitest watch",
|
|
17
19
|
"clean_old": "rimraf -g build1 && rimraf -g types1",
|
|
18
20
|
"clean": "rimraf -g build && rimraf -g types",
|
|
19
21
|
"build_old": "npm run clean_old && npm run pack_old",
|
|
@@ -70,6 +72,8 @@
|
|
|
70
72
|
"readline-sync": "^1.4.10",
|
|
71
73
|
"ts-node": "^10.9.2",
|
|
72
74
|
"tsup": "^8.5.0",
|
|
73
|
-
"typescript": "^5.9.2"
|
|
75
|
+
"typescript": "^5.9.2",
|
|
76
|
+
"vite-tsconfig-paths": "^6.0.4",
|
|
77
|
+
"vitest": "^2.1.4"
|
|
74
78
|
}
|
|
75
79
|
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import os from "os";
|
|
3
|
-
|
|
4
|
-
export const getAppDataDir = (project_id) => {
|
|
5
|
-
if (process.env.BLINQ_APPDATA_DIR) {
|
|
6
|
-
return path.join(process.env.BLINQ_APPDATA_DIR, "blinq.io", project_id);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
let appDataDir;
|
|
10
|
-
|
|
11
|
-
switch (process.platform) {
|
|
12
|
-
case "win32":
|
|
13
|
-
appDataDir = path.join(process.env.APPDATA, "blinq.io", project_id);
|
|
14
|
-
break;
|
|
15
|
-
case "darwin":
|
|
16
|
-
appDataDir = path.join(os.homedir(), "Library", "Application Support", "blinq.io", project_id);
|
|
17
|
-
break;
|
|
18
|
-
default:
|
|
19
|
-
appDataDir = path.join(os.homedir(), ".config", "blinq.io", project_id);
|
|
20
|
-
break;
|
|
21
|
-
}
|
|
22
|
-
return appDataDir;
|
|
23
|
-
};
|