@dev-blinq/cucumber_client 1.0.1239-stage → 1.0.1240-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/client/code_cleanup/utils.js +5 -1
- package/bin/client/code_gen/playwright_codeget.js +2 -0
- package/bin/client/cucumber/feature.js +4 -0
- package/bin/client/recorderv3/bvt_recorder.js +49 -46
- package/bin/client/recorderv3/index.js +26 -54
- package/bin/client/recorderv3/services.js +3 -15
- package/bin/client/utils/socket_logger.js +132 -0
- package/package.json +1 -1
|
@@ -7,10 +7,11 @@ const { default: traverse } = pkg;
|
|
|
7
7
|
import pkg1 from "@babel/generator";
|
|
8
8
|
const { default: generate } = pkg1;
|
|
9
9
|
import * as t from "@babel/types";
|
|
10
|
-
|
|
11
10
|
import { CucumberExpression, ParameterTypeRegistry } from "@cucumber/cucumber-expressions";
|
|
12
11
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
13
12
|
import { getAiConfig } from "../code_gen/page_reflection.js";
|
|
13
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
14
|
+
|
|
14
15
|
const STEP_KEYWORDS = new Set(["Given", "When", "Then"]);
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -307,14 +308,17 @@ export function getDefaultPrettierConfig() {
|
|
|
307
308
|
const configContent = readFileSync(prettierConfigPath, "utf-8");
|
|
308
309
|
prettierConfig = JSON.parse(configContent);
|
|
309
310
|
} catch (error) {
|
|
311
|
+
socketLogger.error("Error parsing Prettier config file", error);
|
|
310
312
|
console.error(`Error parsing Prettier config file: ${error}`);
|
|
311
313
|
}
|
|
312
314
|
} else {
|
|
313
315
|
// save the default config to .prettierrc
|
|
314
316
|
try {
|
|
315
317
|
writeFileSync(prettierConfigPath, JSON.stringify(prettierConfig, null, 2), "utf-8");
|
|
318
|
+
socketLogger.info(`Created default Prettier config at ${prettierConfigPath}`);
|
|
316
319
|
console.log(`Created default Prettier config at ${prettierConfigPath}`);
|
|
317
320
|
} catch (error) {
|
|
321
|
+
socketLogger.error(`Error writing Prettier config file: ${error}`);
|
|
318
322
|
console.error(`Error writing Prettier config file: ${error}`);
|
|
319
323
|
}
|
|
320
324
|
}
|
|
@@ -4,6 +4,7 @@ import { StepsDefinitions } from "../cucumber/steps_definitions.js";
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { CodePage } from "./page_reflection.js";
|
|
6
6
|
import { convertToIdentifier, escapeNonPrintables } from "./utils.js";
|
|
7
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
7
8
|
const findElementIdentifier = (node, step, userData, elements) => {
|
|
8
9
|
if (node.key) {
|
|
9
10
|
// incase of rerunning implemented steps
|
|
@@ -725,6 +726,7 @@ const generateCode = (recording, codePage, userData, projectDir, methodName) =>
|
|
|
725
726
|
}
|
|
726
727
|
let elements = {};
|
|
727
728
|
if (!codePage) {
|
|
729
|
+
socketLogger.info("CodePage is null");
|
|
728
730
|
console.log("codePage is null");
|
|
729
731
|
} else {
|
|
730
732
|
elements = codePage.getVariableDeclarationAsObject("elements");
|
|
@@ -8,6 +8,7 @@ import { parseStepTextParameters, toCucumberExpression, unEscapeNonPrintables }
|
|
|
8
8
|
import stream from "stream";
|
|
9
9
|
import { testStringForRegex } from "../recorderv3/update_feature.js";
|
|
10
10
|
import { generateTestData } from "./feature_data.js";
|
|
11
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
11
12
|
class DataTable {
|
|
12
13
|
constructor(dataTableDocument) {
|
|
13
14
|
if (!dataTableDocument) {
|
|
@@ -544,6 +545,9 @@ const scenarioResolution = async (featureFilePath) => {
|
|
|
544
545
|
fs.rmSync(tmpDir, { recursive: true });
|
|
545
546
|
}
|
|
546
547
|
} catch (e) {
|
|
548
|
+
socketLogger.error(
|
|
549
|
+
`An error has occurred while removing the temp folder at ${tmpDir}. Please remove it manually. Error: ${e}`
|
|
550
|
+
);
|
|
547
551
|
console.error(
|
|
548
552
|
`An error has occurred while removing the temp folder at ${tmpDir}. Please remove it manually. Error: ${e}`
|
|
549
553
|
);
|
|
@@ -12,11 +12,9 @@ import { updateFeatureFile } from "./update_feature.js";
|
|
|
12
12
|
import { parseStepTextParameters } from "../cucumber/utils.js";
|
|
13
13
|
import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
|
|
14
14
|
import chokidar from "chokidar";
|
|
15
|
-
import logger from "../../logger.js";
|
|
16
15
|
import { unEscapeNonPrintables } from "../cucumber/utils.js";
|
|
17
16
|
import { findAvailablePort } from "../utils/index.js";
|
|
18
|
-
import
|
|
19
|
-
|
|
17
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
20
18
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
21
19
|
|
|
22
20
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -45,7 +43,6 @@ async function evaluate(frame, script) {
|
|
|
45
43
|
async function findNestedFrameSelector(frame, obj) {
|
|
46
44
|
try {
|
|
47
45
|
const parent = frame.parentFrame();
|
|
48
|
-
if (parent) console.log(`Parent frame: ${JSON.stringify(parent)}`);
|
|
49
46
|
if (!parent) return { children: obj };
|
|
50
47
|
const frameElement = await frame.frameElement();
|
|
51
48
|
if (!frameElement) return;
|
|
@@ -54,6 +51,7 @@ async function findNestedFrameSelector(frame, obj) {
|
|
|
54
51
|
}, frameElement);
|
|
55
52
|
return findNestedFrameSelector(parent, { children: obj, selectors });
|
|
56
53
|
} catch (e) {
|
|
54
|
+
socketLogger.error(`Error in findNestedFrameSelector: ${e}`);
|
|
57
55
|
console.error(e);
|
|
58
56
|
}
|
|
59
57
|
}
|
|
@@ -150,6 +148,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
|
|
|
150
148
|
};
|
|
151
149
|
}
|
|
152
150
|
default: {
|
|
151
|
+
socketLogger.error(`Action not supported: ${action.name}`);
|
|
153
152
|
console.log("action not supported", action);
|
|
154
153
|
throw new Error("action not supported");
|
|
155
154
|
}
|
|
@@ -161,6 +160,7 @@ const transformAction = (action, el, isVerify, isPopupCloseClick, isInHoverMode,
|
|
|
161
160
|
* @property {string} projectDir
|
|
162
161
|
* @property {string} TOKEN
|
|
163
162
|
* @property {(name:string, data:any)=> void} sendEvent
|
|
163
|
+
* @property {Object} logger
|
|
164
164
|
*/
|
|
165
165
|
export class BVTRecorder {
|
|
166
166
|
#currentURL = "";
|
|
@@ -174,7 +174,6 @@ export class BVTRecorder {
|
|
|
174
174
|
*/
|
|
175
175
|
constructor(initialState) {
|
|
176
176
|
Object.assign(this, initialState);
|
|
177
|
-
this.logger = logger;
|
|
178
177
|
this.screenshotMap = new Map();
|
|
179
178
|
this.snapshotMap = new Map();
|
|
180
179
|
this.scenariosStepsMap = new Map();
|
|
@@ -187,7 +186,6 @@ export class BVTRecorder {
|
|
|
187
186
|
this.pageSet = new Set();
|
|
188
187
|
this.pageMetaDataSet = new Set();
|
|
189
188
|
this.lastKnownUrlPath = "";
|
|
190
|
-
// TODO: what is world?
|
|
191
189
|
this.world = { attach: () => {} };
|
|
192
190
|
this.shouldTakeScreenshot = true;
|
|
193
191
|
this.watcher = null;
|
|
@@ -210,7 +208,7 @@ export class BVTRecorder {
|
|
|
210
208
|
__bvt_recordCommand: async ({ frame, page, context }, event) => {
|
|
211
209
|
this.#activeFrame = frame;
|
|
212
210
|
const nestFrmLoc = await findNestedFrameSelector(frame);
|
|
213
|
-
|
|
211
|
+
this.logger.info(`Time taken for action: ${event.statistics.time}`);
|
|
214
212
|
await this.onAction({ ...event, nestFrmLoc });
|
|
215
213
|
},
|
|
216
214
|
__bvt_getMode: async () => {
|
|
@@ -229,8 +227,7 @@ export class BVTRecorder {
|
|
|
229
227
|
await this.onClosePopup();
|
|
230
228
|
},
|
|
231
229
|
__bvt_log: async (src, message) => {
|
|
232
|
-
|
|
233
|
-
console.log(`Inside Browser: ${message}`);
|
|
230
|
+
this.logger.info(`Inside Browser: ${message}`);
|
|
234
231
|
},
|
|
235
232
|
__bvt_getObject: (_src, obj) => {
|
|
236
233
|
this.processObject(obj);
|
|
@@ -304,7 +301,7 @@ export class BVTRecorder {
|
|
|
304
301
|
try {
|
|
305
302
|
ai_config = JSON.parse(readFileSync(ai_config_file, "utf8"));
|
|
306
303
|
} catch (error) {
|
|
307
|
-
|
|
304
|
+
this.logger.error("Error reading ai_config.json", error);
|
|
308
305
|
}
|
|
309
306
|
}
|
|
310
307
|
this.config = ai_config;
|
|
@@ -316,10 +313,7 @@ export class BVTRecorder {
|
|
|
316
313
|
],
|
|
317
314
|
};
|
|
318
315
|
|
|
319
|
-
let startTime = Date.now();
|
|
320
316
|
const bvtContext = await initContext(url, false, false, this.world, 450, initScripts, this.envName);
|
|
321
|
-
let stopTime = Date.now();
|
|
322
|
-
this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
|
|
323
317
|
this.bvtContext = bvtContext;
|
|
324
318
|
this.stepRunner = new BVTStepRunner({
|
|
325
319
|
projectDir: this.projectDir,
|
|
@@ -327,23 +321,18 @@ export class BVTRecorder {
|
|
|
327
321
|
if (data && data.type) {
|
|
328
322
|
switch (data.type) {
|
|
329
323
|
case "cmdExecutionStart":
|
|
330
|
-
console.log("Sending cmdExecutionStart event for cmdId:", data);
|
|
331
324
|
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
332
325
|
break;
|
|
333
326
|
case "cmdExecutionSuccess":
|
|
334
|
-
console.log("Sending cmdExecutionSuccess event for cmdId:", data);
|
|
335
327
|
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
336
328
|
break;
|
|
337
329
|
case "cmdExecutionError":
|
|
338
|
-
console.log("Sending cmdExecutionError event for cmdId:", data);
|
|
339
330
|
this.sendEvent(this.events.cmdExecutionError, data);
|
|
340
331
|
break;
|
|
341
332
|
case "interceptResults":
|
|
342
|
-
console.log("Sending interceptResults event");
|
|
343
333
|
this.sendEvent(this.events.interceptResults, data);
|
|
344
334
|
break;
|
|
345
335
|
default:
|
|
346
|
-
console.warn("Unknown command execution status type:", data.type);
|
|
347
336
|
break;
|
|
348
337
|
}
|
|
349
338
|
}
|
|
@@ -382,13 +371,15 @@ export class BVTRecorder {
|
|
|
382
371
|
}
|
|
383
372
|
return;
|
|
384
373
|
} catch (error) {
|
|
385
|
-
console.error("Error evaluting in context:", error);
|
|
374
|
+
// console.error("Error evaluting in context:", error);
|
|
375
|
+
this.logger.error("Error evaluating in context:", error);
|
|
386
376
|
}
|
|
387
377
|
}
|
|
388
378
|
}
|
|
389
379
|
|
|
390
380
|
getMode() {
|
|
391
|
-
console.log("getMode", this.#mode);
|
|
381
|
+
// console.log("getMode", this.#mode);
|
|
382
|
+
this.logger.info("Current mode:", this.#mode);
|
|
392
383
|
return this.#mode;
|
|
393
384
|
}
|
|
394
385
|
|
|
@@ -430,6 +421,8 @@ export class BVTRecorder {
|
|
|
430
421
|
this.sendEvent(this.events.onBrowserClose);
|
|
431
422
|
}
|
|
432
423
|
} catch (error) {
|
|
424
|
+
this.logger.error("Error in page close event");
|
|
425
|
+
this.logger.error(error);
|
|
433
426
|
console.error("Error in page close event");
|
|
434
427
|
console.error(error);
|
|
435
428
|
}
|
|
@@ -440,8 +433,10 @@ export class BVTRecorder {
|
|
|
440
433
|
if (frame !== page.mainFrame()) return;
|
|
441
434
|
this.handlePageTransition();
|
|
442
435
|
} catch (error) {
|
|
436
|
+
this.logger.error("Error in handlePageTransition event");
|
|
437
|
+
this.logger.error(error);
|
|
443
438
|
console.error("Error in handlePageTransition event");
|
|
444
|
-
|
|
439
|
+
console.error(error);
|
|
445
440
|
}
|
|
446
441
|
try {
|
|
447
442
|
if (frame !== this.#activeFrame) return;
|
|
@@ -460,6 +455,8 @@ export class BVTRecorder {
|
|
|
460
455
|
// await this._setRecordingMode(frame);
|
|
461
456
|
// await this._initPage(page);
|
|
462
457
|
} catch (error) {
|
|
458
|
+
this.logger.error("Error in frame navigate event");
|
|
459
|
+
this.logger.error(error);
|
|
463
460
|
console.error("Error in frame navigate event");
|
|
464
461
|
// console.error(error);
|
|
465
462
|
}
|
|
@@ -542,13 +539,9 @@ export class BVTRecorder {
|
|
|
542
539
|
|
|
543
540
|
try {
|
|
544
541
|
const result = await client.send("Page.getNavigationHistory");
|
|
545
|
-
// console.log("Navigation History:", result);
|
|
546
542
|
const entries = result.entries;
|
|
547
543
|
const currentIndex = result.currentIndex;
|
|
548
544
|
|
|
549
|
-
// ignore if currentIndex is not the last entry
|
|
550
|
-
// if (currentIndex !== entries.length - 1) return;
|
|
551
|
-
|
|
552
545
|
const currentEntry = entries[currentIndex];
|
|
553
546
|
const transitionInfo = this.analyzeTransitionType(entries, currentIndex, currentEntry);
|
|
554
547
|
this.previousIndex = currentIndex;
|
|
@@ -561,6 +554,8 @@ export class BVTRecorder {
|
|
|
561
554
|
navigationAction: transitionInfo.action,
|
|
562
555
|
};
|
|
563
556
|
} catch (error) {
|
|
557
|
+
this.logger.error("Error in getCurrentTransition event");
|
|
558
|
+
this.logger.error(error);
|
|
564
559
|
console.error("Error in getTransistionType event", error);
|
|
565
560
|
} finally {
|
|
566
561
|
await client.detach();
|
|
@@ -629,6 +624,8 @@ export class BVTRecorder {
|
|
|
629
624
|
// add listener for frame navigation on new tab
|
|
630
625
|
this._addFrameNavigateListener(page);
|
|
631
626
|
} catch (error) {
|
|
627
|
+
this.logger.error("Error in page event");
|
|
628
|
+
this.logger.error(error);
|
|
632
629
|
console.error("Error in page event");
|
|
633
630
|
console.error(error);
|
|
634
631
|
}
|
|
@@ -670,6 +667,7 @@ export class BVTRecorder {
|
|
|
670
667
|
const { data } = await client.send("Page.captureScreenshot", { format: "png" });
|
|
671
668
|
return data;
|
|
672
669
|
} catch (error) {
|
|
670
|
+
this.logger.error("Error in taking browser screenshot");
|
|
673
671
|
console.error("Error in taking browser screenshot", error);
|
|
674
672
|
} finally {
|
|
675
673
|
await client.detach();
|
|
@@ -839,6 +837,7 @@ export class BVTRecorder {
|
|
|
839
837
|
BLINQ_ENV: this.envName,
|
|
840
838
|
STORE_DETAILED_NETWORK_DATA: listenNetwork ? "true" : "false",
|
|
841
839
|
CURRENT_STEP_ID: step.id,
|
|
840
|
+
DEBUG: "blinq:route",
|
|
842
841
|
};
|
|
843
842
|
|
|
844
843
|
this.bvtContext.navigate = true;
|
|
@@ -967,10 +966,11 @@ export class BVTRecorder {
|
|
|
967
966
|
if (existsSync(_getDataFile(this.world, this.bvtContext, this.web))) {
|
|
968
967
|
try {
|
|
969
968
|
const testData = JSON.parse(readFileSync(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
970
|
-
this.logger.info("Test data", testData);
|
|
969
|
+
// this.logger.info("Test data", testData);
|
|
971
970
|
this.sendEvent(this.events.getTestData, testData);
|
|
972
971
|
} catch (e) {
|
|
973
|
-
this.logger.error("Error reading test data file", e);
|
|
972
|
+
// this.logger.error("Error reading test data file", e);
|
|
973
|
+
console.log("Error reading test data file", e);
|
|
974
974
|
}
|
|
975
975
|
}
|
|
976
976
|
|
|
@@ -979,10 +979,12 @@ export class BVTRecorder {
|
|
|
979
979
|
this.watcher.on("all", async (event, path) => {
|
|
980
980
|
try {
|
|
981
981
|
const testData = JSON.parse(await readFile(_getDataFile(this.world, this.bvtContext, this.web), "utf8"));
|
|
982
|
-
this.logger.info("Test data", testData);
|
|
982
|
+
// this.logger.info("Test data", testData);
|
|
983
|
+
console.log("Test data changed", testData);
|
|
983
984
|
this.sendEvent(this.events.getTestData, testData);
|
|
984
985
|
} catch (e) {
|
|
985
|
-
this.logger.error("Error reading test data file", e);
|
|
986
|
+
// this.logger.error("Error reading test data file", e);
|
|
987
|
+
console.log("Error reading test data file", e);
|
|
986
988
|
}
|
|
987
989
|
});
|
|
988
990
|
}
|
|
@@ -1015,7 +1017,7 @@ export class BVTRecorder {
|
|
|
1015
1017
|
.filter((file) => file.endsWith(".feature"))
|
|
1016
1018
|
.map((file) => path.join(this.projectDir, "features", file));
|
|
1017
1019
|
try {
|
|
1018
|
-
const parsedFiles = featureFiles.map((file) => parseFeatureFile(file));
|
|
1020
|
+
const parsedFiles = featureFiles.map((file) => this.parseFeatureFile(file));
|
|
1019
1021
|
const output = {};
|
|
1020
1022
|
parsedFiles.forEach((file) => {
|
|
1021
1023
|
if (!file.feature) return;
|
|
@@ -1043,7 +1045,7 @@ export class BVTRecorder {
|
|
|
1043
1045
|
loadExistingScenario({ featureName, scenarioName }) {
|
|
1044
1046
|
const step_definitions = loadStepDefinitions(this.projectDir);
|
|
1045
1047
|
const featureFilePath = path.join(this.projectDir, "features", featureName);
|
|
1046
|
-
const gherkinDoc = parseFeatureFile(featureFilePath);
|
|
1048
|
+
const gherkinDoc = this.parseFeatureFile(featureFilePath);
|
|
1047
1049
|
const scenario = gherkinDoc.feature.children.find((child) => child.scenario.name === scenarioName)?.scenario;
|
|
1048
1050
|
|
|
1049
1051
|
const steps = [];
|
|
@@ -1202,20 +1204,21 @@ export class BVTRecorder {
|
|
|
1202
1204
|
await this.cleanupExecution({ tags });
|
|
1203
1205
|
await this.initExecution({ tags });
|
|
1204
1206
|
}
|
|
1205
|
-
}
|
|
1206
1207
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1208
|
+
parseFeatureFile(featureFilePath) {
|
|
1209
|
+
try {
|
|
1210
|
+
let id = 0;
|
|
1211
|
+
const uuidFn = () => (++id).toString(16);
|
|
1212
|
+
const builder = new AstBuilder(uuidFn);
|
|
1213
|
+
const matcher = new GherkinClassicTokenMatcher();
|
|
1214
|
+
const parser = new Parser(builder, matcher);
|
|
1215
|
+
const source = readFileSync(featureFilePath, "utf8");
|
|
1216
|
+
const gherkinDocument = parser.parse(source);
|
|
1217
|
+
return gherkinDocument;
|
|
1218
|
+
} catch (e) {
|
|
1219
|
+
this.logger.error(`Error parsing feature file: ${featureFilePath}`);
|
|
1220
|
+
console.log(e);
|
|
1221
|
+
}
|
|
1222
|
+
return {};
|
|
1219
1223
|
}
|
|
1220
|
-
|
|
1221
|
-
};
|
|
1224
|
+
}
|
|
@@ -4,6 +4,7 @@ import { BVTRecorder } from "./bvt_recorder.js";
|
|
|
4
4
|
import { compareWithScenario } from "../code_gen/duplication_analysis.js";
|
|
5
5
|
import { getAppDataDir } from "./app_dir.js";
|
|
6
6
|
import { readdir } from "fs/promises";
|
|
7
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
7
8
|
|
|
8
9
|
let port = process.env.EDITOR_PORT || 3003;
|
|
9
10
|
const WS_URL = "http://localhost:" + port;
|
|
@@ -16,7 +17,9 @@ class PromisifiedSocketServer {
|
|
|
16
17
|
init() {
|
|
17
18
|
this.socket.on("request", async (data) => {
|
|
18
19
|
const { event, input, id, roomId, socketId } = data;
|
|
19
|
-
|
|
20
|
+
if (event !== "recorderWindow.getCurrentChromiumPath") {
|
|
21
|
+
socketLogger.info("Received request", { event, input, id, roomId, socketId });
|
|
22
|
+
}
|
|
20
23
|
try {
|
|
21
24
|
const handler = this.routes[event];
|
|
22
25
|
if (!handler) {
|
|
@@ -24,17 +27,12 @@ class PromisifiedSocketServer {
|
|
|
24
27
|
return;
|
|
25
28
|
}
|
|
26
29
|
const response = await handler(input);
|
|
27
|
-
|
|
30
|
+
if (event !== "recorderWindow.getCurrentChromiumPath") {
|
|
31
|
+
socketLogger.info(`Sending response`, { event, id, value: response, roomId, socketId });
|
|
32
|
+
}
|
|
28
33
|
this.socket.emit("response", { id, value: response, roomId, socketId });
|
|
29
34
|
} catch (error) {
|
|
30
|
-
|
|
31
|
-
console.error("response", { id, error, roomId, socketId });
|
|
32
|
-
// console.error({
|
|
33
|
-
// message: error?.message,
|
|
34
|
-
// code: error?.code,
|
|
35
|
-
// info: error?.info,
|
|
36
|
-
// stack: error?.stack,
|
|
37
|
-
// })
|
|
35
|
+
socketLogger.error("Error handling request", { event, input, id, roomId, socketId, error });
|
|
38
36
|
this.socket.emit("response", {
|
|
39
37
|
id,
|
|
40
38
|
error: {
|
|
@@ -51,45 +49,17 @@ class PromisifiedSocketServer {
|
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
function memorySizeOf(obj) {
|
|
55
|
-
var bytes = 0;
|
|
56
|
-
|
|
57
|
-
function sizeOf(obj) {
|
|
58
|
-
if (obj !== null && obj !== undefined) {
|
|
59
|
-
switch (typeof obj) {
|
|
60
|
-
case "number":
|
|
61
|
-
bytes += 8;
|
|
62
|
-
break;
|
|
63
|
-
case "string":
|
|
64
|
-
bytes += obj.length * 2;
|
|
65
|
-
break;
|
|
66
|
-
case "boolean":
|
|
67
|
-
bytes += 4;
|
|
68
|
-
break;
|
|
69
|
-
case "object":
|
|
70
|
-
var objClass = Object.prototype.toString.call(obj).slice(8, -1);
|
|
71
|
-
if (objClass === "Object" || objClass === "Array") {
|
|
72
|
-
for (var key in obj) {
|
|
73
|
-
if (!obj.hasOwnProperty(key)) continue;
|
|
74
|
-
sizeOf(obj[key]);
|
|
75
|
-
}
|
|
76
|
-
} else bytes += obj.toString().length * 2;
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return bytes;
|
|
81
|
-
}
|
|
82
|
-
return sizeOf(obj);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
52
|
const init = ({ envName, projectDir, roomId, TOKEN }) => {
|
|
86
|
-
console.log("
|
|
53
|
+
console.log("Connecting to " + WS_URL);
|
|
87
54
|
const socket = io(WS_URL);
|
|
55
|
+
socketLogger.init(socket, { context: "BVTRecorder", eventName: "BVTRecorder.log" });
|
|
88
56
|
socket.on("connect", () => {
|
|
89
|
-
|
|
57
|
+
socketLogger.info("Connected to BVTRecorder server");
|
|
58
|
+
console.log("Connected to BVTRecorder server");
|
|
90
59
|
});
|
|
91
60
|
socket.on("disconnect", () => {
|
|
92
|
-
|
|
61
|
+
socketLogger.info("Disconnected from server");
|
|
62
|
+
console.log("Disconnected from server");
|
|
93
63
|
});
|
|
94
64
|
socket.emit("joinRoom", { id: roomId, window: "cucumber_client/bvt_recorder" });
|
|
95
65
|
const recorder = new BVTRecorder({
|
|
@@ -97,20 +67,19 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
|
|
|
97
67
|
projectDir,
|
|
98
68
|
TOKEN,
|
|
99
69
|
sendEvent: (event, data) => {
|
|
100
|
-
|
|
101
|
-
console.log("----", event, data, "roomId", roomId);
|
|
70
|
+
socketLogger.info("Sending event", { event, data, roomId });
|
|
102
71
|
socket.emit(event, data, roomId);
|
|
103
|
-
console.log("Successfully sent event", event, "to room", roomId);
|
|
104
72
|
},
|
|
73
|
+
logger: socketLogger,
|
|
105
74
|
});
|
|
106
75
|
recorder
|
|
107
76
|
.openBrowser()
|
|
108
77
|
.then(() => {
|
|
109
|
-
|
|
78
|
+
socketLogger.info("BVTRecorder.browserOpened");
|
|
110
79
|
socket.emit("BVTRecorder.browserOpened", null, roomId);
|
|
111
80
|
})
|
|
112
81
|
.catch((e) => {
|
|
113
|
-
|
|
82
|
+
socketLogger.error("BVTRecorder.browserLaunchFailed", e);
|
|
114
83
|
socket.emit("BVTRecorder.browserLaunchFailed", e, roomId);
|
|
115
84
|
});
|
|
116
85
|
const timeOutForFunction = async (promise, timeout = 5000) => {
|
|
@@ -121,6 +90,7 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
|
|
|
121
90
|
return res;
|
|
122
91
|
} catch (error) {
|
|
123
92
|
console.error(error);
|
|
93
|
+
socketLogger.error(error);
|
|
124
94
|
throw error;
|
|
125
95
|
}
|
|
126
96
|
};
|
|
@@ -130,10 +100,13 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
|
|
|
130
100
|
return recorder
|
|
131
101
|
.openBrowser(input)
|
|
132
102
|
.then(() => {
|
|
103
|
+
socketLogger.info("BVTRecorder.browserOpened");
|
|
104
|
+
console.info("BVTRecorder.browserOpened");
|
|
133
105
|
socket.emit("BVTRecorder.browserOpened", { roomId, window: "cucumber_client/bvt_recorder" });
|
|
134
106
|
})
|
|
135
107
|
.catch((e) => {
|
|
136
|
-
|
|
108
|
+
socketLogger.error("Error opening browser", e);
|
|
109
|
+
console.error("BVTRecorder.browserLaunchFailed", e);
|
|
137
110
|
socket.emit("BVTRecorder.browserLaunchFailed", { roomId, window: "cucumber_client/bvt_recorder" });
|
|
138
111
|
});
|
|
139
112
|
},
|
|
@@ -144,10 +117,12 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
|
|
|
144
117
|
return recorder
|
|
145
118
|
.reOpenBrowser(input)
|
|
146
119
|
.then(() => {
|
|
147
|
-
|
|
120
|
+
socketLogger.info("BVTRecorder.browserOpened");
|
|
121
|
+
console.log("BVTRecorder.browserOpened");
|
|
148
122
|
socket.emit("BVTRecorder.browserOpened", null, roomId);
|
|
149
123
|
})
|
|
150
124
|
.catch((e) => {
|
|
125
|
+
socketLogger.info("BVTRecorder.browserLaunchFailed");
|
|
151
126
|
console.error("BVTRecorder.browserLaunchFailed", e);
|
|
152
127
|
socket.emit("BVTRecorder.browserLaunchFailed", null, roomId);
|
|
153
128
|
});
|
|
@@ -247,9 +222,7 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
|
|
|
247
222
|
"recorderWindow.getSnapshotFiles": async (input) => {
|
|
248
223
|
const snapshotFolder = recorder.getSnapshotFolder();
|
|
249
224
|
if (snapshotFolder) {
|
|
250
|
-
// Get the list of filenames in the snapshot folder
|
|
251
225
|
const files = await readdir(snapshotFolder);
|
|
252
|
-
// Filter the files to only include .png files
|
|
253
226
|
const ymlFiles = files.filter((file) => file.endsWith(".yml") || file.endsWith(".yaml"));
|
|
254
227
|
return { folder: snapshotFolder, files: ymlFiles };
|
|
255
228
|
} else return { folder: null, files: [] };
|
|
@@ -318,7 +291,6 @@ try {
|
|
|
318
291
|
showUsage(error, usage);
|
|
319
292
|
}
|
|
320
293
|
try {
|
|
321
|
-
// console.log({ envName, projectDir, featureName, scenarioName, stepIndex, roomId })
|
|
322
294
|
init({
|
|
323
295
|
envName,
|
|
324
296
|
projectDir,
|
|
@@ -15,20 +15,7 @@ export class NamesService {
|
|
|
15
15
|
if (!screenshot && commands.length > 1) {
|
|
16
16
|
screenshot = this.screenshotMap.get(commands[commands.length - 2].inputID);
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
"data: " +
|
|
20
|
-
JSON.stringify(
|
|
21
|
-
{
|
|
22
|
-
commands,
|
|
23
|
-
stepsNames,
|
|
24
|
-
parameters,
|
|
25
|
-
map,
|
|
26
|
-
},
|
|
27
|
-
null,
|
|
28
|
-
2
|
|
29
|
-
)
|
|
30
|
-
);
|
|
31
|
-
// get screenshot for the last command
|
|
18
|
+
|
|
32
19
|
const url = `${getRunsServiceBaseURL()}/generate-step-information/generate`;
|
|
33
20
|
const TIMEOUT = 120; // 2 minutes
|
|
34
21
|
const { data } = await axiosClient({
|
|
@@ -65,6 +52,7 @@ export class NamesService {
|
|
|
65
52
|
}
|
|
66
53
|
} catch (error) {
|
|
67
54
|
if (i === TIMEOUT - 1) {
|
|
55
|
+
this.logger.error("Timeout while generating step details: ", error);
|
|
68
56
|
console.error("Timeout while generating step details: ", error);
|
|
69
57
|
}
|
|
70
58
|
}
|
|
@@ -96,7 +84,6 @@ export class NamesService {
|
|
|
96
84
|
scenario: scenarioAsText,
|
|
97
85
|
};
|
|
98
86
|
|
|
99
|
-
this.logger.info("data: " + JSON.stringify(genObject, null, 2));
|
|
100
87
|
// get screenshot for the last command
|
|
101
88
|
const url = `${getRunsServiceBaseURL()}/generate-step-information/generate_scenario_feature`;
|
|
102
89
|
const TIMEOUT = 120; // 2 minutes
|
|
@@ -140,6 +127,7 @@ export class NamesService {
|
|
|
140
127
|
if (result.status !== 200) {
|
|
141
128
|
return { success: false, message: "Error while generating step details" };
|
|
142
129
|
}
|
|
130
|
+
|
|
143
131
|
return result.data;
|
|
144
132
|
}
|
|
145
133
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} SocketLoggerEventPayload
|
|
3
|
+
* @property {string} level Log level (e.g. "info", "warn", "error", "debug")
|
|
4
|
+
* @property {string} context Log context/subsystem (e.g. "BVTRecorder")
|
|
5
|
+
* @property {*} data The log message payload (string, object, etc)
|
|
6
|
+
* @property {string} timestamp ISO string when the log was emitted
|
|
7
|
+
* @property {number} dataSize Size of data in bytes (-1 if unknown)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {Object} SocketLoggerInitOptions
|
|
12
|
+
* @property {string=} context Default context for all logs (optional)
|
|
13
|
+
* @property {string=} eventName Default event name (default: "recorder.log")
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* SocketLogger - Singleton for structured socket-based logging.
|
|
18
|
+
*
|
|
19
|
+
* @namespace SocketLogger
|
|
20
|
+
* @property {function(import('socket.io-client').Socket|import('socket.io').Socket, SocketLoggerInitOptions=):void} init
|
|
21
|
+
* @property {function(string, (string|*), Object=, string=, string=):void} log
|
|
22
|
+
* @property {function((string|*), Object=):void} info
|
|
23
|
+
* @property {function((string|*), Object=):void} warn
|
|
24
|
+
* @property {function((string|*), Object=):void} debug
|
|
25
|
+
* @property {function((string|*), Object=):void} error
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* import logger from "./socket_logger.js";
|
|
29
|
+
* logger.init(socket, { context: "BVTRecorder" });
|
|
30
|
+
* logger.info("Step started", { step: 2 });
|
|
31
|
+
* logger.error("Failed!", { error: "bad stuff" });
|
|
32
|
+
*/
|
|
33
|
+
const SocketLogger = (function () {
|
|
34
|
+
/** @type {import('socket.io-client').Socket|import('socket.io').Socket|null} */
|
|
35
|
+
let socket = null;
|
|
36
|
+
/** @type {string} */
|
|
37
|
+
let defaultContext = "";
|
|
38
|
+
/** @type {string} */
|
|
39
|
+
let defaultEventName = "recorder.log";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Initialize the logger (call once).
|
|
43
|
+
* @param {import('socket.io-client').Socket|import('socket.io').Socket} sock
|
|
44
|
+
* @param {SocketLoggerInitOptions=} opts
|
|
45
|
+
*/
|
|
46
|
+
function init(sock, opts) {
|
|
47
|
+
socket = sock;
|
|
48
|
+
defaultContext = (opts && opts.context) || "";
|
|
49
|
+
defaultEventName = (opts && opts.eventName) || "recorder.log";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Low-level log method (most users use info/warn/debug/error).
|
|
54
|
+
* @param {string} level Log level ("info", "warn", "debug", "error")
|
|
55
|
+
* @param {string|*} message The log message or object
|
|
56
|
+
* @param {Object=} extra Extra fields (will be merged into log payload)
|
|
57
|
+
* @param {string=} eventName Override event name for this log (default: "recorder.log")
|
|
58
|
+
* @param {string=} context Override log context for this log (default: set in init)
|
|
59
|
+
*/
|
|
60
|
+
function log(level, message, extra, eventName, context) {
|
|
61
|
+
if (!socket || typeof socket.emit !== "function") return;
|
|
62
|
+
/** @type {*} */
|
|
63
|
+
var data = typeof message === "object" ? message : { message: message };
|
|
64
|
+
/** @type {number} */
|
|
65
|
+
var dataSize = 0;
|
|
66
|
+
try {
|
|
67
|
+
dataSize = Buffer.byteLength(JSON.stringify(data || ""), "utf8");
|
|
68
|
+
} catch (e) {
|
|
69
|
+
dataSize = -1;
|
|
70
|
+
}
|
|
71
|
+
/** @type {SocketLoggerEventPayload} */
|
|
72
|
+
var eventPayload = Object.assign(
|
|
73
|
+
{
|
|
74
|
+
level: level,
|
|
75
|
+
context: context || defaultContext,
|
|
76
|
+
data: data,
|
|
77
|
+
timestamp: new Date().toISOString(),
|
|
78
|
+
dataSize: dataSize,
|
|
79
|
+
},
|
|
80
|
+
extra || {}
|
|
81
|
+
);
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
try {
|
|
84
|
+
if (socket) {
|
|
85
|
+
socket.emit(eventName || defaultEventName, eventPayload);
|
|
86
|
+
}
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.error("Socket logging error:", e);
|
|
89
|
+
console.log("Socket event payload:", eventPayload);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Write an info-level log event.
|
|
95
|
+
* @param {string|*} msg The message or object
|
|
96
|
+
* @param {Object=} ext Any extra fields/metadata
|
|
97
|
+
*/
|
|
98
|
+
function info(msg, ext) {
|
|
99
|
+
log("info", msg, ext);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Write a warn-level log event.
|
|
104
|
+
* @param {string|*} msg The message or object
|
|
105
|
+
* @param {Object=} ext Any extra fields/metadata
|
|
106
|
+
*/
|
|
107
|
+
function warn(msg, ext) {
|
|
108
|
+
log("warn", msg, ext);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Write a debug-level log event.
|
|
113
|
+
* @param {string|*} msg The message or object
|
|
114
|
+
* @param {Object=} ext Any extra fields/metadata
|
|
115
|
+
*/
|
|
116
|
+
function debug(msg, ext) {
|
|
117
|
+
log("debug", msg, ext);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Write an error-level log event.
|
|
122
|
+
* @param {string|*} msg The message or object
|
|
123
|
+
* @param {Object=} ext Any extra fields/metadata
|
|
124
|
+
*/
|
|
125
|
+
function error(msg, ext) {
|
|
126
|
+
log("error", msg, ext);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { init, log, info, warn, debug, error };
|
|
130
|
+
})();
|
|
131
|
+
|
|
132
|
+
export default SocketLogger;
|