pomwright 0.0.3

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/dist/index.mjs ADDED
@@ -0,0 +1,1002 @@
1
+ // src/basePage.ts
2
+ import { selectors } from "@playwright/test";
3
+
4
+ // src/helpers/playwrightReportLogger.ts
5
+ var PlaywrightReportLogger = class _PlaywrightReportLogger {
6
+ constructor(sharedLogLevel, sharedLogEntry, contextName) {
7
+ this.sharedLogLevel = sharedLogLevel;
8
+ this.sharedLogEntry = sharedLogEntry;
9
+ this.contextName = contextName;
10
+ }
11
+ contextName;
12
+ logLevels = ["debug", "info", "warn", "error"];
13
+ /**
14
+ * Creates a new logger instance with a new contextual name which includes a reference to the parent logger.
15
+ *
16
+ * The root loggers log "level" is referenced by all child loggers and their child loggers and so on...
17
+ * Changing the log "level" of one, will change it for all.
18
+ *
19
+ * @param prefix - The prefix to add to the new logger instance.
20
+ * @returns - A new logger instance with the updated prefix.
21
+ */
22
+ getNewChildLogger(prefix) {
23
+ return new _PlaywrightReportLogger(this.sharedLogLevel, this.sharedLogEntry, `${this.contextName} -> ${prefix}`);
24
+ }
25
+ /**
26
+ * Logs a message with the specified log level, prefix, and arguments.
27
+ * The message will only be recorded if the current log level allows it.
28
+ *
29
+ * @param level - The log level for the message.
30
+ * @param message - The log message.
31
+ * @param args - Additional arguments to log.
32
+ */
33
+ log(level, message, ...args) {
34
+ const logLevelIndex = this.logLevels.indexOf(level);
35
+ if (logLevelIndex < this.getCurrentLogLevelIndex()) {
36
+ return;
37
+ }
38
+ this.sharedLogEntry.push({
39
+ timestamp: /* @__PURE__ */ new Date(),
40
+ logLevel: level,
41
+ prefix: this.contextName,
42
+ message: `${message}
43
+
44
+ ${args.join("\n\n")}`
45
+ });
46
+ }
47
+ /**
48
+ * Logs a debug-level message with the specified message and arguments.
49
+ *
50
+ * @param message - The log message.
51
+ * @param args - Additional arguments to log.
52
+ */
53
+ debug(message, ...args) {
54
+ this.log("debug", message, ...args);
55
+ }
56
+ /**
57
+ * Logs a info-level message with the specified message and arguments.
58
+ *
59
+ * @param message - The log message.
60
+ * @param args - Additional arguments to log.
61
+ */
62
+ info(message, ...args) {
63
+ this.log("info", message, ...args);
64
+ }
65
+ /**
66
+ * Logs a warn-level message with the specified message and arguments.
67
+ *
68
+ * @param message - The log message.
69
+ * @param args - Additional arguments to log.
70
+ */
71
+ warn(message, ...args) {
72
+ this.log("warn", message, ...args);
73
+ }
74
+ /**
75
+ * Logs a error-level message with the specified message and arguments.
76
+ *
77
+ * @param message - The log message.
78
+ * @param args - Additional arguments to log.
79
+ */
80
+ error(message, ...args) {
81
+ this.log("error", message, ...args);
82
+ }
83
+ /**
84
+ * Set logLevel during runtime.
85
+ *
86
+ * @param level - The logLevel ("debug" | "info" | "warn" | "error").
87
+ */
88
+ setLogLevel(level) {
89
+ this.sharedLogLevel.current = level;
90
+ }
91
+ /**
92
+ * Returns the current logLevel during runtime.
93
+ *
94
+ * @returns LogLevel ("debug" | "info" | "warn" | "error")
95
+ */
96
+ getCurrentLogLevel() {
97
+ return this.sharedLogLevel.current;
98
+ }
99
+ /**
100
+ * Returns the index of the current logLevel during runtime.
101
+ */
102
+ getCurrentLogLevelIndex() {
103
+ return this.logLevels.indexOf(this.sharedLogLevel.current);
104
+ }
105
+ /**
106
+ * Sets logLevel back to the initial logLevel during runtime.
107
+ */
108
+ resetLogLevel() {
109
+ this.sharedLogLevel.current = this.sharedLogLevel.initial;
110
+ }
111
+ /**
112
+ * isCurrentLogLevel is a method that checks if the input log level is equal to the current log level of the PlaywrightReportLogger instance.
113
+ * @param level The log level to check if it is equal to the current log level.
114
+ * @returns A boolean indicating whether the input log level is equal to the current log level.
115
+ */
116
+ isCurrentLogLevel(level) {
117
+ return this.sharedLogLevel.current === level;
118
+ }
119
+ /**
120
+ * isLogLevelEnabled returns 'true' if the "level" parameter provided has an equal or greater index than the current logLevel.
121
+ * @param level
122
+ * @returns
123
+ */
124
+ isLogLevelEnabled(level) {
125
+ const logLevelIndex = this.logLevels.indexOf(level);
126
+ if (logLevelIndex < this.getCurrentLogLevelIndex()) {
127
+ return false;
128
+ }
129
+ return true;
130
+ }
131
+ /**
132
+ * Attaches the recorded logs to the Playwright HTML report.
133
+ *
134
+ * @param testInfo - The test information object from Playwright.
135
+ */
136
+ attachLogsToTest(testInfo) {
137
+ this.sharedLogEntry.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
138
+ for (const log of this.sharedLogEntry) {
139
+ const printTime = log.timestamp.toLocaleTimeString("nb-NO", {
140
+ hour: "2-digit",
141
+ minute: "2-digit",
142
+ second: "2-digit"
143
+ });
144
+ const printDate = log.timestamp.toLocaleDateString("nb-NO", {
145
+ day: "2-digit",
146
+ month: "2-digit",
147
+ year: "numeric"
148
+ });
149
+ const printLogLevel = `${log.logLevel.toUpperCase()}`;
150
+ const printPrefix = log.prefix ? `: [${log.prefix}]` : "";
151
+ let messageBody;
152
+ let messageContentType;
153
+ try {
154
+ const parsedMessage = JSON.parse(log.message);
155
+ messageContentType = "application/json";
156
+ messageBody = JSON.stringify(parsedMessage, null, 2);
157
+ } catch (error) {
158
+ messageContentType = "text/plain";
159
+ messageBody = log.message;
160
+ }
161
+ testInfo.attach(`${printTime} ${printDate} - ${printLogLevel} ${printPrefix}`, {
162
+ contentType: messageContentType,
163
+ body: Buffer.from(messageBody)
164
+ });
165
+ }
166
+ }
167
+ };
168
+
169
+ // src/helpers/sessionStorage.actions.ts
170
+ import { test } from "@playwright/test";
171
+ var SessionStorage = class {
172
+ constructor(page, pocName) {
173
+ this.page = page;
174
+ this.pocName = pocName;
175
+ }
176
+ queuedStates = {};
177
+ isInitiated = false;
178
+ /**
179
+ * Writes states to the sessionStorage. Private utility function.
180
+ *
181
+ * @param states An object representing the states (key/value pairs) to set.
182
+ */
183
+ async writeToSessionStorage(states) {
184
+ await this.page.evaluate((storage) => {
185
+ for (const [key, value] of Object.entries(storage)) {
186
+ window.sessionStorage.setItem(key, JSON.stringify(value));
187
+ }
188
+ }, states);
189
+ }
190
+ /**
191
+ * Reads all states from the sessionStorage. Private utility function.
192
+ *
193
+ * @returns An object containing all states from the sessionStorage.
194
+ */
195
+ async readFromSessionStorage() {
196
+ return await this.page.evaluate(() => {
197
+ const storage = {};
198
+ for (let i = 0; i < sessionStorage.length; i++) {
199
+ const key = sessionStorage.key(i);
200
+ if (key !== null) {
201
+ const item = sessionStorage.getItem(key);
202
+ try {
203
+ storage[key] = item ? JSON.parse(item) : null;
204
+ } catch (e) {
205
+ storage[key] = item;
206
+ }
207
+ }
208
+ }
209
+ return storage;
210
+ });
211
+ }
212
+ /**
213
+ * Sets the specified states in the sessionStorage.
214
+ *
215
+ * @param states An object representing the states (key/value pairs) to set in sessionStorage.
216
+ * @param reload If true, reloads the page after setting the sessionStorage data.
217
+ *
218
+ * Usage:
219
+ * await set({ user: 'John Doe', token: 'abc123' }, true);
220
+ */
221
+ async set(states, reload) {
222
+ await test.step(`${this.pocName}: setSessionStorage`, async () => {
223
+ await this.writeToSessionStorage(states);
224
+ if (reload) {
225
+ await this.page.reload();
226
+ }
227
+ });
228
+ }
229
+ /**
230
+ * Queues states to be set in the sessionStorage before the next navigation occurs.
231
+ * This handles multiple scenarios:
232
+ *
233
+ * 1. No Context, Single Call: Queues and sets states upon the next navigation.
234
+ * 2. No Context, Multiple Calls: Merges states from multiple calls and sets them upon the next navigation.
235
+ * 3. With Context: Directly sets states in sessionStorage if the context already exists.
236
+ *
237
+ * @param states An object representing the states (key/value pairs) to set.
238
+ *
239
+ * Usage:
240
+ * await setOnNextNavigation({ key: 'value' });
241
+ */
242
+ async setOnNextNavigation(states) {
243
+ this.queuedStates = { ...this.queuedStates, ...states };
244
+ const populateStorage = async () => {
245
+ await test.step(`${this.pocName}: setSessionStorageBeforeNavigation`, async () => {
246
+ await this.writeToSessionStorage(this.queuedStates);
247
+ });
248
+ this.queuedStates = {};
249
+ };
250
+ let contextExists = false;
251
+ try {
252
+ contextExists = await this.page.evaluate(() => {
253
+ return typeof window !== "undefined" && window.sessionStorage !== void 0;
254
+ });
255
+ } catch (e) {
256
+ contextExists = false;
257
+ }
258
+ if (contextExists) {
259
+ await populateStorage();
260
+ return;
261
+ }
262
+ if (!this.isInitiated) {
263
+ this.isInitiated = true;
264
+ this.page.once("framenavigated", async () => {
265
+ await populateStorage();
266
+ });
267
+ }
268
+ }
269
+ /**
270
+ * Fetches all or selected states from sessionStorage.
271
+ *
272
+ * @param keys Optional array of keys to fetch from sessionStorage.
273
+ * @returns An object containing the fetched states.
274
+ *
275
+ * Usage:
276
+ * 1. To fetch all states: await get();
277
+ * 2. To fetch selected states: await get(['key1', 'key2']);
278
+ */
279
+ async get(keys) {
280
+ let result = {};
281
+ await test.step(`${this.pocName}: getSessionStorage`, async () => {
282
+ const allData = await this.readFromSessionStorage();
283
+ if (keys && keys.length > 0) {
284
+ for (const key of keys) {
285
+ if (Object.prototype.hasOwnProperty.call(allData, key)) {
286
+ result[key] = allData[key];
287
+ }
288
+ }
289
+ } else {
290
+ result = allData;
291
+ }
292
+ });
293
+ return result;
294
+ }
295
+ /**
296
+ * Clears all states in sessionStorage.
297
+ *
298
+ * Usage:
299
+ * await clear();
300
+ */
301
+ async clear() {
302
+ await test.step(`${this.pocName}: clear SessionStorage`, async () => {
303
+ await this.page.evaluate(() => sessionStorage.clear());
304
+ });
305
+ }
306
+ };
307
+
308
+ // src/utils/selectorEngines.ts
309
+ function createCypressIdEngine() {
310
+ return {
311
+ query(document, selector) {
312
+ const attr = `[data-cy="${selector}"]`;
313
+ const el = document.querySelector(attr);
314
+ return el;
315
+ },
316
+ queryAll(document, selector) {
317
+ const attr = `[data-cy="${selector}"]`;
318
+ const els = Array.from(document.querySelectorAll(attr));
319
+ return els;
320
+ }
321
+ };
322
+ }
323
+
324
+ // src/helpers/getLocatorBase.ts
325
+ import { test as test2 } from "@playwright/test";
326
+
327
+ // src/helpers/locatorSchema.interface.ts
328
+ var GetByMethod = /* @__PURE__ */ ((GetByMethod2) => {
329
+ GetByMethod2["role"] = "role";
330
+ GetByMethod2["text"] = "text";
331
+ GetByMethod2["label"] = "label";
332
+ GetByMethod2["placeholder"] = "placeholder";
333
+ GetByMethod2["altText"] = "altText";
334
+ GetByMethod2["title"] = "title";
335
+ GetByMethod2["locator"] = "locator";
336
+ GetByMethod2["frameLocator"] = "frameLocator";
337
+ GetByMethod2["testId"] = "testId";
338
+ GetByMethod2["dataCy"] = "dataCy";
339
+ GetByMethod2["id"] = "id";
340
+ return GetByMethod2;
341
+ })(GetByMethod || {});
342
+ var locatorSchemaDummy = {
343
+ role: void 0,
344
+ roleOptions: {
345
+ checked: void 0,
346
+ disabled: void 0,
347
+ exact: void 0,
348
+ expanded: void 0,
349
+ includeHidden: void 0,
350
+ level: void 0,
351
+ name: void 0,
352
+ pressed: void 0,
353
+ selected: void 0
354
+ },
355
+ text: void 0,
356
+ textOptions: {
357
+ exact: void 0
358
+ },
359
+ label: void 0,
360
+ labelOptions: {
361
+ exact: void 0
362
+ },
363
+ placeholder: void 0,
364
+ placeholderOptions: {
365
+ exact: void 0
366
+ },
367
+ altText: void 0,
368
+ altTextOptions: {
369
+ exact: void 0
370
+ },
371
+ title: void 0,
372
+ titleOptions: {
373
+ exact: void 0
374
+ },
375
+ locator: void 0,
376
+ locatorOptions: {
377
+ has: void 0,
378
+ hasNot: void 0,
379
+ hasNotText: void 0,
380
+ hasText: void 0
381
+ },
382
+ frameLocator: void 0,
383
+ testId: void 0,
384
+ dataCy: void 0,
385
+ id: void 0,
386
+ locatorMethod: void 0,
387
+ locatorSchemaPath: void 0
388
+ };
389
+ function getLocatorSchemaDummy() {
390
+ return locatorSchemaDummy;
391
+ }
392
+
393
+ // src/helpers/getBy.locator.ts
394
+ import "@playwright/test";
395
+ var GetBy = class {
396
+ constructor(page, pwrl) {
397
+ this.page = page;
398
+ this.log = pwrl.getNewChildLogger(this.constructor.name);
399
+ this.methodMap = {
400
+ ["role" /* role */]: this.role,
401
+ ["text" /* text */]: this.text,
402
+ ["label" /* label */]: this.label,
403
+ ["placeholder" /* placeholder */]: this.placeholder,
404
+ ["altText" /* altText */]: this.altText,
405
+ ["title" /* title */]: this.title,
406
+ ["locator" /* locator */]: this.locator,
407
+ ["frameLocator" /* frameLocator */]: this.frameLocator,
408
+ ["testId" /* testId */]: this.testId,
409
+ ["dataCy" /* dataCy */]: this.dataCy,
410
+ ["id" /* id */]: this.id
411
+ };
412
+ this.subMethodMap = {
413
+ ["role" /* role */]: this.page.getByRole,
414
+ ["text" /* text */]: this.page.getByText,
415
+ ["label" /* label */]: this.page.getByLabel,
416
+ ["placeholder" /* placeholder */]: this.page.getByPlaceholder,
417
+ ["altText" /* altText */]: this.page.getByAltText,
418
+ ["title" /* title */]: this.page.getByTitle,
419
+ ["locator" /* locator */]: this.page.locator
420
+ };
421
+ }
422
+ log;
423
+ methodMap;
424
+ subMethodMap;
425
+ /**
426
+ * Retrieves a Playwright Locator based on the method specified in the LocatorSchema.
427
+ *
428
+ * @param locatorSchema The LocatorSchema object specifying the locator method and its parameters.
429
+ * @returns A promise that resolves to the appropriate Playwright Locator.
430
+ */
431
+ getLocator = (locatorSchema) => {
432
+ const methodName = locatorSchema.locatorMethod;
433
+ const method = this.methodMap[methodName];
434
+ if (method) {
435
+ return method(locatorSchema);
436
+ }
437
+ throw new Error(`Unsupported locator method: ${methodName}`);
438
+ };
439
+ getBy = (caller, locator) => {
440
+ const method = this.subMethodMap[caller];
441
+ if (!method) {
442
+ const errorText = "Error: unknown caller of method getBy(caller, locator) in getBy.locators.ts";
443
+ this.log.error(errorText);
444
+ throw new Error(errorText);
445
+ }
446
+ const initialPWLocator = locator[caller] ? method.call(this.page, locator[caller], locator?.[`${caller}Options`]) : null;
447
+ if (!initialPWLocator) {
448
+ const errorText = `Locator "${locator.locatorSchemaPath}" .${caller} is undefined.`;
449
+ this.log.warn(errorText);
450
+ throw new Error(errorText);
451
+ }
452
+ return initialPWLocator;
453
+ };
454
+ /**
455
+ * Creates a new method that returns a Playwright Locator using the specified method name.
456
+ *
457
+ * @param methodName The name of the method to use for getting the element.
458
+ * @returns An async function that takes a locator and returns a Playwright Locator.
459
+ */
460
+ createByMethod = (methodName) => {
461
+ return (locator) => {
462
+ return this.getBy(methodName, locator);
463
+ };
464
+ };
465
+ /**
466
+ * Returns a {@link Locator} using the selectors 'role' (required) and 'roleOptions' (optional), from a {@link LocatorSchema} Object.
467
+ *
468
+ * @param locator - The locator.
469
+ * @returns - A promise that resolves to the {@link Locator}.
470
+ */
471
+ role = this.createByMethod("role" /* role */);
472
+ /**
473
+ * Returns a {@link Locator} using the selectors 'text' (required) and 'textOptions' (optional), from a {@link LocatorSchema} Object.
474
+ *
475
+ * @param locator - The locator.
476
+ * @returns - A promise that resolves to the {@link Locator}.
477
+ */
478
+ text = this.createByMethod("text" /* text */);
479
+ /**
480
+ * Returns a {@link Locator} using the selectors 'label' (required) and 'labelOptions' (optional), from a {@link LocatorSchema} Object.
481
+ *
482
+ * @param locator - The locator.
483
+ * @returns - A promise that resolves to the {@link Locator}.
484
+ */
485
+ label = this.createByMethod("label" /* label */);
486
+ /**
487
+ * Returns a {@link Locator} using the selectors 'placeholder' (required) and 'placeholderOptions' (optional), from a {@link LocatorSchema} Object.
488
+ *
489
+ * @param locator - The locator.
490
+ * @returns - A promise that resolves to the {@link Locator}.
491
+ */
492
+ placeholder = this.createByMethod("placeholder" /* placeholder */);
493
+ /**
494
+ * Returns a {@link Locator} using the selectors 'altText' (required) and 'altTextOptions' (optional), from a {@link LocatorSchema} Object.
495
+ *
496
+ * @param locator - The locator.
497
+ * @returns - A promise that resolves to the {@link Locator}.
498
+ */
499
+ altText = this.createByMethod("altText" /* altText */);
500
+ /**
501
+ * Returns a {@link Locator} using the selectors 'title' (required) and 'titleOptions' (optional), from a {@link LocatorSchema} Object.
502
+ *
503
+ * @param locator - The locator.
504
+ * @returns - A promise that resolves to the {@link Locator}.
505
+ */
506
+ title = this.createByMethod("title" /* title */);
507
+ /**
508
+ * Returns a {@link Locator} using the selectors 'locator' (required), from a {@link LocatorSchema} Object.
509
+ *
510
+ * @param locator - The locator.
511
+ * @returns - A promise that resolves to the {@link Locator}.
512
+ */
513
+ locator = this.createByMethod("locator" /* locator */);
514
+ /**
515
+ * Returns a {@link FrameLocator} using the selector string 'frameLocator' (required), from a {@link LocatorSchema} Object.
516
+ *
517
+ * @param locatorSchema - Which contains the frameLocator selector.
518
+ * @returns A promise that resolves to the {@link FrameLocator}.
519
+ */
520
+ frameLocator = (locatorSchema) => {
521
+ const initialFrameLocator = locatorSchema.frameLocator ? this.page.frameLocator(locatorSchema.frameLocator) : null;
522
+ if (!initialFrameLocator) {
523
+ const errorText = `Locator "${locatorSchema.locatorSchemaPath}" .frameLocator is not defined.`;
524
+ this.log.warn(errorText);
525
+ throw new Error(errorText);
526
+ }
527
+ return initialFrameLocator;
528
+ };
529
+ /**
530
+ * Returns a {@link Locator} using the selectors 'testId' (required), from a {@link LocatorSchema} Object.
531
+ *
532
+ * @param locator - The locator.
533
+ * @returns - A promise that resolves to the {@link Locator}.
534
+ */
535
+ testId = (locator) => {
536
+ const initialPWLocator = locator.testId ? this.page.getByTestId(locator.testId) : null;
537
+ if (!initialPWLocator) {
538
+ const errorText = `Locator "${locator.locatorSchemaPath}" .testId is not defined.`;
539
+ this.log.warn(`Locator "${locator.locatorSchemaPath}" .testId is not defined.`);
540
+ throw new Error(errorText);
541
+ }
542
+ return initialPWLocator;
543
+ };
544
+ /**
545
+ * Returns a {@link Locator} using the selectors 'dataCy' (required), from a {@link LocatorSchema} Object.
546
+ *
547
+ * @param locator - The locator.
548
+ * @returns - A promise that resolves to the {@link Locator}.
549
+ */
550
+ dataCy = (locator) => {
551
+ let initialPWLocator = null;
552
+ if (locator.dataCy) {
553
+ initialPWLocator = locator.dataCy.startsWith("data-cy=") ? this.page.locator(locator.dataCy) : this.page.locator(`data-cy=${locator.dataCy}`);
554
+ } else {
555
+ const errorText = `Locator "${locator.locatorSchemaPath}" .dataCy is undefined.`;
556
+ this.log.warn(errorText);
557
+ throw new Error(errorText);
558
+ }
559
+ return initialPWLocator;
560
+ };
561
+ /**
562
+ * Returns a {@link Locator} using the selectors 'id' (required), from a {@link LocatorSchema} Object.
563
+ *
564
+ * @param locator - The locator.
565
+ * @returns - A promise that resolves to the {@link Locator}.
566
+ */
567
+ id = (locator) => {
568
+ let initialPWLocator = null;
569
+ let selector;
570
+ let regexPattern;
571
+ if (!locator.id) {
572
+ const errorText = `Locator "${locator.locatorSchemaPath}" .id is not defined.`;
573
+ this.log.warn(errorText);
574
+ throw new Error(errorText);
575
+ }
576
+ if (typeof locator.id === "string") {
577
+ if (locator.id.startsWith("#")) {
578
+ selector = locator.id;
579
+ } else if (locator.id.startsWith("id=")) {
580
+ selector = `#${locator.id.slice("id=".length)}`;
581
+ } else {
582
+ selector = `#${locator.id}`;
583
+ }
584
+ } else if (locator.id instanceof RegExp) {
585
+ regexPattern = locator.id.source;
586
+ selector = `*[id^="${regexPattern}"]`;
587
+ } else {
588
+ const errorText = `Unsupported id type: ${typeof locator.id}`;
589
+ this.log.error(errorText);
590
+ throw new Error(errorText);
591
+ }
592
+ initialPWLocator = this.page.locator(selector);
593
+ return initialPWLocator;
594
+ };
595
+ };
596
+
597
+ // src/helpers/getLocatorBase.ts
598
+ var GetLocatorBase = class {
599
+ /**
600
+ * Constructor for the GetLocatorBaseClass.
601
+ *
602
+ * @param {BasePage} pageObjectClass - The page object class to which the locator pertains.
603
+ * @param {PlaywrightReportLogger} log - The PlaywrightReportLogger child of the page object class.
604
+ */
605
+ constructor(pageObjectClass, log) {
606
+ this.pageObjectClass = pageObjectClass;
607
+ this.log = log;
608
+ this.locatorSchemas = /* @__PURE__ */ new Map();
609
+ this.getBy = new GetBy(this.pageObjectClass.page, this.log.getNewChildLogger("GetBy"));
610
+ }
611
+ getBy;
612
+ locatorSchemas;
613
+ /**
614
+ * test
615
+ * @param locatorSchemaPath
616
+ * @returns
617
+ */
618
+ getLocatorSchema(locatorSchemaPath) {
619
+ const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath);
620
+ const schemasMap = this.collectDeepCopies(locatorSchemaPath, pathIndexPairs);
621
+ const locatorSchemaCopy = schemasMap.get(locatorSchemaPath);
622
+ locatorSchemaCopy.schemasMap = schemasMap;
623
+ const self = this;
624
+ locatorSchemaCopy.update = function(updates) {
625
+ self.applyUpdate(schemasMap, locatorSchemaPath, updates);
626
+ return this;
627
+ };
628
+ locatorSchemaCopy.updates = function(indexedUpdates) {
629
+ self.applyUpdates(schemasMap, pathIndexPairs, indexedUpdates);
630
+ return this;
631
+ };
632
+ locatorSchemaCopy.getNestedLocator = async (indices) => {
633
+ return await this.buildNestedLocator(locatorSchemaPath, indices, schemasMap);
634
+ };
635
+ locatorSchemaCopy.getLocator = async () => {
636
+ return this.getBy.getLocator(locatorSchemaCopy);
637
+ };
638
+ return locatorSchemaCopy;
639
+ }
640
+ collectDeepCopies(locatorSchemaPath, pathIndexPairs) {
641
+ const schemasMap = /* @__PURE__ */ new Map();
642
+ const fullSchemaFunc = this.safeGetLocatorSchema(locatorSchemaPath);
643
+ if (!fullSchemaFunc) {
644
+ const errorMessage = `LocatorSchema not found for path: '${locatorSchemaPath}'`;
645
+ this.log.error(errorMessage);
646
+ throw new Error(`[${this.pageObjectClass.pocName}] ${errorMessage}`);
647
+ }
648
+ schemasMap.set(locatorSchemaPath, structuredClone(fullSchemaFunc()));
649
+ for (const { path } of pathIndexPairs) {
650
+ if (path !== locatorSchemaPath) {
651
+ const schemaFunc = this.safeGetLocatorSchema(path);
652
+ if (schemaFunc) {
653
+ schemasMap.set(path, structuredClone(schemaFunc()));
654
+ }
655
+ }
656
+ }
657
+ return schemasMap;
658
+ }
659
+ applyUpdate(schemasMap, locatorSchemaPath, updateData) {
660
+ const schema = schemasMap.get(locatorSchemaPath);
661
+ if (schema) {
662
+ schemasMap.set(locatorSchemaPath, this.deepMerge(schema, updateData));
663
+ }
664
+ }
665
+ applyUpdates(schemasMap, pathIndexPairs, updatesData) {
666
+ Object.entries(updatesData).forEach(([index, updateAtIndex]) => {
667
+ const path = pathIndexPairs[parseInt(index)]?.path;
668
+ if (path && updateAtIndex) {
669
+ const schema = schemasMap.get(path);
670
+ if (schema) {
671
+ schemasMap.set(path, this.deepMerge(schema, updateAtIndex));
672
+ }
673
+ }
674
+ });
675
+ }
676
+ createLocatorSchema(schemaDetails, locatorSchemaPath) {
677
+ const schema = { ...schemaDetails, locatorSchemaPath };
678
+ return schema;
679
+ }
680
+ addSchema(locatorSchemaPath, schemaDetails) {
681
+ const newLocatorSchema = this.createLocatorSchema(schemaDetails, locatorSchemaPath);
682
+ const existingSchemaFunc = this.safeGetLocatorSchema(locatorSchemaPath);
683
+ if (existingSchemaFunc) {
684
+ const existingLocatorSchema = existingSchemaFunc();
685
+ throw new Error(
686
+ `[${this.pageObjectClass.pocName}] A LocatorSchema with the path '${locatorSchemaPath}' already exists.
687
+ Existing Schema: ${JSON.stringify(existingLocatorSchema, null, 2)}
688
+ Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
689
+ );
690
+ }
691
+ this.locatorSchemas.set(locatorSchemaPath, () => newLocatorSchema);
692
+ }
693
+ safeGetLocatorSchema(path) {
694
+ return this.locatorSchemas.get(path);
695
+ }
696
+ extractPathsFromSchema = (paths, indices = {}) => {
697
+ const schemaParts = paths.split(".");
698
+ let cumulativePath = "";
699
+ return schemaParts.map((part, index) => {
700
+ cumulativePath = cumulativePath ? `${cumulativePath}.${part}` : part;
701
+ return {
702
+ path: cumulativePath,
703
+ index: indices[index] ?? void 0
704
+ };
705
+ });
706
+ };
707
+ /**
708
+ * logError is a utility function for logging errors with detailed debug information
709
+ * It re-throws the error after logging to ensure the test will fail.
710
+ */
711
+ logError = (error, locatorSchemaPath, currentLocator, currentPath, pathIndexPairs, nestedLocatorResults) => {
712
+ const errorDetails = {
713
+ error: error.message,
714
+ locatorSchemaPath,
715
+ currentPath,
716
+ pathIndexPairs: JSON.stringify(pathIndexPairs, null, 2),
717
+ currentLocatorDetails: currentLocator ? {
718
+ locatorString: currentLocator,
719
+ isNotNull: true
720
+ } : { isNotNull: false },
721
+ nestedLocatorResults
722
+ };
723
+ this.log.error(
724
+ "An error occurred during nested locator construction.\n",
725
+ "Error details:\n",
726
+ JSON.stringify(errorDetails, null, 2)
727
+ );
728
+ throw error;
729
+ };
730
+ // Merges 'source' into 'target', combining their properties into a new isolated object.
731
+ deepMerge(target, source) {
732
+ const merged = { ...target };
733
+ const dummySchema = getLocatorSchemaDummy();
734
+ if (typeof source === "object" && source !== null) {
735
+ Object.keys(source).filter((key) => key !== "locatorSchemaPath").forEach((key) => {
736
+ const targetKey = key;
737
+ const sourceKey = key;
738
+ if (!(key in dummySchema)) {
739
+ throw new Error(`Invalid property: '${key}' is not a valid property of LocatorSchema`);
740
+ }
741
+ const targetValue = merged[targetKey];
742
+ const sourceValue = source[sourceKey];
743
+ if (sourceValue !== void 0) {
744
+ if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
745
+ merged[targetKey] = [...targetValue, ...sourceValue];
746
+ } else if (sourceValue instanceof RegExp) {
747
+ merged[targetKey] = new RegExp(sourceValue.source, sourceValue.flags);
748
+ } else if (typeof sourceValue === "object" && sourceValue !== null) {
749
+ merged[targetKey] = targetValue ? this.deepMerge(targetValue, sourceValue) : structuredClone(sourceValue);
750
+ } else {
751
+ merged[targetKey] = sourceValue;
752
+ }
753
+ }
754
+ });
755
+ }
756
+ return merged;
757
+ }
758
+ buildNestedLocator = async (locatorSchemaPath, indices = {}, schemasMap) => {
759
+ return await test2.step(`${this.pageObjectClass.pocName}: Build Nested Locator`, async () => {
760
+ const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath, indices);
761
+ let currentLocator = null;
762
+ let currentIFrame = null;
763
+ const nestedLocatorResults = {
764
+ LocatorSchema: null,
765
+ // Initialize as an empty object
766
+ NestingSteps: []
767
+ };
768
+ for (const { path, index } of pathIndexPairs) {
769
+ const currentSchema = schemasMap.get(path);
770
+ if (!currentSchema)
771
+ continue;
772
+ try {
773
+ const nextLocator = this.getBy.getLocator(currentSchema);
774
+ if (currentLocator) {
775
+ currentLocator = currentLocator.locator(nextLocator);
776
+ if (index != null) {
777
+ currentLocator = currentLocator.nth(index);
778
+ }
779
+ } else {
780
+ currentLocator = nextLocator;
781
+ if (index != null) {
782
+ currentLocator = currentLocator.nth(index);
783
+ }
784
+ }
785
+ if (this.log.isLogLevelEnabled("debug")) {
786
+ if (!nestedLocatorResults.LocatorSchema) {
787
+ const safeGetLocatorSchema = this.safeGetLocatorSchema(locatorSchemaPath);
788
+ if (safeGetLocatorSchema !== void 0) {
789
+ nestedLocatorResults.LocatorSchema = safeGetLocatorSchema();
790
+ }
791
+ }
792
+ if (currentSchema.locatorMethod === "frameLocator" /* frameLocator */) {
793
+ if (!currentIFrame) {
794
+ currentIFrame = currentSchema.frameLocator;
795
+ }
796
+ if (currentIFrame != null && currentSchema.frameLocator != null) {
797
+ if (currentIFrame.endsWith(currentSchema.frameLocator)) {
798
+ currentIFrame += ` -> ${currentSchema.frameLocator}`;
799
+ }
800
+ }
801
+ }
802
+ if (currentIFrame !== void 0) {
803
+ await this.evaluateCurrentLocator(currentLocator, nestedLocatorResults.NestingSteps, currentIFrame);
804
+ } else {
805
+ await this.evaluateCurrentLocator(currentLocator, nestedLocatorResults.NestingSteps, null);
806
+ }
807
+ }
808
+ } catch (error) {
809
+ this.logError(error, locatorSchemaPath, currentLocator, path, pathIndexPairs, nestedLocatorResults);
810
+ break;
811
+ }
812
+ }
813
+ if (!currentLocator) {
814
+ this.logError(
815
+ new Error(`Failed to build nested locator for path: ${locatorSchemaPath}`),
816
+ locatorSchemaPath,
817
+ currentLocator,
818
+ locatorSchemaPath,
819
+ pathIndexPairs
820
+ );
821
+ }
822
+ if (this.log.isLogLevelEnabled("debug")) {
823
+ this.log.debug("Nested locator evaluation results:", JSON.stringify(nestedLocatorResults, null, 2));
824
+ }
825
+ if (currentLocator != null) {
826
+ currentLocator.scrollIntoViewIfNeeded().catch(() => {
827
+ });
828
+ return currentLocator;
829
+ } else {
830
+ throw new Error(
831
+ `"currentLocator" is null or undefined. Failed to build nested locator for path: ${locatorSchemaPath}`
832
+ );
833
+ }
834
+ });
835
+ };
836
+ evaluateCurrentLocator = async (currentLocator, resultsArray, currentIFrame) => {
837
+ if (currentIFrame) {
838
+ resultsArray.push({
839
+ currentLocatorString: currentLocator,
840
+ currentIFrame,
841
+ Note: "iFrame locators evaluation not implemented"
842
+ });
843
+ } else {
844
+ const elementsData = await this.evaluateAndGetAttributes(currentLocator);
845
+ resultsArray.push({
846
+ currentLocatorString: currentLocator,
847
+ resolved: elementsData.length > 0,
848
+ elementCount: elementsData.length,
849
+ elementsResolvedTo: elementsData
850
+ });
851
+ }
852
+ };
853
+ /**
854
+ * Evaluates the Playwright locator, checking if it resolves to any elements, and retrieves element attributes.
855
+ *
856
+ * @param pwLocator - The Playwright locator to evaluate.
857
+ * @returns - A promise that resolves to an object containing the Playwright locator and an array of element attributes for each element located, or null if no elements are found.
858
+ */
859
+ evaluateAndGetAttributes = async (pwLocator) => {
860
+ return await pwLocator.evaluateAll(
861
+ (objects) => objects.map((el) => {
862
+ const elementAttributes = el.hasAttributes() ? Object.fromEntries(Array.from(el.attributes).map(({ name, value }) => [name, value])) : {};
863
+ return { tagName: el.tagName, attributes: elementAttributes };
864
+ })
865
+ );
866
+ };
867
+ };
868
+
869
+ // src/basePage.ts
870
+ var selectorRegistered = false;
871
+ var BasePage2 = class {
872
+ /** Provides Playwright page methods */
873
+ page;
874
+ /** Playwright TestInfo contains information about currently running test, available to any test function */
875
+ testInfo;
876
+ /** Selectors can be used to install custom selector engines.*/
877
+ selector;
878
+ /** The base URL of the Page Object Class */
879
+ baseUrl;
880
+ /** The URL path of the Page Object Class */
881
+ urlPath;
882
+ fullUrl;
883
+ /** The name of the Page Object Class */
884
+ pocName;
885
+ /** The Page Object Class' PlaywrightReportLogger instance, prefixed with its name. Log levels: debug, info, warn, and error. */
886
+ log;
887
+ /** The SessionStorage class provides methods for setting and getting session storage data in Playwright.*/
888
+ sessionStorage;
889
+ locators;
890
+ constructor(page, testInfo, baseUrl, urlPath, pocName, pwrl) {
891
+ this.page = page;
892
+ this.testInfo = testInfo;
893
+ this.selector = selectors;
894
+ this.baseUrl = baseUrl;
895
+ this.urlPath = urlPath;
896
+ this.fullUrl = `${this.baseUrl}${this.urlPath}`;
897
+ this.pocName = pocName;
898
+ this.log = pwrl.getNewChildLogger(pocName);
899
+ this.locators = new GetLocatorBase(this, this.log.getNewChildLogger("GetLocator"));
900
+ this.initLocatorSchemas();
901
+ this.sessionStorage = new SessionStorage(this.page, this.pocName);
902
+ if (!selectorRegistered) {
903
+ selectors.register("data-cy", createCypressIdEngine);
904
+ selectorRegistered = true;
905
+ }
906
+ }
907
+ /**
908
+ * Asynchronously retrieves a nested locator based on the provided LocatorSchemaPath and optional indices per nested locator.
909
+ * Useful for interacting with elements in a structured or hierarchical manner, reducing the number of possible elements we can resolve to.
910
+ *
911
+ * The update and updates methods cannot be used with this method. Use the getLocatorSchema method instead.
912
+ *
913
+ * @param LocatorSchemaPathType locatorSchemaPath - The unique path identifier for the locator schema.
914
+ * @param indices - An optional object to specify the nth occurrence of each nested locator.
915
+ * @returns Promise<Locator> - A promise that resolves to the nested locator.
916
+ */
917
+ getNestedLocator = async (locatorSchemaPath, indices) => {
918
+ return await this.getLocatorSchema(locatorSchemaPath).getNestedLocator(indices);
919
+ };
920
+ /**
921
+ * Asynchronously retrieves the locator based on the current LocatorSchemaPath. This method does not perform nesting.
922
+ * Useful for directly interacting with an element based on its LocatorSchema.
923
+ *
924
+ * The update and updates methods cannot be used with this method. Use the getLocatorSchema method instead.
925
+ *
926
+ * @param LocatorSchemaPathType locatorSchemaPath - The unique path identifier for the locator schema.
927
+ * @returns Promise<Locator> - A promise that resolves to the nested locator.
928
+ */
929
+ getLocator = async (locatorSchemaPath) => {
930
+ return await this.getLocatorSchema(locatorSchemaPath).getLocator();
931
+ };
932
+ /**
933
+ * The "getLocatorSchema" method is used to retrieve a deep copy of a locator schema defined in the GetLocatorBase class.
934
+ * It enriches the returned schema with additional methods to handle updates and retrieval of deep copy locators.
935
+ *
936
+ * @param LocatorSchemaPathType locatorSchemaPath - The unique path identifier for the locator schema.
937
+ * @returns LocatorSchemaWithMethods - A deep copy of the locator schema with additional methods.
938
+ *
939
+ * Methods added to the returned LocatorSchemaWithMethods object:
940
+ *
941
+ * - update(updates: Partial<UpdatableLocatorSchemaProperties>):
942
+ * Allows updating properties of the locator schema.
943
+ * This method is used for modifying the current schema without affecting the original schema.
944
+ * @param updates - An object with properties to be updated in the locator schema, omits the locatorSchemaPath parameter.
945
+ * @returns LocatorSchemaWithMethods - The updated locator schema object.
946
+ *
947
+ * - updates(indexedUpdates: { [index: number]: Partial<UpdatableLocatorSchemaProperties> | null }):
948
+ * Similar to update, but allows for indexed updates of any locator to be nested within the path.
949
+ * This method is used for modifying the current schema without affecting the original schema
950
+ * @param indexedUpdates - An object where keys represent index levels, and values are the updates at each level.
951
+ * @returns LocatorSchemaWithMethods - The locator schema object with indexed updates.
952
+ *
953
+ * - getNestedLocator(indices?: { [key: number]: number | null }):
954
+ * Asynchronously retrieves a nested locator based on the current schema and optional indices per nested locator.
955
+ * Useful for interacting with elements in a structured or hierarchical manner.
956
+ * @param indices - An optional object to specify the nth occurrence of each nested locator.
957
+ * @returns Promise<Locator> - A promise that resolves to the nested locator.
958
+ *
959
+ * - getLocator():
960
+ * Asynchronously retrieves the locator based on the current schema. This method does not consider nesting.
961
+ * Useful for directly interacting with an element based on its schema.
962
+ * @returns Promise<Locator> - A promise that resolves to the locator.
963
+ */
964
+ getLocatorSchema(locatorSchemaPath) {
965
+ return this.locators.getLocatorSchema(locatorSchemaPath);
966
+ }
967
+ };
968
+
969
+ // src/fixture/base.fixtures.ts
970
+ import { test as base } from "@playwright/test";
971
+ var test3 = base.extend({
972
+ log: async ({}, use, testInfo) => {
973
+ const contextName = "TestCase";
974
+ const sharedLogEntry = [];
975
+ let sharedLogLevel = testInfo.retry === 0 ? { current: "warn", initial: "warn" } : { current: "debug", initial: "debug" };
976
+ const log = new PlaywrightReportLogger(sharedLogLevel, sharedLogEntry, contextName);
977
+ await use(log);
978
+ log.attachLogsToTest(testInfo);
979
+ }
980
+ });
981
+
982
+ // src/api/baseApi.ts
983
+ var BaseApi = class {
984
+ baseUrl;
985
+ apiName;
986
+ log;
987
+ request;
988
+ constructor(baseUrl, apiName, context, pwrl) {
989
+ this.baseUrl = baseUrl;
990
+ this.apiName = apiName;
991
+ this.log = pwrl.getNewChildLogger(apiName);
992
+ this.request = context;
993
+ }
994
+ };
995
+ export {
996
+ GetByMethod,
997
+ BasePage2 as POMWright,
998
+ BaseApi as POMWrightApi,
999
+ GetLocatorBase as POMWrightGetLocatorBase,
1000
+ PlaywrightReportLogger as POMWrightLogger,
1001
+ test3 as POMWrightTestFixture
1002
+ };