@dev-blinq/cucumber_client 1.0.1310-dev → 1.0.1312-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.
|
@@ -213,102 +213,107 @@ class LocatorGenerator {
|
|
|
213
213
|
return [];
|
|
214
214
|
}
|
|
215
215
|
const result = [];
|
|
216
|
-
|
|
217
|
-
for (const locator of locators) {
|
|
218
|
-
const selector = locator.css;
|
|
219
|
-
if (!selector || typeof selector !== "string") {
|
|
220
|
-
console.error("Locator must have a valid css selector");
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
const parseResult = this.injectedScript.parseSelector(selector);
|
|
224
|
-
const parts = parseResult.parts;
|
|
225
|
-
if (!parts || !Array.isArray(parts) || parts.length === 0) {
|
|
226
|
-
console.error("Locator must have a valid css selector");
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
// ignore parts.length < 3
|
|
230
|
-
if (parts.length < 3) {
|
|
231
|
-
// console.warn("Locator must have at least 3 parts to be a context locator");
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
const firstPart = parts[0];
|
|
235
|
-
if (firstPart.name !== "internal:text") {
|
|
236
|
-
// console.warn("Locator must have internal:text as the first part to be a context locator");
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
const textBody = firstPart.body;
|
|
240
|
-
if (!textBody || typeof textBody !== "string" || textBody.length === 0) {
|
|
241
|
-
console.error("Locator must have a valid text in the first part to be a context locator");
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
const secondPart = parts[1];
|
|
245
|
-
if (secondPart.name !== "xpath") {
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
const xpath = secondPart.body;
|
|
249
|
-
if (!xpath || typeof xpath !== "string" || xpath.length === 0) {
|
|
250
|
-
// console.error("Locator must have a valid xpath in the second part to be a context locator");
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
const climbString = secondPart.body;
|
|
254
|
-
if (!climbString || typeof climbString !== "string" || climbString.length === 0) {
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
const climbStringRegex = /(\.\.)(\/\.\.)*/;
|
|
258
|
-
try {
|
|
259
|
-
const match = climbStringRegex.test(climbString);
|
|
260
|
-
if (match) {
|
|
261
|
-
const climbCount = climbString.split("..").length - 1;
|
|
262
|
-
const lastIndex = selector.indexOf(climbString);
|
|
263
|
-
const restOfSelector = selector.substring(lastIndex + climbString.length + 3).trim();
|
|
264
|
-
if (restOfSelector.length === 0) {
|
|
265
|
-
// console.warn("Locator must have a valid rest of selector after the xpath part to
|
|
266
|
-
// be a context locator");
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
216
|
+
try {
|
|
269
217
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
218
|
+
const textSet = new Set();
|
|
219
|
+
for (const locator of locators) {
|
|
220
|
+
const selector = locator.css;
|
|
221
|
+
if (!selector || typeof selector !== "string") {
|
|
222
|
+
console.error("Locator must have a valid css selector");
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const parseResult = this.injectedScript.parseSelector(selector);
|
|
226
|
+
const parts = parseResult.parts;
|
|
227
|
+
if (!parts || !Array.isArray(parts) || parts.length === 0) {
|
|
228
|
+
console.error("Locator must have a valid css selector");
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
// ignore parts.length < 3
|
|
232
|
+
if (parts.length < 3) {
|
|
233
|
+
// console.warn("Locator must have at least 3 parts to be a context locator");
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const firstPart = parts[0];
|
|
237
|
+
if (firstPart.name !== "internal:text") {
|
|
238
|
+
// console.warn("Locator must have internal:text as the first part to be a context locator");
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const textBody = firstPart.body;
|
|
242
|
+
if (!textBody || typeof textBody !== "string" || textBody.length === 0) {
|
|
243
|
+
console.error("Locator must have a valid text in the first part to be a context locator");
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const secondPart = parts[1];
|
|
247
|
+
if (secondPart.name !== "xpath") {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const xpath = secondPart.body;
|
|
251
|
+
if (!xpath || typeof xpath !== "string" || xpath.length === 0) {
|
|
252
|
+
// console.error("Locator must have a valid xpath in the second part to be a context locator");
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const climbString = secondPart.body;
|
|
256
|
+
if (!climbString || typeof climbString !== "string" || climbString.length === 0) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const climbStringRegex = /(\.\.)(\/\.\.)*/;
|
|
260
|
+
try {
|
|
261
|
+
const match = climbStringRegex.test(climbString);
|
|
262
|
+
if (match) {
|
|
263
|
+
const climbCount = climbString.split("..").length - 1;
|
|
264
|
+
const lastIndex = selector.indexOf(climbString);
|
|
265
|
+
const restOfSelector = selector.substring(lastIndex + climbString.length + 3).trim();
|
|
266
|
+
if (restOfSelector.length === 0) {
|
|
267
|
+
// console.warn("Locator must have a valid rest of selector after the xpath part to
|
|
268
|
+
// be a context locator");
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const textLocator = `internal:text=${textBody}`;
|
|
273
|
+
const elements = this.getMatchingElements(textLocator, {});
|
|
274
|
+
if (elements.length !== 1) {
|
|
275
|
+
// throw new Error("Context locator must have exactly one element matching the text part");
|
|
276
|
+
console.error("Context locator must have exactly one element matching the text part");
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const textElement = elements[0];
|
|
280
|
+
// const text = this.PW.selectorUtils.elementText(textElement);
|
|
281
|
+
const text = this.injectedScript.utils.elementText(new Map(), textElement).full;
|
|
282
|
+
|
|
283
|
+
const fullSelector = `${textLocator} >> xpath=${xpath} >> ${restOfSelector}`;
|
|
284
|
+
const fullElements = this.getMatchingElements(fullSelector, {});
|
|
285
|
+
if (fullElements.length !== 1) {
|
|
286
|
+
// throw new Error("Context locator must have exactly one element matching the full selector");
|
|
287
|
+
console.error("Context locator must have exactly one element matching the full selector");
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const fullElement = fullElements[0];
|
|
291
|
+
if (fullElement !== element) {
|
|
292
|
+
// throw new Error("Context locator must have the text element as the full element");
|
|
293
|
+
console.error("Context locator must have the text element as the full element");
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (!textSet.has(text)) {
|
|
297
|
+
textSet.add(text);
|
|
298
|
+
const loc = {
|
|
299
|
+
css: restOfSelector,
|
|
300
|
+
climb: climbCount,
|
|
301
|
+
text,
|
|
302
|
+
priority: 1,
|
|
303
|
+
};
|
|
304
|
+
if (locator.index !== undefined) {
|
|
305
|
+
loc.index = locator.index;
|
|
306
|
+
}
|
|
307
|
+
result.push(loc);
|
|
304
308
|
}
|
|
305
|
-
result.push(loc);
|
|
306
309
|
}
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.error("Error parsing climb string:", error);
|
|
312
|
+
continue;
|
|
307
313
|
}
|
|
308
|
-
} catch (error) {
|
|
309
|
-
console.error("Error parsing climb string:", error);
|
|
310
|
-
continue;
|
|
311
314
|
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.error("Error generating context locators:", error);
|
|
312
317
|
}
|
|
313
318
|
// Sort by text length to prioritize shorter texts
|
|
314
319
|
result.sort((a, b) => a.text.length - b.text.length);
|
|
@@ -316,65 +321,70 @@ class LocatorGenerator {
|
|
|
316
321
|
}
|
|
317
322
|
getDigitIgnoreLocators(element, locators) {
|
|
318
323
|
const result = [];
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
for (const locator of locators) {
|
|
325
|
-
const selector = locator.css;
|
|
326
|
-
if (!selector || typeof selector !== "string") {
|
|
327
|
-
console.error("Locator must have a valid css selector");
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
const parseresult = this.injectedScript.parseSelector(selector);
|
|
331
|
-
const parts = parseresult.parts;
|
|
332
|
-
if (!parts || !Array.isArray(parts) || parts.length === 0) {
|
|
333
|
-
console.error("Locator must have a valid css selector");
|
|
334
|
-
continue;
|
|
324
|
+
try {
|
|
325
|
+
if (!locators || !Array.isArray(locators)) {
|
|
326
|
+
console.error("Locators must be an array");
|
|
327
|
+
return [];
|
|
335
328
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if (
|
|
340
|
-
|
|
329
|
+
|
|
330
|
+
for (const locator of locators) {
|
|
331
|
+
const selector = locator.css;
|
|
332
|
+
if (!selector || typeof selector !== "string") {
|
|
333
|
+
console.error("Locator must have a valid css selector");
|
|
341
334
|
continue;
|
|
342
335
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
336
|
+
const parseresult = this.injectedScript.parseSelector(selector);
|
|
337
|
+
const parts = parseresult.parts;
|
|
338
|
+
if (!parts || !Array.isArray(parts) || parts.length === 0) {
|
|
339
|
+
console.error("Locator must have a valid css selector");
|
|
346
340
|
continue;
|
|
347
341
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
342
|
+
let finalSelector = "";
|
|
343
|
+
let hasDigitsInText = false;
|
|
344
|
+
for (const part of parts) {
|
|
345
|
+
if (part.name !== "internal:text") {
|
|
346
|
+
finalSelector += `${part.name === "css" ? "" : part.name + "="}${part.source} >> `;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (typeof part.body !== "string" || part.body.length === 0) {
|
|
350
|
+
// console.error("Locator must have a valid text in the first part to be a digit ignore locator");
|
|
351
|
+
finalSelector += `${part.name === "css" ? "" : part.name + "="}${part.source} >> `;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const text = part.body;
|
|
355
|
+
const digitsRegex = /\d+/g;
|
|
356
|
+
hasDigitsInText = digitsRegex.test(text);
|
|
351
357
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
if (!hasDigitsInText) {
|
|
357
|
-
continue;
|
|
358
|
-
}
|
|
359
|
-
if (finalSelector.endsWith(` >> `)) {
|
|
360
|
-
finalSelector = finalSelector.slice(0, -4);
|
|
361
|
-
}
|
|
362
|
-
if (finalSelector) {
|
|
363
|
-
const elements = this.getMatchingElements(finalSelector, {});
|
|
364
|
-
if (elements.length !== 1) {
|
|
365
|
-
console.error("Digit ignore locator must have exactly one element matching the final selector");
|
|
366
|
-
continue;
|
|
358
|
+
let pattern = this.PW.stringUtils.escapeRegExp(text.substring(1, text.length - 2));
|
|
359
|
+
pattern = pattern.replace(digitsRegex, "\\d+");
|
|
360
|
+
finalSelector += `internal:text=/${pattern}/ >> `;
|
|
367
361
|
}
|
|
368
|
-
if (
|
|
369
|
-
console.error("Digit ignore locator must match the original element");
|
|
362
|
+
if (!hasDigitsInText) {
|
|
370
363
|
continue;
|
|
371
364
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
365
|
+
if (finalSelector.endsWith(` >> `)) {
|
|
366
|
+
finalSelector = finalSelector.slice(0, -4);
|
|
367
|
+
}
|
|
368
|
+
if (finalSelector) {
|
|
369
|
+
const elements = this.getMatchingElements(finalSelector, {});
|
|
370
|
+
if (elements.length !== 1) {
|
|
371
|
+
console.error("Digit ignore locator must have exactly one element matching the final selector");
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (elements[0] !== element) {
|
|
375
|
+
console.error("Digit ignore locator must match the original element");
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
result.push({
|
|
379
|
+
css: finalSelector,
|
|
380
|
+
priority: locator.priority || 1,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
376
383
|
}
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.error("Error generating digit ignore locators:", error);
|
|
377
386
|
}
|
|
387
|
+
|
|
378
388
|
return result;
|
|
379
389
|
}
|
|
380
390
|
getTextwithIndexLocators(locators) {
|
|
@@ -383,17 +393,22 @@ class LocatorGenerator {
|
|
|
383
393
|
return [];
|
|
384
394
|
}
|
|
385
395
|
const result = [];
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
396
|
+
try {
|
|
397
|
+
for (const locator of locators) {
|
|
398
|
+
if (!locator || !locator.css || typeof locator.css !== "string") {
|
|
399
|
+
console.error("Locator must have a valid css selector");
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
const index = locator.index;
|
|
403
|
+
if (typeof index !== "number" || index < 0) {
|
|
404
|
+
// console.error("Locator must have a valid index");
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
result.push(locator);
|
|
395
408
|
}
|
|
396
|
-
|
|
409
|
+
|
|
410
|
+
} catch (error) {
|
|
411
|
+
console.error("Error getting text with index locators:", error);
|
|
397
412
|
}
|
|
398
413
|
return result;
|
|
399
414
|
}
|
|
@@ -417,28 +432,33 @@ class LocatorGenerator {
|
|
|
417
432
|
categorizeLocators(element, locators, options) {
|
|
418
433
|
const unique = [];
|
|
419
434
|
const nonUnique = [];
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
435
|
+
try {
|
|
436
|
+
|
|
437
|
+
for (const locator of locators) {
|
|
438
|
+
const elements = this.getMatchingElements(locator.css, options);
|
|
439
|
+
if (elements.length === 0) {
|
|
440
|
+
console.warn(`No elements found for locator: ${locator.css}`);
|
|
441
|
+
continue;
|
|
442
|
+
} else if (elements.length === 1) {
|
|
443
|
+
if (element === elements[0]) {
|
|
444
|
+
locator.priority = 1;
|
|
445
|
+
unique.push(locator);
|
|
446
|
+
} else if (element.contains(elements[0])) {
|
|
447
|
+
locator.priority = 1;
|
|
448
|
+
const climb = this.dom_Parent.getClimbCountToParent(elements[0], element);
|
|
449
|
+
const climbSelector = this.getXPathSelector(climb);
|
|
450
|
+
const newSelector = `${locator.css} >> ${climbSelector}`;
|
|
451
|
+
locator.css = newSelector;
|
|
452
|
+
unique.push(locator);
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
locator.priority = 2;
|
|
456
|
+
locator.elements = elements;
|
|
457
|
+
nonUnique.push(locator);
|
|
436
458
|
}
|
|
437
|
-
} else {
|
|
438
|
-
locator.priority = 2;
|
|
439
|
-
locator.elements = elements;
|
|
440
|
-
nonUnique.push(locator);
|
|
441
459
|
}
|
|
460
|
+
} catch (error) {
|
|
461
|
+
console.error("Error categorizing locators:", error);
|
|
442
462
|
}
|
|
443
463
|
return { unique, nonUnique };
|
|
444
464
|
}
|
|
@@ -19,9 +19,7 @@ const elements = {};
|
|
|
19
19
|
|
|
20
20
|
let context = null;
|
|
21
21
|
Before(async function (scenario) {
|
|
22
|
-
|
|
23
|
-
context = await initContext(url, false, false, this);
|
|
24
|
-
}
|
|
22
|
+
context = await initContext(url, false, false, this);
|
|
25
23
|
await navigate(url);
|
|
26
24
|
await context.web.beforeScenario(this, scenario);
|
|
27
25
|
});
|
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.1312-dev",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "bin/index.js",
|
|
6
6
|
"types": "bin/index.d.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@cucumber/tag-expressions": "^6.1.1",
|
|
33
33
|
"@dev-blinq/cucumber-js": "1.0.177-dev",
|
|
34
34
|
"@faker-js/faker": "^8.1.0",
|
|
35
|
-
"automation_model": "1.0.
|
|
35
|
+
"automation_model": "1.0.773-dev",
|
|
36
36
|
"axios": "^1.7.4",
|
|
37
37
|
"chokidar": "^3.6.0",
|
|
38
38
|
"create-require": "^1.1.1",
|