@dev-blinq/cucumber_client 1.0.1323-dev → 1.0.1323-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 +108 -108
- package/bin/assets/preload/css_gen.js +10 -10
- package/bin/assets/preload/recorderv3.js +3 -1
- package/bin/assets/preload/toolbar.js +27 -29
- package/bin/assets/preload/unique_locators.js +1 -1
- package/bin/assets/preload/yaml.js +288 -275
- package/bin/assets/scripts/aria_snapshot.js +223 -220
- package/bin/assets/scripts/dom_attr.js +329 -329
- package/bin/assets/scripts/dom_parent.js +169 -174
- package/bin/assets/scripts/event_utils.js +94 -94
- package/bin/assets/scripts/pw.js +2050 -1949
- package/bin/assets/scripts/recorder.js +5 -17
- package/bin/assets/scripts/snapshot_capturer.js +153 -146
- package/bin/assets/scripts/unique_locators.js +156 -48
- package/bin/assets/scripts/yaml.js +796 -783
- package/bin/assets/templates/_hooks_template.txt +41 -0
- package/bin/assets/templates/utils_template.txt +1 -44
- package/bin/client/apiTest/apiTest.js +6 -0
- package/bin/client/cli_helpers.js +11 -13
- package/bin/client/code_cleanup/utils.js +5 -1
- package/bin/client/code_gen/api_codegen.js +2 -2
- package/bin/client/code_gen/code_inversion.js +53 -4
- package/bin/client/code_gen/page_reflection.js +839 -906
- package/bin/client/code_gen/playwright_codeget.js +26 -18
- package/bin/client/cucumber/feature.js +89 -27
- package/bin/client/cucumber/feature_data.js +2 -2
- package/bin/client/cucumber/project_to_document.js +9 -3
- package/bin/client/cucumber/steps_definitions.js +6 -3
- package/bin/client/cucumber_selector.js +17 -1
- package/bin/client/local_agent.js +6 -5
- package/bin/client/parse_feature_file.js +23 -26
- package/bin/client/playground/projects/env.json +2 -2
- package/bin/client/project.js +186 -196
- package/bin/client/recorderv3/bvt_recorder.js +190 -127
- package/bin/client/recorderv3/implemented_steps.js +74 -16
- package/bin/client/recorderv3/index.js +68 -54
- package/bin/client/recorderv3/network.js +22 -5
- package/bin/client/recorderv3/scriptTest.js +1 -1
- package/bin/client/recorderv3/services.js +4 -16
- package/bin/client/recorderv3/step_runner.js +303 -220
- package/bin/client/recorderv3/step_utils.js +484 -7
- package/bin/client/recorderv3/update_feature.js +32 -30
- package/bin/client/run_cucumber.js +5 -1
- package/bin/client/scenario_report.js +0 -5
- package/bin/client/test_scenario.js +0 -1
- package/bin/client/upload-service.js +2 -2
- package/bin/client/utils/socket_logger.js +132 -0
- package/bin/index.js +1 -0
- package/bin/logger.js +3 -2
- package/bin/min/consoleApi.min.cjs +2 -3
- package/bin/min/injectedScript.min.cjs +16 -16
- package/package.json +21 -12
|
@@ -14,6 +14,427 @@ 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 = ["custom", "context", "basic", "text_with_index", "ignore_digit", "no_text"];
|
|
313
|
+
for (const strategy of orderedPriorities) {
|
|
314
|
+
if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
|
|
315
|
+
return strategy;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const _parameterizeLocators = (locators, replacementFromValue, replacementToValue) => {
|
|
322
|
+
for (const loc of locators) {
|
|
323
|
+
if (loc?.css?.includes(replacementFromValue)) {
|
|
324
|
+
loc.css = loc.css.replaceAll(replacementFromValue, replacementToValue);
|
|
325
|
+
}
|
|
326
|
+
if (loc?.text?.includes(replacementFromValue)) {
|
|
327
|
+
loc.text = loc.text.replaceAll(replacementFromValue, replacementToValue);
|
|
328
|
+
}
|
|
329
|
+
if (loc?.climb && typeof loc.climb === "string" && loc.climb?.includes(replacementFromValue)) {
|
|
330
|
+
loc.climb = loc.climb.replaceAll(replacementFromValue, replacementToValue);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return locators;
|
|
334
|
+
};
|
|
335
|
+
const parameterizeLocators = ({ cmd, locs, isValueVariable, isTargetValueVariable, parametersMap }) => {
|
|
336
|
+
if (isValueVariable) {
|
|
337
|
+
const variable = cmd.value.slice(1, -1);
|
|
338
|
+
// const val = parametersMap[variable];
|
|
339
|
+
if (typeof cmd.text === "string") {
|
|
340
|
+
const replacementFromValue = cmd.text.trim().replace(/\s+/, " ") ?? ""; // val.trim();
|
|
341
|
+
if (replacementFromValue.length > 0) {
|
|
342
|
+
const replacementToValue = `{${variable}}`;
|
|
343
|
+
locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (isTargetValueVariable) {
|
|
348
|
+
const variable = cmd.targetValue.slice(1, -1);
|
|
349
|
+
// const val = parametersMap[variable];
|
|
350
|
+
if (typeof cmd.targetText === "string") {
|
|
351
|
+
const replacementFromValue = cmd.targetText.trim().replace(/\s+/, " ") ?? ""; // val.trim();
|
|
352
|
+
if (replacementFromValue.length > 0) {
|
|
353
|
+
const replacementToValue = `{${variable}}`;
|
|
354
|
+
locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return locs;
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
//TODO: IMPORTAN
|
|
362
|
+
export const toRecordingStep = (cmd, parametersMap) => {
|
|
363
|
+
if (cmd.type === "api") {
|
|
364
|
+
return {
|
|
365
|
+
type: "api",
|
|
366
|
+
value: cmd.value,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
const step = _toRecordingStep(cmd);
|
|
370
|
+
const cmdID = {
|
|
371
|
+
cmdId: cmd.id,
|
|
372
|
+
};
|
|
373
|
+
Object.assign(step, cmdID);
|
|
374
|
+
|
|
375
|
+
const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
|
|
376
|
+
|
|
377
|
+
if (!locatorsObject) return step;
|
|
378
|
+
|
|
379
|
+
const element_name = cmd?.locators?.element_name ?? `${cmd.label} ${cmd.role ?? "Text"}`;
|
|
380
|
+
locatorsObject.element_name = element_name;
|
|
381
|
+
|
|
382
|
+
const isValueVariable = isVariable(cmd.value);
|
|
383
|
+
const isTargetValueVariable = isVariable(cmd.targetValue);
|
|
384
|
+
const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
|
|
385
|
+
step.locators = locatorsObject;
|
|
386
|
+
step.allStrategyLocators = allStrategyLocators;
|
|
387
|
+
step.isLocatorsAssigned = true;
|
|
388
|
+
|
|
389
|
+
if (!isValueVariable && !isTargetValueVariable) {
|
|
390
|
+
return step;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (isValueVariable) {
|
|
394
|
+
step.dataSource = "parameters";
|
|
395
|
+
step.dataKey = convertToIdentifier(cmd.value.slice(1, -1));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (!allStrategyLocators) {
|
|
399
|
+
let locs = locatorsObject.locators;
|
|
400
|
+
locs = parameterizeLocators({
|
|
401
|
+
cmd,
|
|
402
|
+
locs,
|
|
403
|
+
isValueVariable,
|
|
404
|
+
isTargetValueVariable,
|
|
405
|
+
parametersMap,
|
|
406
|
+
});
|
|
407
|
+
locatorsObject.locators = locs;
|
|
408
|
+
return {
|
|
409
|
+
...step,
|
|
410
|
+
locators: locatorsObject,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
for (const key in allStrategyLocators) {
|
|
415
|
+
if (key === "strategy") continue;
|
|
416
|
+
if (key === "no_text" || key === "custom") continue;
|
|
417
|
+
const locators = allStrategyLocators[key];
|
|
418
|
+
if (locators.length === 0) continue;
|
|
419
|
+
parameterizeLocators({
|
|
420
|
+
cmd,
|
|
421
|
+
locs: locators,
|
|
422
|
+
isValueVariable,
|
|
423
|
+
isTargetValueVariable,
|
|
424
|
+
parametersMap,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
locatorsObject.locators = allStrategyLocators[allStrategyLocators.strategy] ?? locatorsObject.locators;
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
...step,
|
|
432
|
+
locators: locatorsObject,
|
|
433
|
+
allStrategyLocators,
|
|
434
|
+
isLocatorsAssigned: true,
|
|
435
|
+
};
|
|
436
|
+
};
|
|
437
|
+
|
|
17
438
|
export const toMethodName = (str) => {
|
|
18
439
|
// Remove any non-word characters (excluding underscore) and trim spaces
|
|
19
440
|
let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
|
|
@@ -70,11 +491,13 @@ function makeStepTextUnique(step, stepsDefinitions) {
|
|
|
70
491
|
step.text = stepText;
|
|
71
492
|
}
|
|
72
493
|
|
|
73
|
-
export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
|
|
74
|
-
|
|
494
|
+
export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
|
|
495
|
+
if (step.commands && Array.isArray(step.commands)) {
|
|
496
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
|
|
497
|
+
}
|
|
75
498
|
let routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
76
499
|
|
|
77
|
-
if (process.env.TEMP_RUN) {
|
|
500
|
+
if (process.env.TEMP_RUN === "true") {
|
|
78
501
|
if (existsSync(routesPath)) {
|
|
79
502
|
rmSync(routesPath, { recursive: true });
|
|
80
503
|
}
|
|
@@ -111,7 +534,8 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
111
534
|
return;
|
|
112
535
|
}
|
|
113
536
|
}
|
|
114
|
-
|
|
537
|
+
|
|
538
|
+
routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
115
539
|
if (process.env.TEMP_RUN === "true") {
|
|
116
540
|
console.log("Save routes in temp folder for running:", routesPath);
|
|
117
541
|
if (existsSync(routesPath)) {
|
|
@@ -142,6 +566,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
142
566
|
|
|
143
567
|
cucumberStep.text = step.text;
|
|
144
568
|
const recording = new Recording();
|
|
569
|
+
|
|
145
570
|
const steps = step.commands;
|
|
146
571
|
|
|
147
572
|
recording.loadFromObject({ steps, step: cucumberStep });
|
|
@@ -217,12 +642,18 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
217
642
|
if (step.commands && step.commands.length > 0 && step.commands[0]) {
|
|
218
643
|
path = step.commands[0].lastKnownUrlPath;
|
|
219
644
|
}
|
|
645
|
+
let protect = false;
|
|
646
|
+
if (step.commands && step.commands.length > 0 && step.commands[0].type) {
|
|
647
|
+
if (step.commands[0].type === "verify_element_property" || step.commands[0].type === "conditional_wait") {
|
|
648
|
+
protect = true;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
220
651
|
const infraResult = codePage.addInfraCommand(
|
|
221
652
|
methodName,
|
|
222
653
|
description,
|
|
223
654
|
cucumberStep.getVariablesList(),
|
|
224
655
|
generateCodeResult.codeLines,
|
|
225
|
-
|
|
656
|
+
protect,
|
|
226
657
|
"recorder",
|
|
227
658
|
path
|
|
228
659
|
);
|
|
@@ -244,6 +675,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
244
675
|
}
|
|
245
676
|
|
|
246
677
|
codePage.removeUnusedElements();
|
|
678
|
+
codePage.mergeSimilarElements();
|
|
247
679
|
cucumberStep.methodName = methodName;
|
|
248
680
|
if (generateCodeResult.locatorsMetadata) {
|
|
249
681
|
codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
|
|
@@ -373,6 +805,12 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
373
805
|
const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
|
|
374
806
|
const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
|
|
375
807
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
808
|
+
const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
|
|
809
|
+
if (existsSync(hooksTemplateFilePath)) {
|
|
810
|
+
const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
|
|
811
|
+
const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
|
|
812
|
+
writeFileSync(hooksFilePath, hooksContent, "utf8");
|
|
813
|
+
}
|
|
376
814
|
const steps = scenario.steps;
|
|
377
815
|
|
|
378
816
|
const stepsDefinitions = new StepsDefinitions(projectDir);
|
|
@@ -388,6 +826,38 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
388
826
|
}
|
|
389
827
|
}
|
|
390
828
|
if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
|
|
829
|
+
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
830
|
+
if (process.env.TEMP_RUN === "true") {
|
|
831
|
+
console.log("Save routes in temp folder for running:", routesPath);
|
|
832
|
+
if (existsSync(routesPath)) {
|
|
833
|
+
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
834
|
+
routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
835
|
+
rmSync(routesPath, { recursive: true });
|
|
836
|
+
}
|
|
837
|
+
mkdirSync(routesPath, { recursive: true });
|
|
838
|
+
console.log("Created temp_routes_folder:", routesPath);
|
|
839
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
840
|
+
} else {
|
|
841
|
+
console.log("Saving routes in project directory:", projectDir);
|
|
842
|
+
if (existsSync(routesPath)) {
|
|
843
|
+
// remove the folder
|
|
844
|
+
try {
|
|
845
|
+
rmSync(routesPath, { recursive: true });
|
|
846
|
+
console.log("Removed temp_routes_folder:", routesPath);
|
|
847
|
+
} catch (error) {
|
|
848
|
+
console.error("Error removing temp_routes folder", error);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
routesPath = path.join(projectDir, "data", "routes");
|
|
852
|
+
console.log("Saving routes to:", routesPath);
|
|
853
|
+
if (!existsSync(routesPath)) {
|
|
854
|
+
mkdirSync(routesPath, { recursive: true });
|
|
855
|
+
}
|
|
856
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
857
|
+
}
|
|
858
|
+
if (step.commands && Array.isArray(step.commands)) {
|
|
859
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
|
|
860
|
+
}
|
|
391
861
|
continue;
|
|
392
862
|
}
|
|
393
863
|
const cucumberStep = getCucumberStep({ step });
|
|
@@ -395,7 +865,14 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
395
865
|
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
396
866
|
// path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
397
867
|
let codePage = getCodePage(stepDefsFilePath);
|
|
398
|
-
codePage = await saveRecording({
|
|
868
|
+
codePage = await saveRecording({
|
|
869
|
+
step,
|
|
870
|
+
cucumberStep,
|
|
871
|
+
codePage,
|
|
872
|
+
projectDir,
|
|
873
|
+
stepsDefinitions,
|
|
874
|
+
parametersMap: scenario.parametersMap,
|
|
875
|
+
});
|
|
399
876
|
if (!codePage) {
|
|
400
877
|
continue;
|
|
401
878
|
}
|
|
@@ -421,7 +898,7 @@ export function saveRoutes({ step, folderPath }) {
|
|
|
421
898
|
const oldFilters = routeItem.filters;
|
|
422
899
|
const queryParamsObject = {};
|
|
423
900
|
oldFilters.queryParams.forEach((queryParam) => {
|
|
424
|
-
queryParamsObject[queryParam.
|
|
901
|
+
queryParamsObject[queryParam.key] = queryParam.value;
|
|
425
902
|
});
|
|
426
903
|
const newFilters = { path: oldFilters.path, method: oldFilters.method, queryParams: queryParamsObject };
|
|
427
904
|
return {
|
|
@@ -64,7 +64,7 @@ const escapeNonPrintables = (text) => {
|
|
|
64
64
|
export function getCommandContent(command) {
|
|
65
65
|
switch (command.type) {
|
|
66
66
|
case "click_element": {
|
|
67
|
-
return
|
|
67
|
+
return `${command.count === 2 ? "Double click" : "Click"} on ${escapeNonPrintables(command.element.name)}`;
|
|
68
68
|
}
|
|
69
69
|
case "fill_element": {
|
|
70
70
|
return `fill ${escapeNonPrintables(command.element.name)} with ${escapeNonPrintables(command.parameters[0])}${command.parameters[1] ? ` and press enter key` : ""}`;
|
|
@@ -82,7 +82,7 @@ export function getCommandContent(command) {
|
|
|
82
82
|
return `verify the element ${escapeNonPrintables(command.element.name)} contains text ${escapeNonPrintables(command.parameters[0])}`;
|
|
83
83
|
}
|
|
84
84
|
case "context_click": {
|
|
85
|
-
return
|
|
85
|
+
return `${command.count === 2 ? "Double click" : "Click"} on ${escapeNonPrintables(command.label)} in the context of ${escapeNonPrintables(command.value)}`;
|
|
86
86
|
}
|
|
87
87
|
case "hover_element": {
|
|
88
88
|
return `hover over ${escapeNonPrintables(command.element.name)}`;
|
|
@@ -99,6 +99,9 @@ export function getCommandContent(command) {
|
|
|
99
99
|
case "verify_page_snapshot": {
|
|
100
100
|
return `verify page snapshot stored in ${command.parameters[0]}`;
|
|
101
101
|
}
|
|
102
|
+
case "parameterized_click": {
|
|
103
|
+
return `${command.count === 2 ? "Parameterized double click" : "Parameterized click"} on ${escapeNonPrintables(command.element.name)}`;
|
|
104
|
+
}
|
|
102
105
|
default: {
|
|
103
106
|
return "";
|
|
104
107
|
}
|
|
@@ -126,10 +129,14 @@ export function getExamplesContent(parametersMap) {
|
|
|
126
129
|
function getTagsContent(scenario, featureFileObject) {
|
|
127
130
|
let oldTags = featureFileObject?.scenarios?.find((s) => s.name === scenario.name)?.tags ?? [];
|
|
128
131
|
for (const tag of scenario.tags) {
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
if (tag === "global_test_data") {
|
|
133
|
+
if (!oldTags.includes("global_test_data")) {
|
|
134
|
+
oldTags.push("global_test_data");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (tag === "remove_global_test_data") {
|
|
139
|
+
oldTags = oldTags.filter((t) => t !== "global_test_data");
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
|
|
@@ -210,14 +217,12 @@ const GherkinToObject = (gherkin) => {
|
|
|
210
217
|
steps: [],
|
|
211
218
|
};
|
|
212
219
|
while (idx < lines.length && lines[idx].startsWith("@")) {
|
|
213
|
-
skipEmptyLines();
|
|
214
220
|
const tags = [...lines[idx].matchAll(/@([^@]+)/g)].map((match) => match[1].trim());
|
|
215
221
|
scenario.tags.push(...(tags ?? []));
|
|
216
222
|
idx++;
|
|
223
|
+
skipEmptyLines();
|
|
217
224
|
}
|
|
218
225
|
|
|
219
|
-
skipEmptyLines();
|
|
220
|
-
|
|
221
226
|
if (idx < lines.length && (lines[idx].startsWith("Scenario:") || lines[idx].startsWith("Scenario Outline:"))) {
|
|
222
227
|
scenario.name = lines[idx].substring(lines[idx].indexOf(":") + 1).trim();
|
|
223
228
|
idx++;
|
|
@@ -240,32 +245,32 @@ const GherkinToObject = (gherkin) => {
|
|
|
240
245
|
!lines[idx].startsWith("@")
|
|
241
246
|
) {
|
|
242
247
|
const line = lines[idx++];
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
type: "comment",
|
|
248
|
-
text: comment,
|
|
249
|
-
};
|
|
250
|
-
scenario.steps.push(command);
|
|
251
|
-
}
|
|
252
|
-
} else if (line.startsWith("Examples:")) {
|
|
253
|
-
obj.hasParams = true;
|
|
254
|
-
const command = {
|
|
248
|
+
let command;
|
|
249
|
+
if (line.startsWith("Examples:")) {
|
|
250
|
+
scenario.hasParams = true;
|
|
251
|
+
command = {
|
|
255
252
|
type: "examples",
|
|
256
253
|
lines: [],
|
|
257
254
|
};
|
|
258
|
-
|
|
259
255
|
while (idx < lines.length && lines[idx].startsWith("|")) {
|
|
260
256
|
const line = lines[idx++];
|
|
261
257
|
command.lines.push(line);
|
|
262
258
|
}
|
|
263
259
|
} else {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
260
|
+
if (line.startsWith("#")) {
|
|
261
|
+
command = {
|
|
262
|
+
type: "comment",
|
|
263
|
+
text: line,
|
|
264
|
+
};
|
|
265
|
+
} else {
|
|
266
|
+
command = {
|
|
267
|
+
type: "step",
|
|
268
|
+
text: line,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
268
271
|
}
|
|
272
|
+
scenario.steps.push(command);
|
|
273
|
+
skipEmptyLines();
|
|
269
274
|
}
|
|
270
275
|
|
|
271
276
|
return scenario;
|
|
@@ -274,7 +279,6 @@ const GherkinToObject = (gherkin) => {
|
|
|
274
279
|
while (idx < lines.length) {
|
|
275
280
|
const scenario = getScenario();
|
|
276
281
|
if (scenario === -1) break;
|
|
277
|
-
|
|
278
282
|
if (scenario.error) {
|
|
279
283
|
return {
|
|
280
284
|
error: scenario.error,
|
|
@@ -300,8 +304,7 @@ function updateExistingScenario({ featureFileContent, scenarioName, scenarioCont
|
|
|
300
304
|
skipScenarioIndex = i;
|
|
301
305
|
continue;
|
|
302
306
|
}
|
|
303
|
-
let scenarioContent = `${
|
|
304
|
-
|
|
307
|
+
let scenarioContent = `${scenario.hasParams ? "Scenario Outline" : "Scenario"}: ${scenario.name}`;
|
|
305
308
|
let tagsLine;
|
|
306
309
|
if (scenario.tags?.length > 0) {
|
|
307
310
|
tagsLine = `${scenario.tags.map((t) => `@${t}`).join(" ")}`;
|
|
@@ -324,7 +327,6 @@ function updateExistingScenario({ featureFileContent, scenarioName, scenarioCont
|
|
|
324
327
|
if (skipScenarioIndex !== -1) {
|
|
325
328
|
finalContent = results.join("\n") + "\n" + scenarioContent;
|
|
326
329
|
}
|
|
327
|
-
|
|
328
330
|
return finalContent;
|
|
329
331
|
}
|
|
330
332
|
export async function updateFeatureFile({ featureName, scenario, override, projectDir }) {
|