pomwright 1.1.0 → 1.2.0
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/CHANGELOG.md +589 -0
- package/README.md +228 -43
- package/dist/index.d.mts +524 -128
- package/dist/index.d.ts +524 -128
- package/dist/index.js +478 -277
- package/dist/index.mjs +467 -266
- package/index.ts +10 -4
- package/package.json +22 -9
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var POMWright_exports = {};
|
|
22
22
|
__export(POMWright_exports, {
|
|
23
23
|
BaseApi: () => BaseApi,
|
|
24
|
-
BasePage: () =>
|
|
24
|
+
BasePage: () => BasePage,
|
|
25
25
|
GetByMethod: () => GetByMethod,
|
|
26
26
|
GetLocatorBase: () => GetLocatorBase,
|
|
27
27
|
PlaywrightReportLogger: () => PlaywrightReportLogger,
|
|
@@ -30,12 +30,9 @@ __export(POMWright_exports, {
|
|
|
30
30
|
module.exports = __toCommonJS(POMWright_exports);
|
|
31
31
|
|
|
32
32
|
// src/basePage.ts
|
|
33
|
-
var
|
|
33
|
+
var import_test3 = require("@playwright/test");
|
|
34
34
|
|
|
35
35
|
// src/helpers/getLocatorBase.ts
|
|
36
|
-
var import_test2 = require("@playwright/test");
|
|
37
|
-
|
|
38
|
-
// src/helpers/getBy.locator.ts
|
|
39
36
|
var import_test = require("@playwright/test");
|
|
40
37
|
|
|
41
38
|
// src/helpers/locatorSchema.interface.ts
|
|
@@ -97,6 +94,12 @@ var locatorSchemaDummy = {
|
|
|
97
94
|
testId: void 0,
|
|
98
95
|
dataCy: void 0,
|
|
99
96
|
id: void 0,
|
|
97
|
+
filter: {
|
|
98
|
+
has: void 0,
|
|
99
|
+
hasNot: void 0,
|
|
100
|
+
hasNotText: void 0,
|
|
101
|
+
hasText: void 0
|
|
102
|
+
},
|
|
100
103
|
locatorMethod: void 0,
|
|
101
104
|
locatorSchemaPath: void 0
|
|
102
105
|
};
|
|
@@ -104,147 +107,6 @@ function getLocatorSchemaDummy() {
|
|
|
104
107
|
return locatorSchemaDummy;
|
|
105
108
|
}
|
|
106
109
|
|
|
107
|
-
// src/helpers/playwrightReportLogger.ts
|
|
108
|
-
var PlaywrightReportLogger = class _PlaywrightReportLogger {
|
|
109
|
-
// Initializes the logger with shared log level, log entries, and a context name.
|
|
110
|
-
constructor(sharedLogLevel, sharedLogEntry, contextName) {
|
|
111
|
-
this.sharedLogLevel = sharedLogLevel;
|
|
112
|
-
this.sharedLogEntry = sharedLogEntry;
|
|
113
|
-
this.contextName = contextName;
|
|
114
|
-
}
|
|
115
|
-
contextName;
|
|
116
|
-
logLevels = ["debug", "info", "warn", "error"];
|
|
117
|
-
/**
|
|
118
|
-
* Creates a child logger with a new contextual name, sharing the same log level and log entries with the parent logger.
|
|
119
|
-
*
|
|
120
|
-
* The root loggers log "level" is referenced by all child loggers and their child loggers and so on...
|
|
121
|
-
* Changing the log "level" of one, will change it for all.
|
|
122
|
-
*/
|
|
123
|
-
getNewChildLogger(prefix) {
|
|
124
|
-
return new _PlaywrightReportLogger(this.sharedLogLevel, this.sharedLogEntry, `${this.contextName} -> ${prefix}`);
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Logs a message with the specified log level, prefix, and additional arguments if the current log level permits.
|
|
128
|
-
*/
|
|
129
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
130
|
-
log(level, message, ...args) {
|
|
131
|
-
const logLevelIndex = this.logLevels.indexOf(level);
|
|
132
|
-
if (logLevelIndex < this.getCurrentLogLevelIndex()) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
this.sharedLogEntry.push({
|
|
136
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
137
|
-
logLevel: level,
|
|
138
|
-
prefix: this.contextName,
|
|
139
|
-
message: `${message}
|
|
140
|
-
|
|
141
|
-
${args.join("\n\n")}`
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Logs a debug-level message with the specified message and arguments.
|
|
146
|
-
*/
|
|
147
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
148
|
-
debug(message, ...args) {
|
|
149
|
-
this.log("debug", message, ...args);
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Logs a info-level message with the specified message and arguments.
|
|
153
|
-
*/
|
|
154
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
155
|
-
info(message, ...args) {
|
|
156
|
-
this.log("info", message, ...args);
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Logs a warn-level message with the specified message and arguments.
|
|
160
|
-
*/
|
|
161
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
162
|
-
warn(message, ...args) {
|
|
163
|
-
this.log("warn", message, ...args);
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Logs a error-level message with the specified message and arguments.
|
|
167
|
-
*/
|
|
168
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
169
|
-
error(message, ...args) {
|
|
170
|
-
this.log("error", message, ...args);
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Sets the current log level to the specified level during runTime.
|
|
174
|
-
*/
|
|
175
|
-
setLogLevel(level) {
|
|
176
|
-
this.sharedLogLevel.current = level;
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Retrieves the current log level during runtime.
|
|
180
|
-
*/
|
|
181
|
-
getCurrentLogLevel() {
|
|
182
|
-
return this.sharedLogLevel.current;
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Retrieves the index of the current log level in the logLevels array during runtime.
|
|
186
|
-
*/
|
|
187
|
-
getCurrentLogLevelIndex() {
|
|
188
|
-
return this.logLevels.indexOf(this.sharedLogLevel.current);
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Resets the current log level to the initial level during runtime.
|
|
192
|
-
*/
|
|
193
|
-
resetLogLevel() {
|
|
194
|
-
this.sharedLogLevel.current = this.sharedLogLevel.initial;
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Checks if the input log level is equal to the current log level of the PlaywrightReportLogger instance.
|
|
198
|
-
*/
|
|
199
|
-
isCurrentLogLevel(level) {
|
|
200
|
-
return this.sharedLogLevel.current === level;
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Returns 'true' if the "level" parameter provided has an equal or greater index than the current logLevel.
|
|
204
|
-
*/
|
|
205
|
-
isLogLevelEnabled(level) {
|
|
206
|
-
const logLevelIndex = this.logLevels.indexOf(level);
|
|
207
|
-
if (logLevelIndex < this.getCurrentLogLevelIndex()) {
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Attaches the recorded log entries to the Playwright HTML report in a sorted and formatted manner.
|
|
214
|
-
*/
|
|
215
|
-
attachLogsToTest(testInfo) {
|
|
216
|
-
this.sharedLogEntry.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
217
|
-
for (const log of this.sharedLogEntry) {
|
|
218
|
-
const printTime = log.timestamp.toLocaleTimeString("nb-NO", {
|
|
219
|
-
hour: "2-digit",
|
|
220
|
-
minute: "2-digit",
|
|
221
|
-
second: "2-digit"
|
|
222
|
-
});
|
|
223
|
-
const printDate = log.timestamp.toLocaleDateString("nb-NO", {
|
|
224
|
-
day: "2-digit",
|
|
225
|
-
month: "2-digit",
|
|
226
|
-
year: "numeric"
|
|
227
|
-
});
|
|
228
|
-
const printLogLevel = `${log.logLevel.toUpperCase()}`;
|
|
229
|
-
const printPrefix = log.prefix ? `: [${log.prefix}]` : "";
|
|
230
|
-
let messageBody = "";
|
|
231
|
-
let messageContentType = "";
|
|
232
|
-
try {
|
|
233
|
-
const parsedMessage = JSON.parse(log.message);
|
|
234
|
-
messageContentType = "application/json";
|
|
235
|
-
messageBody = JSON.stringify(parsedMessage, null, 2);
|
|
236
|
-
} catch (error) {
|
|
237
|
-
messageContentType = "text/plain";
|
|
238
|
-
messageBody = log.message;
|
|
239
|
-
}
|
|
240
|
-
testInfo.attach(`${printTime} ${printDate} - ${printLogLevel} ${printPrefix}`, {
|
|
241
|
-
contentType: messageContentType,
|
|
242
|
-
body: Buffer.from(messageBody)
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
|
|
248
110
|
// src/helpers/getBy.locator.ts
|
|
249
111
|
var GetBy = class {
|
|
250
112
|
constructor(page, pwrl) {
|
|
@@ -410,52 +272,72 @@ var GetBy = class {
|
|
|
410
272
|
var REQUIRED_PROPERTIES_FOR_LOCATOR_SCHEMA_WITH_METHODS = [
|
|
411
273
|
"update",
|
|
412
274
|
"updates",
|
|
275
|
+
"addFilter",
|
|
413
276
|
"getNestedLocator",
|
|
414
277
|
"getLocator",
|
|
415
278
|
"locatorSchemaPath",
|
|
416
279
|
"locatorMethod",
|
|
417
|
-
"schemasMap"
|
|
280
|
+
"schemasMap",
|
|
281
|
+
"filterMap"
|
|
418
282
|
];
|
|
283
|
+
var safeStringifyOfNestedLocatorResults = (obj) => {
|
|
284
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
285
|
+
return JSON.stringify(
|
|
286
|
+
obj,
|
|
287
|
+
(key, value) => {
|
|
288
|
+
if (value instanceof Map) {
|
|
289
|
+
return Array.from(value.entries());
|
|
290
|
+
}
|
|
291
|
+
if (value instanceof RegExp) {
|
|
292
|
+
return { type: "RegExp", source: value.source, flags: value.flags };
|
|
293
|
+
}
|
|
294
|
+
if (value && typeof value === "object" && value.constructor && value.constructor.name === "Locator") {
|
|
295
|
+
return { type: "Locator", note: "Custom placeholder - Locators are complex." };
|
|
296
|
+
}
|
|
297
|
+
if (typeof value === "object" && value !== null) {
|
|
298
|
+
if (seen.has(value)) return "[Circular]";
|
|
299
|
+
seen.add(value);
|
|
300
|
+
}
|
|
301
|
+
return value;
|
|
302
|
+
},
|
|
303
|
+
2
|
|
304
|
+
);
|
|
305
|
+
};
|
|
419
306
|
var GetLocatorBase = class {
|
|
420
|
-
|
|
421
|
-
* Initializes the GetLocatorBase class with a page object class and a logger.
|
|
422
|
-
*/
|
|
423
|
-
constructor(pageObjectClass, log) {
|
|
307
|
+
constructor(pageObjectClass, log, locatorSubstring) {
|
|
424
308
|
this.pageObjectClass = pageObjectClass;
|
|
425
309
|
this.log = log;
|
|
310
|
+
this.locatorSubstring = locatorSubstring;
|
|
426
311
|
this.locatorSchemas = /* @__PURE__ */ new Map();
|
|
427
312
|
this.getBy = new GetBy(this.pageObjectClass.page, this.log.getNewChildLogger("GetBy"));
|
|
428
313
|
}
|
|
429
314
|
getBy;
|
|
430
315
|
locatorSchemas;
|
|
431
316
|
/**
|
|
432
|
-
*
|
|
317
|
+
* getLocatorSchema:
|
|
318
|
+
* Given a path P, we:
|
|
319
|
+
* 1. Collect deep copies of the schemas involved.
|
|
320
|
+
* 2. Create a WithMethodsClass instance with LocatorSubstring = P.
|
|
321
|
+
* 3. Return a locator schema copy enriched with chainable methods.
|
|
433
322
|
*/
|
|
434
323
|
getLocatorSchema(locatorSchemaPath) {
|
|
435
324
|
const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath);
|
|
436
325
|
const schemasMap = this.collectDeepCopies(locatorSchemaPath, pathIndexPairs);
|
|
437
326
|
const locatorSchemaCopy = schemasMap.get(locatorSchemaPath);
|
|
438
327
|
locatorSchemaCopy.schemasMap = schemasMap;
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
};
|
|
448
|
-
locatorSchemaCopy.getNestedLocator = async (indices) => {
|
|
449
|
-
return await this.buildNestedLocator(locatorSchemaPath, schemasMap, indices);
|
|
450
|
-
};
|
|
451
|
-
locatorSchemaCopy.getLocator = async () => {
|
|
452
|
-
return this.getBy.getLocator(locatorSchemaCopy);
|
|
453
|
-
};
|
|
454
|
-
return locatorSchemaCopy;
|
|
328
|
+
locatorSchemaCopy.filterMap = /* @__PURE__ */ new Map();
|
|
329
|
+
const wrapper = new WithMethodsClass(
|
|
330
|
+
this.pageObjectClass,
|
|
331
|
+
this.log,
|
|
332
|
+
locatorSchemaPath,
|
|
333
|
+
schemasMap
|
|
334
|
+
);
|
|
335
|
+
return wrapper.init(locatorSchemaPath, locatorSchemaCopy);
|
|
455
336
|
}
|
|
456
337
|
/**
|
|
457
|
-
*
|
|
458
|
-
*
|
|
338
|
+
* collectDeepCopies:
|
|
339
|
+
* Clones and stores all schemas related to the chosen path and its sub-paths.
|
|
340
|
+
* Ensures updates and filters don't affect original schema definitions.
|
|
459
341
|
*/
|
|
460
342
|
collectDeepCopies(locatorSchemaPath, pathIndexPairs) {
|
|
461
343
|
const schemasMap = /* @__PURE__ */ new Map();
|
|
@@ -480,8 +362,25 @@ var GetLocatorBase = class {
|
|
|
480
362
|
return REQUIRED_PROPERTIES_FOR_LOCATOR_SCHEMA_WITH_METHODS.every((p) => p in schema);
|
|
481
363
|
}
|
|
482
364
|
/**
|
|
483
|
-
*
|
|
484
|
-
*
|
|
365
|
+
* applyUpdateToSubPath:
|
|
366
|
+
* Applies updates to a specific sub-path schema within schemasMap.
|
|
367
|
+
* Similar to applyUpdate, but we locate the sub-path schema directly by its path.
|
|
368
|
+
*/
|
|
369
|
+
applyUpdateToSubPath(schemasMap, subPath, updates) {
|
|
370
|
+
const schema = schemasMap.get(subPath);
|
|
371
|
+
if (!schema) {
|
|
372
|
+
throw new Error(`No schema found for sub-path: '${subPath}'`);
|
|
373
|
+
}
|
|
374
|
+
const updatedSchema = this.deepMerge(schema, updates);
|
|
375
|
+
if (this.isLocatorSchemaWithMethods(schema)) {
|
|
376
|
+
Object.assign(schema, updatedSchema);
|
|
377
|
+
} else {
|
|
378
|
+
schemasMap.set(subPath, updatedSchema);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* applyUpdate:
|
|
383
|
+
* Applies updates to a single schema within the schemasMap.
|
|
485
384
|
*/
|
|
486
385
|
applyUpdate(schemasMap, locatorSchemaPath, updateData) {
|
|
487
386
|
const schema = schemasMap.get(locatorSchemaPath);
|
|
@@ -495,12 +394,12 @@ var GetLocatorBase = class {
|
|
|
495
394
|
}
|
|
496
395
|
}
|
|
497
396
|
/**
|
|
498
|
-
*
|
|
499
|
-
*
|
|
397
|
+
* applyUpdates:
|
|
398
|
+
* Applies multiple updates to multiple schemas in the chain, identified by their path indexes.
|
|
500
399
|
*/
|
|
501
400
|
applyUpdates(schemasMap, pathIndexPairs, updatesData) {
|
|
502
401
|
for (const [index, updateAtIndex] of Object.entries(updatesData)) {
|
|
503
|
-
const path = pathIndexPairs[parseInt(index)]?.path;
|
|
402
|
+
const path = pathIndexPairs[Number.parseInt(index)]?.path;
|
|
504
403
|
if (path && updateAtIndex) {
|
|
505
404
|
const schema = schemasMap.get(path);
|
|
506
405
|
if (schema) {
|
|
@@ -515,16 +414,17 @@ var GetLocatorBase = class {
|
|
|
515
414
|
}
|
|
516
415
|
}
|
|
517
416
|
/**
|
|
518
|
-
*
|
|
519
|
-
*
|
|
417
|
+
* createLocatorSchema:
|
|
418
|
+
* Creates a fresh LocatorSchema object by merging provided schemaDetails with a required locatorSchemaPath.
|
|
520
419
|
*/
|
|
521
420
|
createLocatorSchema(schemaDetails, locatorSchemaPath) {
|
|
522
421
|
const schema = { ...schemaDetails, locatorSchemaPath };
|
|
523
422
|
return schema;
|
|
524
423
|
}
|
|
525
424
|
/**
|
|
526
|
-
*
|
|
527
|
-
*
|
|
425
|
+
* addSchema:
|
|
426
|
+
* Registers a new LocatorSchema under the given locatorSchemaPath.
|
|
427
|
+
* Throws an error if a schema already exists at that path.
|
|
528
428
|
*/
|
|
529
429
|
addSchema(locatorSchemaPath, schemaDetails) {
|
|
530
430
|
const newLocatorSchema = this.createLocatorSchema(schemaDetails, locatorSchemaPath);
|
|
@@ -540,16 +440,16 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
540
440
|
this.locatorSchemas.set(locatorSchemaPath, () => newLocatorSchema);
|
|
541
441
|
}
|
|
542
442
|
/**
|
|
543
|
-
*
|
|
544
|
-
*
|
|
443
|
+
* safeGetLocatorSchema:
|
|
444
|
+
* Safely retrieves a schema function if available for the given path.
|
|
545
445
|
*/
|
|
546
446
|
safeGetLocatorSchema(path) {
|
|
547
447
|
return this.locatorSchemas.get(path);
|
|
548
448
|
}
|
|
549
449
|
/**
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
450
|
+
* extractPathsFromSchema:
|
|
451
|
+
* Splits a path into incremental sub-paths and associates them with optional indices.
|
|
452
|
+
* Used by updates and getNestedLocator methods.
|
|
553
453
|
*/
|
|
554
454
|
extractPathsFromSchema = (paths, indices = {}) => {
|
|
555
455
|
const schemaParts = paths.split(".");
|
|
@@ -563,8 +463,8 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
563
463
|
});
|
|
564
464
|
};
|
|
565
465
|
/**
|
|
566
|
-
* logError
|
|
567
|
-
*
|
|
466
|
+
* logError:
|
|
467
|
+
* Logs detailed error information and re-throws the error to ensure tests fail as expected.
|
|
568
468
|
*/
|
|
569
469
|
logError = (error, locatorSchemaPath, currentLocator, currentPath, pathIndexPairs, nestedLocatorResults) => {
|
|
570
470
|
const errorDetails = {
|
|
@@ -576,7 +476,7 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
576
476
|
locatorString: currentLocator,
|
|
577
477
|
isNotNull: true
|
|
578
478
|
} : { isNotNull: false },
|
|
579
|
-
nestedLocatorResults
|
|
479
|
+
nestedLocatorResults: safeStringifyOfNestedLocatorResults(nestedLocatorResults)
|
|
580
480
|
};
|
|
581
481
|
this.log.error(
|
|
582
482
|
"An error occurred during nested locator construction.\n",
|
|
@@ -585,7 +485,11 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
585
485
|
);
|
|
586
486
|
throw error;
|
|
587
487
|
};
|
|
588
|
-
/**
|
|
488
|
+
/**
|
|
489
|
+
* deepMerge:
|
|
490
|
+
* Recursively merges source properties into target, validating them against LocatorSchema to ensure no invalid keys.
|
|
491
|
+
* Ensures immutability by creating a new object rather than modifying in place.
|
|
492
|
+
*/
|
|
589
493
|
deepMerge(target, source, schema = getLocatorSchemaDummy()) {
|
|
590
494
|
const merged = { ...target };
|
|
591
495
|
for (const key of Object.keys(source)) {
|
|
@@ -610,7 +514,6 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
610
514
|
} else {
|
|
611
515
|
merged[key] = this.deepMerge(
|
|
612
516
|
{},
|
|
613
|
-
// Updated type here
|
|
614
517
|
sourceValue,
|
|
615
518
|
schema[key]
|
|
616
519
|
);
|
|
@@ -631,52 +534,60 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
631
534
|
return merged;
|
|
632
535
|
}
|
|
633
536
|
/**
|
|
634
|
-
*
|
|
635
|
-
*
|
|
636
|
-
*
|
|
537
|
+
* buildNestedLocator:
|
|
538
|
+
* Constructs a nested locator by iterating through each sub-path of locatorSchemaPath and chaining locators.
|
|
539
|
+
* Applies filters, indexing (nth), and logs details for debugging during test retries.
|
|
637
540
|
*/
|
|
638
|
-
buildNestedLocator = async (locatorSchemaPath, schemasMap, indices = {}) => {
|
|
639
|
-
return await
|
|
541
|
+
buildNestedLocator = async (locatorSchemaPath, schemasMap, filterMap, indices = {}) => {
|
|
542
|
+
return await import_test.test.step(`${this.pageObjectClass.pocName}: Build Nested Locator`, async () => {
|
|
640
543
|
const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath, indices);
|
|
641
544
|
let currentLocator = null;
|
|
642
545
|
let currentIFrame = null;
|
|
643
546
|
const nestedLocatorResults = {
|
|
644
547
|
LocatorSchema: null,
|
|
645
|
-
// Initialize as an empty object
|
|
646
548
|
NestingSteps: []
|
|
647
549
|
};
|
|
648
550
|
for (const { path, index } of pathIndexPairs) {
|
|
649
551
|
const currentSchema = schemasMap.get(path);
|
|
650
|
-
if (!currentSchema)
|
|
651
|
-
continue;
|
|
552
|
+
if (!currentSchema) continue;
|
|
652
553
|
try {
|
|
653
554
|
const nextLocator = this.getBy.getLocator(currentSchema);
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
555
|
+
currentLocator = currentLocator ? currentLocator.locator(nextLocator) : nextLocator;
|
|
556
|
+
if (currentSchema.locatorMethod !== "frameLocator" /* frameLocator */ && currentSchema.filter) {
|
|
557
|
+
currentLocator = currentLocator.filter({
|
|
558
|
+
has: currentSchema.filter.has,
|
|
559
|
+
hasNot: currentSchema.filter.hasNot,
|
|
560
|
+
hasNotText: currentSchema.filter.hasNotText,
|
|
561
|
+
hasText: currentSchema.filter.hasText
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
const filterEntries = filterMap.get(path);
|
|
565
|
+
if (filterEntries) {
|
|
566
|
+
for (const filterData of filterEntries) {
|
|
567
|
+
currentLocator = currentLocator.filter({
|
|
568
|
+
has: filterData.has,
|
|
569
|
+
hasNot: filterData.hasNot,
|
|
570
|
+
hasNotText: filterData.hasNotText,
|
|
571
|
+
hasText: filterData.hasText
|
|
572
|
+
});
|
|
663
573
|
}
|
|
664
574
|
}
|
|
575
|
+
if (index != null) {
|
|
576
|
+
currentLocator = currentLocator.nth(index);
|
|
577
|
+
}
|
|
665
578
|
if (this.log.isLogLevelEnabled("debug")) {
|
|
666
579
|
if (!nestedLocatorResults.LocatorSchema) {
|
|
667
|
-
const
|
|
668
|
-
if (
|
|
669
|
-
nestedLocatorResults.LocatorSchema =
|
|
580
|
+
const schemaFromMap = schemasMap.get(locatorSchemaPath);
|
|
581
|
+
if (schemaFromMap) {
|
|
582
|
+
nestedLocatorResults.LocatorSchema = schemaFromMap;
|
|
670
583
|
}
|
|
671
584
|
}
|
|
672
585
|
if (currentSchema.locatorMethod === "frameLocator" /* frameLocator */) {
|
|
673
586
|
if (!currentIFrame) {
|
|
674
587
|
currentIFrame = currentSchema.frameLocator;
|
|
675
588
|
}
|
|
676
|
-
if (currentIFrame
|
|
677
|
-
|
|
678
|
-
currentIFrame += ` -> ${currentSchema.frameLocator}`;
|
|
679
|
-
}
|
|
589
|
+
if (currentIFrame && currentSchema.frameLocator && currentIFrame.endsWith(currentSchema.frameLocator)) {
|
|
590
|
+
currentIFrame += ` -> ${currentSchema.frameLocator}`;
|
|
680
591
|
}
|
|
681
592
|
}
|
|
682
593
|
if (currentIFrame !== void 0) {
|
|
@@ -700,7 +611,7 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
700
611
|
);
|
|
701
612
|
}
|
|
702
613
|
if (this.log.isLogLevelEnabled("debug")) {
|
|
703
|
-
this.log.debug("Nested locator evaluation results:",
|
|
614
|
+
this.log.debug("Nested locator evaluation results:", safeStringifyOfNestedLocatorResults(nestedLocatorResults));
|
|
704
615
|
}
|
|
705
616
|
if (currentLocator != null) {
|
|
706
617
|
return currentLocator;
|
|
@@ -708,7 +619,9 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
708
619
|
});
|
|
709
620
|
};
|
|
710
621
|
/**
|
|
711
|
-
*
|
|
622
|
+
* evaluateCurrentLocator:
|
|
623
|
+
* Gathers debug information about the current locator's resolved elements.
|
|
624
|
+
* Helps with logging and debugging complex locator chains.
|
|
712
625
|
*/
|
|
713
626
|
evaluateCurrentLocator = async (currentLocator, resultsArray, currentIFrame) => {
|
|
714
627
|
if (currentIFrame) {
|
|
@@ -720,7 +633,7 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
720
633
|
} else {
|
|
721
634
|
const elementsData = await this.evaluateAndGetAttributes(currentLocator);
|
|
722
635
|
resultsArray.push({
|
|
723
|
-
currentLocatorString: currentLocator
|
|
636
|
+
currentLocatorString: `${currentLocator}`,
|
|
724
637
|
resolved: elementsData.length > 0,
|
|
725
638
|
elementCount: elementsData.length,
|
|
726
639
|
elementsResolvedTo: elementsData
|
|
@@ -728,8 +641,8 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
728
641
|
}
|
|
729
642
|
};
|
|
730
643
|
/**
|
|
731
|
-
*
|
|
732
|
-
*
|
|
644
|
+
* evaluateAndGetAttributes:
|
|
645
|
+
* Extracts tagName and attributes from all elements matched by the locator for debugging purposes.
|
|
733
646
|
*/
|
|
734
647
|
evaluateAndGetAttributes = async (pwLocator) => {
|
|
735
648
|
return await pwLocator.evaluateAll(
|
|
@@ -740,9 +653,129 @@ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
|
740
653
|
);
|
|
741
654
|
};
|
|
742
655
|
};
|
|
656
|
+
var WithMethodsClass = class extends GetLocatorBase {
|
|
657
|
+
constructor(pageObjectClass, log, locatorSubstring, schemasMap) {
|
|
658
|
+
super(pageObjectClass, log, locatorSubstring);
|
|
659
|
+
this.pageObjectClass = pageObjectClass;
|
|
660
|
+
this.log = log;
|
|
661
|
+
this.schemasMap = schemasMap;
|
|
662
|
+
}
|
|
663
|
+
locatorSchemaPath;
|
|
664
|
+
/**
|
|
665
|
+
* init:
|
|
666
|
+
* Assigns the locatorSchemaPath and binds methods (update, updates, addFilter, getNestedLocator, getLocator)
|
|
667
|
+
* directly on the locatorSchemaCopy. Returns the modified copy, now fully chainable and type-safe.
|
|
668
|
+
*/
|
|
669
|
+
init(locatorSchemaPath, locatorSchemaCopy) {
|
|
670
|
+
this.locatorSchemaPath = locatorSchemaPath;
|
|
671
|
+
const self = this;
|
|
672
|
+
locatorSchemaCopy.update = function(a, b) {
|
|
673
|
+
const fullPath = this.locatorSchemaPath;
|
|
674
|
+
if (b === void 0) {
|
|
675
|
+
const updates = a;
|
|
676
|
+
self.applyUpdate(self.schemasMap, self.locatorSchemaPath, updates);
|
|
677
|
+
} else {
|
|
678
|
+
const subPath = a;
|
|
679
|
+
const updates = b;
|
|
680
|
+
if (!(subPath === fullPath || fullPath.startsWith(`${subPath}.`))) {
|
|
681
|
+
throw new Error(`Invalid sub-path: '${subPath}' is not a valid sub-path of '${fullPath}'.`);
|
|
682
|
+
}
|
|
683
|
+
self.applyUpdateToSubPath(self.schemasMap, subPath, updates);
|
|
684
|
+
}
|
|
685
|
+
return this;
|
|
686
|
+
};
|
|
687
|
+
locatorSchemaCopy.updates = function(indexedUpdates) {
|
|
688
|
+
const pathIndexPairs = self.extractPathsFromSchema(self.locatorSchemaPath);
|
|
689
|
+
self.applyUpdates(self.schemasMap, pathIndexPairs, indexedUpdates);
|
|
690
|
+
return this;
|
|
691
|
+
};
|
|
692
|
+
locatorSchemaCopy.addFilter = function(subPath, filterData) {
|
|
693
|
+
const fullPath = this.locatorSchemaPath;
|
|
694
|
+
if (!self.schemasMap.has(subPath)) {
|
|
695
|
+
const allowedPaths = self.extractPathsFromSchema(fullPath).map((p) => p.path).filter((path) => self.schemasMap.has(path));
|
|
696
|
+
throw new Error(
|
|
697
|
+
`Invalid sub-path '${subPath}' in addFilter. Allowed sub-paths are:
|
|
698
|
+
${allowedPaths.join(",\n")}`
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
if (!this.filterMap) {
|
|
702
|
+
this.filterMap = /* @__PURE__ */ new Map();
|
|
703
|
+
}
|
|
704
|
+
const existingFilters = this.filterMap.get(subPath) || [];
|
|
705
|
+
existingFilters.push(filterData);
|
|
706
|
+
this.filterMap.set(subPath, existingFilters);
|
|
707
|
+
return this;
|
|
708
|
+
};
|
|
709
|
+
locatorSchemaCopy.getNestedLocator = async function(arg) {
|
|
710
|
+
if (arg !== void 0 && arg !== null && typeof arg !== "object") {
|
|
711
|
+
throw new Error("Invalid argument passed to getNestedLocator: Expected an object or null.");
|
|
712
|
+
}
|
|
713
|
+
if (!arg || Object.keys(arg).length === 0) {
|
|
714
|
+
return await self.buildNestedLocator(
|
|
715
|
+
self.locatorSchemaPath,
|
|
716
|
+
self.schemasMap,
|
|
717
|
+
this.filterMap,
|
|
718
|
+
{}
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
const keys = Object.keys(arg);
|
|
722
|
+
const isNumberKey = keys.every((key) => /^\d+$/.test(key));
|
|
723
|
+
const numericIndices = {};
|
|
724
|
+
if (isNumberKey) {
|
|
725
|
+
for (const [key, value] of Object.entries(arg)) {
|
|
726
|
+
const index = Number(key);
|
|
727
|
+
if (typeof value === "number" && value >= 0) {
|
|
728
|
+
numericIndices[index] = value;
|
|
729
|
+
} else if (value !== null) {
|
|
730
|
+
throw new Error(`Invalid index value at key '${key}': Expected a positive number or null.`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
} else {
|
|
734
|
+
const pathIndexPairs = self.extractPathsFromSchema(self.locatorSchemaPath);
|
|
735
|
+
const pathToIndexMap = new Map(pathIndexPairs.map((pair, idx) => [pair.path, idx]));
|
|
736
|
+
for (const [subPath, value] of Object.entries(arg)) {
|
|
737
|
+
if (!self.schemasMap.has(subPath)) {
|
|
738
|
+
const validPaths = Array.from(self.schemasMap.keys());
|
|
739
|
+
throw new Error(
|
|
740
|
+
`Invalid sub-path '${subPath}' in getNestedLocator. Allowed sub-paths are:
|
|
741
|
+
${validPaths.join(",\n")}`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
if (!pathToIndexMap.has(subPath)) {
|
|
745
|
+
const validPaths = pathIndexPairs.map((p) => p.path).filter((path) => self.schemasMap.has(path));
|
|
746
|
+
throw new Error(
|
|
747
|
+
`Invalid sub-path '${subPath}' in getNestedLocator. Allowed sub-paths are:
|
|
748
|
+
${validPaths.join(",\n")}`
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
const numericIndex = pathToIndexMap.get(subPath);
|
|
752
|
+
if (numericIndex === void 0) {
|
|
753
|
+
throw new Error(`Sub-path '${subPath}' not found in pathToIndexMap.`);
|
|
754
|
+
}
|
|
755
|
+
if (value !== null && (typeof value !== "number" || value < 0)) {
|
|
756
|
+
throw new Error(`Invalid index for sub-path '${subPath}': Expected a positive number or null.`);
|
|
757
|
+
}
|
|
758
|
+
if (value !== null) {
|
|
759
|
+
numericIndices[numericIndex] = value;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return await self.buildNestedLocator(
|
|
764
|
+
self.locatorSchemaPath,
|
|
765
|
+
self.schemasMap,
|
|
766
|
+
this.filterMap,
|
|
767
|
+
numericIndices
|
|
768
|
+
);
|
|
769
|
+
};
|
|
770
|
+
locatorSchemaCopy.getLocator = async () => {
|
|
771
|
+
return self.getBy.getLocator(locatorSchemaCopy);
|
|
772
|
+
};
|
|
773
|
+
return locatorSchemaCopy;
|
|
774
|
+
}
|
|
775
|
+
};
|
|
743
776
|
|
|
744
777
|
// src/helpers/sessionStorage.actions.ts
|
|
745
|
-
var
|
|
778
|
+
var import_test2 = require("@playwright/test");
|
|
746
779
|
var SessionStorage = class {
|
|
747
780
|
// Initializes the class with a Playwright Page object and a name for the Page Object Class.
|
|
748
781
|
constructor(page, pocName) {
|
|
@@ -792,7 +825,7 @@ var SessionStorage = class {
|
|
|
792
825
|
*/
|
|
793
826
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
794
827
|
async set(states, reload) {
|
|
795
|
-
await
|
|
828
|
+
await import_test2.test.step(`${this.pocName}: setSessionStorage`, async () => {
|
|
796
829
|
await this.writeToSessionStorage(states);
|
|
797
830
|
if (reload) {
|
|
798
831
|
await this.page.reload();
|
|
@@ -814,7 +847,7 @@ var SessionStorage = class {
|
|
|
814
847
|
async setOnNextNavigation(states) {
|
|
815
848
|
this.queuedStates = { ...this.queuedStates, ...states };
|
|
816
849
|
const populateStorage = async () => {
|
|
817
|
-
await
|
|
850
|
+
await import_test2.test.step(`${this.pocName}: setSessionStorageBeforeNavigation`, async () => {
|
|
818
851
|
await this.writeToSessionStorage(this.queuedStates);
|
|
819
852
|
});
|
|
820
853
|
this.queuedStates = {};
|
|
@@ -851,7 +884,7 @@ var SessionStorage = class {
|
|
|
851
884
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
852
885
|
async get(keys) {
|
|
853
886
|
let result = {};
|
|
854
|
-
await
|
|
887
|
+
await import_test2.test.step(`${this.pocName}: getSessionStorage`, async () => {
|
|
855
888
|
const allData = await this.readFromSessionStorage();
|
|
856
889
|
if (keys && keys.length > 0) {
|
|
857
890
|
for (const key of keys) {
|
|
@@ -869,7 +902,7 @@ var SessionStorage = class {
|
|
|
869
902
|
* Clears all states in sessionStorage.
|
|
870
903
|
*/
|
|
871
904
|
async clear() {
|
|
872
|
-
await
|
|
905
|
+
await import_test2.test.step(`${this.pocName}: clear SessionStorage`, async () => {
|
|
873
906
|
await this.page.evaluate(() => sessionStorage.clear());
|
|
874
907
|
});
|
|
875
908
|
}
|
|
@@ -914,7 +947,7 @@ function createCypressIdEngine() {
|
|
|
914
947
|
|
|
915
948
|
// src/basePage.ts
|
|
916
949
|
var selectorRegistered = false;
|
|
917
|
-
var
|
|
950
|
+
var BasePage = class {
|
|
918
951
|
/** Provides Playwright page methods */
|
|
919
952
|
page;
|
|
920
953
|
/** Playwright TestInfo contains information about currently running test, available to any test function */
|
|
@@ -922,12 +955,10 @@ var BasePage2 = class {
|
|
|
922
955
|
/** Selectors can be used to install custom selector engines.*/
|
|
923
956
|
selector;
|
|
924
957
|
/** The base URL of the Page Object Class */
|
|
925
|
-
// baseUrl: string;
|
|
926
958
|
baseUrl;
|
|
927
959
|
/** The URL path of the Page Object Class */
|
|
928
|
-
// urlPath: string;
|
|
929
960
|
urlPath;
|
|
930
|
-
|
|
961
|
+
/** The full URL of the Page Object Class */
|
|
931
962
|
fullUrl;
|
|
932
963
|
/** The name of the Page Object Class */
|
|
933
964
|
pocName;
|
|
@@ -935,24 +966,39 @@ var BasePage2 = class {
|
|
|
935
966
|
log;
|
|
936
967
|
/** The SessionStorage class provides methods for setting and getting session storage data in Playwright.*/
|
|
937
968
|
sessionStorage;
|
|
969
|
+
/**
|
|
970
|
+
* locators:
|
|
971
|
+
* An instance of GetLocatorBase that handles schema management and provides getLocatorSchema calls.
|
|
972
|
+
* Initially, LocatorSubstring is undefined. Once getLocatorSchema(path) is called,
|
|
973
|
+
* we get a chainable object typed with LocatorSubstring = P.
|
|
974
|
+
*/
|
|
938
975
|
locators;
|
|
939
|
-
constructor(page, testInfo, baseUrl, urlPath, pocName, pwrl) {
|
|
976
|
+
constructor(page, testInfo, baseUrl, urlPath, pocName, pwrl, locatorSubstring) {
|
|
940
977
|
this.page = page;
|
|
941
978
|
this.testInfo = testInfo;
|
|
942
|
-
this.selector =
|
|
979
|
+
this.selector = import_test3.selectors;
|
|
943
980
|
this.baseUrl = baseUrl;
|
|
944
981
|
this.urlPath = urlPath;
|
|
945
982
|
this.fullUrl = this.constructFullUrl(baseUrl, urlPath);
|
|
946
983
|
this.pocName = pocName;
|
|
947
984
|
this.log = pwrl.getNewChildLogger(pocName);
|
|
948
|
-
this.locators = new GetLocatorBase(
|
|
985
|
+
this.locators = new GetLocatorBase(
|
|
986
|
+
this,
|
|
987
|
+
this.log.getNewChildLogger("GetLocator"),
|
|
988
|
+
locatorSubstring
|
|
989
|
+
);
|
|
949
990
|
this.initLocatorSchemas();
|
|
950
991
|
this.sessionStorage = new SessionStorage(this.page, this.pocName);
|
|
951
992
|
if (!selectorRegistered) {
|
|
952
|
-
|
|
993
|
+
import_test3.selectors.register("data-cy", createCypressIdEngine);
|
|
953
994
|
selectorRegistered = true;
|
|
954
995
|
}
|
|
955
996
|
}
|
|
997
|
+
/**
|
|
998
|
+
* constructFullUrl:
|
|
999
|
+
* Combines baseUrl and urlPath, handling both strings and RegExps.
|
|
1000
|
+
* Ensures a flexible approach to URL matching (string or regex-based).
|
|
1001
|
+
*/
|
|
956
1002
|
constructFullUrl(baseUrl, urlPath) {
|
|
957
1003
|
const escapeStringForRegExp = (str) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
958
1004
|
if (typeof baseUrl === "string" && typeof urlPath === "string") {
|
|
@@ -970,64 +1016,67 @@ var BasePage2 = class {
|
|
|
970
1016
|
throw new Error("Invalid baseUrl or urlPath types. Expected string or RegExp.");
|
|
971
1017
|
}
|
|
972
1018
|
/**
|
|
973
|
-
*
|
|
974
|
-
* - Asynchronously retrieves a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
|
|
975
|
-
* - Can be chained after the update and updates methods, getNestedLocator will end the chain.
|
|
976
|
-
* - The optional parameter of the method takes an object with 0-based indices "{0: 0, 3: 1}" for one or more locators
|
|
977
|
-
* to be nested given by sub-paths (indices correspond to last "word" of a sub-path).
|
|
978
|
-
* - Returns a promise that resolves to the nested locator.
|
|
1019
|
+
* Implementation of getNestedLocator.
|
|
979
1020
|
*/
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1021
|
+
async getNestedLocator(locatorSchemaPath, subPathIndices) {
|
|
1022
|
+
const withValidation = new WithSubPathValidation(
|
|
1023
|
+
this,
|
|
1024
|
+
this.log.getNewChildLogger("SubPathValidation"),
|
|
1025
|
+
locatorSchemaPath
|
|
1026
|
+
);
|
|
1027
|
+
return await withValidation.getNestedLocator(subPathIndices);
|
|
1028
|
+
}
|
|
983
1029
|
/**
|
|
984
|
-
* getLocator()
|
|
985
|
-
*
|
|
986
|
-
* and will return the locator for which the full LocatorSchemaPath resolves to,
|
|
987
|
-
*
|
|
988
|
-
*
|
|
1030
|
+
* Short-hand wrapper method for calling .getLocatorSchema(LocatorSchemaPath).getLocator()
|
|
1031
|
+
*
|
|
1032
|
+
* This method does not perform nesting,and will return the locator for which the full LocatorSchemaPath resolves to,
|
|
1033
|
+
* provided by getLocatorSchema("...")
|
|
1034
|
+
*
|
|
1035
|
+
* Note: This short-hand wrapper method is useful for quickly getting a locator without having to call
|
|
1036
|
+
* getLocatorSchema("...") first. On the other hand, it can't be used to update or add filters to the LocatorSchema.
|
|
1037
|
+
*
|
|
1038
|
+
* @example
|
|
1039
|
+
* // Usage:
|
|
1040
|
+
* const submitButton = await poc.getLocator("main.form.button@submit");
|
|
1041
|
+
* await expect(submitButton, "should only exist one submit button").toHaveCount(1);
|
|
989
1042
|
*/
|
|
990
1043
|
getLocator = async (locatorSchemaPath) => {
|
|
991
1044
|
return await this.getLocatorSchema(locatorSchemaPath).getLocator();
|
|
992
1045
|
};
|
|
993
1046
|
/**
|
|
1047
|
+
* getLocatorSchema:
|
|
1048
|
+
* Delegates to this.locators.getLocatorSchema.
|
|
1049
|
+
* Returns a chainable schema object for the given path.
|
|
1050
|
+
* Once called with a specific path P, the update and addFilter methods are restricted to sub-paths of P.
|
|
1051
|
+
*
|
|
994
1052
|
* The "getLocatorSchema" method is used to retrieve an updatable deep copy of a LocatorSchema defined in the
|
|
995
1053
|
* GetLocatorBase class. It enriches the returned schema with additional methods to handle updates and retrieval of
|
|
996
1054
|
* deep copy locators.
|
|
997
1055
|
*
|
|
998
1056
|
* getLocatorSchema adds the following chainable methods to the returned LocatorSchemaWithMethods object:
|
|
999
1057
|
*
|
|
1000
|
-
* update
|
|
1001
|
-
* - Allows updating
|
|
1002
|
-
* -
|
|
1003
|
-
* -
|
|
1004
|
-
* will overwrite the corresponding property in the current schema.
|
|
1005
|
-
* - Returns the updated deep copy of the "LocatorSchema" with methods.
|
|
1006
|
-
* - Can be chained with the update and updates methods, and the getLocator or getNestedLocator method.
|
|
1058
|
+
* update
|
|
1059
|
+
* - Allows updating any schema in the chain by specifying the subPath directly.
|
|
1060
|
+
* - Gives compile-time suggestions for valid sub-paths of the LocatorSchemaPath provided to .getLocatorSchema().
|
|
1061
|
+
* - If you want to update multiple schemas, chain multiple .update() calls.
|
|
1007
1062
|
*
|
|
1008
|
-
*
|
|
1009
|
-
* -
|
|
1010
|
-
* - This method
|
|
1011
|
-
*
|
|
1012
|
-
* - Takes an object where keys represent the index of the last "word" of a sub-path, where the value per key is a
|
|
1013
|
-
* "LocatorSchema" object which omits the locatorSchemaPath parameter as input, the parameters provided will overwrite
|
|
1014
|
-
* the corresponding property in the given schema.
|
|
1015
|
-
* - Returns the updated deep copy of the LocatorSchema object with methods and its own updated deep copies for all
|
|
1016
|
-
* LocatorSchema each sub-path resolved to.
|
|
1017
|
-
* - Can be chained with the update and updates methods, and the getLocator or getNestedLocator method.
|
|
1063
|
+
* addFilter(subPath: SubPaths<LocatorSchemaPathType, LocatorSubstring>, filterData: FilterEntry)
|
|
1064
|
+
* - The equivalent of the Playwright locator.filter() method
|
|
1065
|
+
* - This method is used for filtering the specified locator based on the provided filterData.
|
|
1066
|
+
* - Can be chained multiple times to add multiple filters to the same or different LocatorSchema.
|
|
1018
1067
|
*
|
|
1019
|
-
* getNestedLocator
|
|
1020
|
-
* - Asynchronously
|
|
1021
|
-
* - Can be chained after the update and
|
|
1022
|
-
* -
|
|
1023
|
-
*
|
|
1024
|
-
*
|
|
1068
|
+
* getNestedLocator
|
|
1069
|
+
* - Asynchronously builds a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
|
|
1070
|
+
* - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
|
|
1071
|
+
* - getNestedLocator will end the method chain and return a nested Playwright Locator.
|
|
1072
|
+
* - Optionally parameter takes a list of key(subPath)-value(index) pairs, the locator constructed from the LocatorSchema
|
|
1073
|
+
* with the specified subPath will resolve to the .nth(n) occurrence of the element, within the chain.
|
|
1025
1074
|
*
|
|
1026
1075
|
* getLocator()
|
|
1027
|
-
* - Asynchronously retrieves a locator based on the current
|
|
1028
|
-
* and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
|
|
1029
|
-
* - Can be chained after the update and
|
|
1030
|
-
* -
|
|
1076
|
+
* - Asynchronously retrieves a locator based on the current LocatorSchemaPath.
|
|
1077
|
+
* - This method does not perform nesting and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
|
|
1078
|
+
* - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
|
|
1079
|
+
* - getLocator will end the method chain and return a Playwright Locator.
|
|
1031
1080
|
*
|
|
1032
1081
|
* Note: Calling getLocator() and getNestedLocator() on the same LocatorSchemaPath will return a Locator for the same
|
|
1033
1082
|
* element, but the Locator returned by getNestedLocator() will be a locator resolving to said same element through
|
|
@@ -1037,16 +1086,168 @@ var BasePage2 = class {
|
|
|
1037
1086
|
* That said, for certain use cases, getLocator() can be useful, and you could use it to manually chain locators
|
|
1038
1087
|
* yourself if some edge case required it. Though, it would be likely be more prudent to expand your LocatorSchemaPath
|
|
1039
1088
|
* type and initLocatorSchemas() method to include the additional locators you need for the given POC, and then use
|
|
1040
|
-
* getNestedLocator() instead.
|
|
1089
|
+
* getNestedLocator() instead, or by implementing a helper method on your Page Object Class.
|
|
1041
1090
|
*/
|
|
1042
|
-
getLocatorSchema(
|
|
1043
|
-
return this.locators.getLocatorSchema(
|
|
1091
|
+
getLocatorSchema(path) {
|
|
1092
|
+
return this.locators.getLocatorSchema(path);
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
var WithSubPathValidation = class extends GetLocatorBase {
|
|
1096
|
+
constructor(pageObjectClass, log, locatorSchemaPath) {
|
|
1097
|
+
super(pageObjectClass, log, locatorSchemaPath);
|
|
1098
|
+
this.locatorSchemaPath = locatorSchemaPath;
|
|
1099
|
+
}
|
|
1100
|
+
async getNestedLocator(arg) {
|
|
1101
|
+
return await this.pageObjectClass.getLocatorSchema(this.locatorSchemaPath).getNestedLocator(arg);
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1105
|
+
// src/fixture/base.fixtures.ts
|
|
1106
|
+
var import_test4 = require("@playwright/test");
|
|
1107
|
+
|
|
1108
|
+
// src/helpers/playwrightReportLogger.ts
|
|
1109
|
+
var PlaywrightReportLogger = class _PlaywrightReportLogger {
|
|
1110
|
+
// Initializes the logger with shared log level, log entries, and a context name.
|
|
1111
|
+
constructor(sharedLogLevel, sharedLogEntry, contextName) {
|
|
1112
|
+
this.sharedLogLevel = sharedLogLevel;
|
|
1113
|
+
this.sharedLogEntry = sharedLogEntry;
|
|
1114
|
+
this.contextName = contextName;
|
|
1115
|
+
}
|
|
1116
|
+
contextName;
|
|
1117
|
+
logLevels = ["debug", "info", "warn", "error"];
|
|
1118
|
+
/**
|
|
1119
|
+
* Creates a child logger with a new contextual name, sharing the same log level and log entries with the parent logger.
|
|
1120
|
+
*
|
|
1121
|
+
* The root loggers log "level" is referenced by all child loggers and their child loggers and so on...
|
|
1122
|
+
* Changing the log "level" of one, will change it for all.
|
|
1123
|
+
*/
|
|
1124
|
+
getNewChildLogger(prefix) {
|
|
1125
|
+
return new _PlaywrightReportLogger(this.sharedLogLevel, this.sharedLogEntry, `${this.contextName} -> ${prefix}`);
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Logs a message with the specified log level, prefix, and additional arguments if the current log level permits.
|
|
1129
|
+
*/
|
|
1130
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1131
|
+
log(level, message, ...args) {
|
|
1132
|
+
const logLevelIndex = this.logLevels.indexOf(level);
|
|
1133
|
+
if (logLevelIndex < this.getCurrentLogLevelIndex()) {
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
this.sharedLogEntry.push({
|
|
1137
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1138
|
+
logLevel: level,
|
|
1139
|
+
prefix: this.contextName,
|
|
1140
|
+
message: `${message}
|
|
1141
|
+
|
|
1142
|
+
${args.join("\n\n")}`
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Logs a debug-level message with the specified message and arguments.
|
|
1147
|
+
*/
|
|
1148
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1149
|
+
debug(message, ...args) {
|
|
1150
|
+
this.log("debug", message, ...args);
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Logs a info-level message with the specified message and arguments.
|
|
1154
|
+
*/
|
|
1155
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1156
|
+
info(message, ...args) {
|
|
1157
|
+
this.log("info", message, ...args);
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Logs a warn-level message with the specified message and arguments.
|
|
1161
|
+
*/
|
|
1162
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1163
|
+
warn(message, ...args) {
|
|
1164
|
+
this.log("warn", message, ...args);
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Logs a error-level message with the specified message and arguments.
|
|
1168
|
+
*/
|
|
1169
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1170
|
+
error(message, ...args) {
|
|
1171
|
+
this.log("error", message, ...args);
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Sets the current log level to the specified level during runTime.
|
|
1175
|
+
*/
|
|
1176
|
+
setLogLevel(level) {
|
|
1177
|
+
this.sharedLogLevel.current = level;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Retrieves the current log level during runtime.
|
|
1181
|
+
*/
|
|
1182
|
+
getCurrentLogLevel() {
|
|
1183
|
+
return this.sharedLogLevel.current;
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Retrieves the index of the current log level in the logLevels array during runtime.
|
|
1187
|
+
*/
|
|
1188
|
+
getCurrentLogLevelIndex() {
|
|
1189
|
+
return this.logLevels.indexOf(this.sharedLogLevel.current);
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Resets the current log level to the initial level during runtime.
|
|
1193
|
+
*/
|
|
1194
|
+
resetLogLevel() {
|
|
1195
|
+
this.sharedLogLevel.current = this.sharedLogLevel.initial;
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Checks if the input log level is equal to the current log level of the PlaywrightReportLogger instance.
|
|
1199
|
+
*/
|
|
1200
|
+
isCurrentLogLevel(level) {
|
|
1201
|
+
return this.sharedLogLevel.current === level;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Returns 'true' if the "level" parameter provided has an equal or greater index than the current logLevel.
|
|
1205
|
+
*/
|
|
1206
|
+
isLogLevelEnabled(level) {
|
|
1207
|
+
const logLevelIndex = this.logLevels.indexOf(level);
|
|
1208
|
+
if (logLevelIndex < this.getCurrentLogLevelIndex()) {
|
|
1209
|
+
return false;
|
|
1210
|
+
}
|
|
1211
|
+
return true;
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Attaches the recorded log entries to the Playwright HTML report in a sorted and formatted manner.
|
|
1215
|
+
*/
|
|
1216
|
+
attachLogsToTest(testInfo) {
|
|
1217
|
+
this.sharedLogEntry.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
1218
|
+
for (const log of this.sharedLogEntry) {
|
|
1219
|
+
const printTime = log.timestamp.toLocaleTimeString("nb-NO", {
|
|
1220
|
+
hour: "2-digit",
|
|
1221
|
+
minute: "2-digit",
|
|
1222
|
+
second: "2-digit"
|
|
1223
|
+
});
|
|
1224
|
+
const printDate = log.timestamp.toLocaleDateString("nb-NO", {
|
|
1225
|
+
day: "2-digit",
|
|
1226
|
+
month: "2-digit",
|
|
1227
|
+
year: "numeric"
|
|
1228
|
+
});
|
|
1229
|
+
const printLogLevel = `${log.logLevel.toUpperCase()}`;
|
|
1230
|
+
const printPrefix = log.prefix ? `: [${log.prefix}]` : "";
|
|
1231
|
+
let messageBody = "";
|
|
1232
|
+
let messageContentType = "";
|
|
1233
|
+
try {
|
|
1234
|
+
const parsedMessage = JSON.parse(log.message);
|
|
1235
|
+
messageContentType = "application/json";
|
|
1236
|
+
messageBody = JSON.stringify(parsedMessage, null, 2);
|
|
1237
|
+
} catch (error) {
|
|
1238
|
+
messageContentType = "text/plain";
|
|
1239
|
+
messageBody = log.message;
|
|
1240
|
+
}
|
|
1241
|
+
testInfo.attach(`${printTime} ${printDate} - ${printLogLevel} ${printPrefix}`, {
|
|
1242
|
+
contentType: messageContentType,
|
|
1243
|
+
body: Buffer.from(messageBody)
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1044
1246
|
}
|
|
1045
1247
|
};
|
|
1046
1248
|
|
|
1047
1249
|
// src/fixture/base.fixtures.ts
|
|
1048
|
-
var
|
|
1049
|
-
var test3 = import_test5.test.extend({
|
|
1250
|
+
var test3 = import_test4.test.extend({
|
|
1050
1251
|
// biome-ignore lint/correctness/noEmptyPattern: <Playwright does not support the use of _, thus we must provide an empty object {}>
|
|
1051
1252
|
log: async ({}, use, testInfo) => {
|
|
1052
1253
|
const contextName = "TestCase";
|