@dev-blinq/cucumber_client 1.0.1454-dev → 1.0.1455-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.
@@ -32,7 +32,7 @@ class PromisifiedSocketServer {
32
32
  }
33
33
  this.socket.emit("response", { id, value: response, roomId, socketId });
34
34
  } catch (error) {
35
- socketLogger.error("Error handling request", { event, input, id, roomId, socketId, error });
35
+ socketLogger.error("Error handling request", { event, input, id, roomId, socketId, error: error instanceof Error ? `${error.message}\n${error.stack}` : error });
36
36
  this.socket.emit("response", {
37
37
  id,
38
38
  error: {
@@ -257,7 +257,7 @@ export class BVTStepRunner {
257
257
 
258
258
  try {
259
259
  this.#lastAttemptedCmdId = null;
260
- let cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
260
+ let cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId ?? cmd.id);
261
261
  bvtContext.web.pausedCmd = null;
262
262
 
263
263
  // Clear the liveExecutionMap and set up new entries for this step
@@ -273,7 +273,7 @@ export class BVTStepRunner {
273
273
  if (bvtContext.web) {
274
274
  bvtContext.web.getCmdId = () => {
275
275
  if (cmdIDs.length === 0) {
276
- cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
276
+ cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId ?? cmd.id);
277
277
  }
278
278
  const cId = cmdIDs.shift();
279
279
  this.sendExecutionStatus({
@@ -320,6 +320,7 @@ export class BVTStepRunner {
320
320
  codePage,
321
321
  projectDir: this.projectDir,
322
322
  stepsDefinitions,
323
+ parametersMap
323
324
  });
324
325
  if (codePage) {
325
326
  await codePage.save(stepDefsFilePath);
@@ -14,6 +14,423 @@ import { createHash } from "crypto";
14
14
 
15
15
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
16
16
 
17
+ const convertToIdentifier = (text) => {
18
+ // replace all invalid characters with _
19
+ return text.replace(/[^a-zA-Z0-9_]/g, "_");
20
+ };
21
+
22
+ export const isVariable = (text) => {
23
+ if (typeof text !== "string") return false;
24
+ const isParametric = text.startsWith("<") && text.endsWith(">");
25
+ if (!isParametric) return false;
26
+ const l = text.length;
27
+ if (l < 2) return false;
28
+ const leftindex = text.indexOf("<");
29
+ const rightindex = text.indexOf(">");
30
+ return leftindex === 0 && rightindex === l - 1;
31
+ };
32
+
33
+ export const extractQuotes = (text) => {
34
+ const stringRegex = /"([^"]*)"/g;
35
+ const matches = text.match(stringRegex);
36
+ if (!matches) return [];
37
+ const quotes = [];
38
+ for (const match of matches) {
39
+ const value = match.slice(1, -1);
40
+ quotes.push(value);
41
+ }
42
+ return quotes;
43
+ };
44
+
45
+ const replaceLastOccurence = (str, search, replacement) => {
46
+ const lastIndex = str.lastIndexOf(search);
47
+ if (lastIndex === -1) return str;
48
+ return str.substring(0, lastIndex) + replacement + str.substring(lastIndex + search.length);
49
+ };
50
+
51
+ const _toRecordingStep = (cmd) => {
52
+ switch (cmd.type) {
53
+ case "hover_element": {
54
+ return {
55
+ type: "hover_element",
56
+ element: {
57
+ role: cmd.role,
58
+ name: cmd.label,
59
+ },
60
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
61
+ };
62
+ }
63
+ case "click_element": {
64
+ return {
65
+ type: "click_element",
66
+ element: {
67
+ role: cmd.role,
68
+ name: cmd.label,
69
+ },
70
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
71
+ count: cmd.count ?? 1,
72
+ };
73
+ }
74
+ case "context_click": {
75
+ return {
76
+ type: "context_click",
77
+ element: {
78
+ role: cmd.role,
79
+ name: cmd.label,
80
+ },
81
+ label: cmd.label,
82
+ value: cmd.value,
83
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
84
+ text: cmd.text,
85
+ count: cmd.count ?? 1,
86
+ };
87
+ }
88
+ case "parameterized_click": {
89
+ return {
90
+ type: "parameterized_click",
91
+ element: {
92
+ role: cmd.role,
93
+ name: cmd.label,
94
+ },
95
+ label: cmd.label,
96
+ value: cmd.value,
97
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
98
+ count: cmd.count ?? 1,
99
+ };
100
+ }
101
+ case "fill_element": {
102
+ return {
103
+ type: "fill_element",
104
+ element: {
105
+ role: cmd.role,
106
+ name: cmd.label,
107
+ },
108
+ parameters: [cmd.value, cmd.enter ?? false],
109
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
110
+ };
111
+ }
112
+ case "select_combobox": {
113
+ return {
114
+ type: "select_combobox",
115
+ element: {
116
+ role: "combobox",
117
+ name: cmd.label,
118
+ },
119
+ selectMode: "select",
120
+ parameters: [cmd.value],
121
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
122
+ };
123
+ }
124
+ case "verify_page_contains_text": {
125
+ return {
126
+ type: "verify_page_contains_text",
127
+ parameters: [cmd.value, cmd.isRegex],
128
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
129
+ };
130
+ }
131
+ case "verify_element_contains_text": {
132
+ return {
133
+ type: "verify_element_contains_text",
134
+ element: {
135
+ role: cmd.role,
136
+ name: cmd.label,
137
+ },
138
+ parameters: [cmd.value, cmd.climb],
139
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
140
+ };
141
+ }
142
+ case "close_page": {
143
+ return {
144
+ type: "close_page",
145
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
146
+ };
147
+ }
148
+ case "check_element": {
149
+ return {
150
+ type: "check_element",
151
+ element: {
152
+ role: cmd.role,
153
+ name: cmd.label,
154
+ },
155
+ check: cmd.check,
156
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
157
+ };
158
+ }
159
+ case "press_key": {
160
+ return {
161
+ type: "press_key",
162
+ element: {
163
+ role: cmd.role,
164
+ name: cmd.label,
165
+ },
166
+ key: cmd.value,
167
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
168
+ };
169
+ }
170
+ case "load_user": {
171
+ return {
172
+ type: "load_data",
173
+ parameters: ["users", cmd.value],
174
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
175
+ };
176
+ }
177
+ case "load_csv": {
178
+ return {
179
+ type: "load_data",
180
+ parameters: ["csv", `${cmd.label}:${cmd.value}`],
181
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
182
+ };
183
+ }
184
+ case "set_date_time": {
185
+ return {
186
+ type: "set_date_time",
187
+ element: {
188
+ role: cmd.role,
189
+ name: cmd.label,
190
+ },
191
+ parameters: [cmd.value],
192
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
193
+ };
194
+ }
195
+ case "set_input": {
196
+ return {
197
+ type: "set_input",
198
+ element: {
199
+ role: cmd.role,
200
+ name: cmd.label,
201
+ },
202
+ value: cmd.value,
203
+ parameters: [cmd.value],
204
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
205
+ };
206
+ }
207
+ case "extract_attribute": {
208
+ return {
209
+ type: "extract_attribute",
210
+ element: {
211
+ role: cmd.role,
212
+ name: cmd.label,
213
+ },
214
+ parameters: [cmd.selectedField, cmd.variableName],
215
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
216
+ regex: cmd.regex,
217
+ trimSpaces: cmd.trimSpaces,
218
+ };
219
+ }
220
+ case "extract_property": {
221
+ return {
222
+ type: "extract_property",
223
+ element: {
224
+ role: cmd.role,
225
+ name: cmd.label,
226
+ },
227
+ parameters: [cmd.selectedField, cmd.variableName],
228
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
229
+ regex: cmd.regex,
230
+ trimSpaces: cmd.trimSpaces,
231
+ };
232
+ }
233
+ case "verify_element_attribute": {
234
+ return {
235
+ type: "verify_element_attribute",
236
+ element: {
237
+ role: cmd.role,
238
+ name: cmd.label,
239
+ },
240
+ parameters: [cmd.selectedField, cmd.value],
241
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
242
+ };
243
+ }
244
+ case "verify_element_property": {
245
+ return {
246
+ type: "verify_element_property",
247
+ element: {
248
+ role: cmd.role,
249
+ name: cmd.label,
250
+ },
251
+ parameters: [cmd.selectedField, cmd.value],
252
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
253
+ };
254
+ }
255
+ case "conditional_wait": {
256
+ return {
257
+ type: "conditional_wait",
258
+ element: {
259
+ role: cmd.role,
260
+ name: cmd.label,
261
+ },
262
+ parameters: [cmd.timeout, cmd.selectedField, cmd.value],
263
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
264
+ };
265
+ }
266
+ case "navigate": {
267
+ return {
268
+ type: "navigate",
269
+ parameters: [cmd.value],
270
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
271
+ };
272
+ }
273
+ case "browser_go_back": {
274
+ return {
275
+ type: "browser_go_back",
276
+ };
277
+ }
278
+ case "browser_go_forward": {
279
+ return {
280
+ type: "browser_go_forward",
281
+ };
282
+ }
283
+ case "set_input_files": {
284
+ return {
285
+ type: "set_input_files",
286
+ element: {
287
+ role: cmd.role,
288
+ name: cmd.label,
289
+ },
290
+ parameters: [cmd.files],
291
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
292
+ };
293
+ }
294
+ case "verify_page_snapshot": {
295
+ return {
296
+ type: "verify_page_snapshot",
297
+ parameters: [cmd.value],
298
+ selectors: cmd.selectors,
299
+ };
300
+ }
301
+ default: {
302
+ return {
303
+ type: cmd.type,
304
+ parameters: [cmd.value],
305
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
306
+ };
307
+ }
308
+ }
309
+ };
310
+
311
+ function getBestStrategy(allStrategyLocators) {
312
+ const orderedPriorities = [
313
+ "custom",
314
+ "context",
315
+ "basic",
316
+ "text_with_index",
317
+ "ignore_digit",
318
+ "no_text",
319
+ ];
320
+ for (const strategy of orderedPriorities) {
321
+ if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
322
+ return strategy;
323
+ }
324
+ }
325
+ return null;
326
+ }
327
+
328
+
329
+ const _parameterizeLocators = (locators, replacementFromValue, replacementToValue) => {
330
+ for (const loc of locators) {
331
+ if (loc?.css?.includes(replacementFromValue)) {
332
+ loc.css = loc.css.replaceAll(replacementFromValue, replacementToValue);
333
+ }
334
+ if (loc?.text?.includes(replacementFromValue)) {
335
+ loc.text = loc.text.replaceAll(replacementFromValue, replacementToValue);
336
+ }
337
+ if (loc?.climb && typeof loc.climb === "string" && loc.climb?.includes(replacementFromValue)) {
338
+ loc.climb = loc.climb.replaceAll(replacementFromValue, replacementToValue);
339
+ }
340
+ }
341
+ return locators;
342
+ }
343
+ const parameterizeLocators = ({
344
+ cmd, locs, isValueVariable, isTextVariable,
345
+ parametersMap
346
+ }) => {
347
+ if (isValueVariable) {
348
+ const variable = cmd.value.slice(1, -1);
349
+ const val = parametersMap[variable];
350
+ const replacementFromValue = val.trim();
351
+ const replacementToValue = `{${variable}}`
352
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
353
+ }
354
+ if (isTextVariable) {
355
+ const variable = cmd.text.slice(1, -1);
356
+ const val = parametersMap[variable];
357
+ const replacementFromValue = val.trim();
358
+ const replacementToValue = `{${variable}}`
359
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
360
+ }
361
+ return locs
362
+ }
363
+
364
+ //TODO: IMPORTAN
365
+ export const toRecordingStep = (cmd, parametersMap) => {
366
+ if (cmd.type === "api") {
367
+ return {
368
+ type: "api",
369
+ value: cmd.value,
370
+ };
371
+ }
372
+ const step = _toRecordingStep(cmd);
373
+ const cmdID = {
374
+ cmdId: cmd.id,
375
+ };
376
+ Object.assign(step, cmdID);
377
+
378
+ const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
379
+
380
+ if (!locatorsObject) return step;
381
+ const isValueVariable = isVariable(cmd.value);
382
+ const isTextVariable = isVariable(cmd.text);
383
+
384
+ if (!isValueVariable && !isTextVariable) {
385
+ return step;
386
+ }
387
+
388
+ if (isValueVariable) {
389
+ step.dataSource = "parameters";
390
+ step.dataKey = convertToIdentifier(cmd.value.slice(1, -1))
391
+ }
392
+ const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
393
+
394
+ if (!allStrategyLocators) {
395
+ let locs = locatorsObject.locators;
396
+ locs = parameterizeLocators({
397
+ cmd,
398
+ locs,
399
+ isValueVariable,
400
+ isTextVariable,
401
+ parametersMap
402
+ });
403
+ locatorsObject.locators = locs;
404
+ return {
405
+ ...step,
406
+ locators: locatorsObject
407
+ };
408
+ }
409
+
410
+ for (const key in allStrategyLocators) {
411
+ if (key === "strategy") continue;
412
+ if (key === "no_text" || key === "custom") continue;
413
+ const locators = allStrategyLocators[key];
414
+ if (locators.length === 0) continue;
415
+ parameterizeLocators({
416
+ cmd,
417
+ locs: locators,
418
+ isValueVariable,
419
+ isTextVariable,
420
+ parametersMap
421
+ });
422
+ }
423
+
424
+ locatorsObject.locators = allStrategyLocators[allStrategyLocators.strategy] ?? locatorsObject.locators;
425
+
426
+ return {
427
+ ...step,
428
+ locators: locatorsObject,
429
+ allStrategyLocators,
430
+ isLocatorsAssigned: true
431
+ };
432
+ }
433
+
17
434
  export const toMethodName = (str) => {
18
435
  // Remove any non-word characters (excluding underscore) and trim spaces
19
436
  let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
@@ -70,7 +487,7 @@ function makeStepTextUnique(step, stepsDefinitions) {
70
487
  step.text = stepText;
71
488
  }
72
489
 
73
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
490
+ export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
74
491
  let routesPath = path.join(tmpdir(), "blinq_temp_routes");
75
492
 
76
493
  if (process.env.TEMP_RUN === "true") {
@@ -142,6 +559,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
142
559
 
143
560
  cucumberStep.text = step.text;
144
561
  const recording = new Recording();
562
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
145
563
  const steps = step.commands;
146
564
 
147
565
  recording.loadFromObject({ steps, step: cucumberStep });
@@ -436,7 +854,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
436
854
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
437
855
  // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
438
856
  let codePage = getCodePage(stepDefsFilePath);
439
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
857
+ codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap: scenario.parametersMap });
440
858
  if (!codePage) {
441
859
  continue;
442
860
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev-blinq/cucumber_client",
3
- "version": "1.0.1454-dev",
3
+ "version": "1.0.1455-dev",
4
4
  "description": " ",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",