pomwright 1.5.0 → 2.0.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 +36 -0
- package/README.md +5 -5
- package/dist/index.d.mts +91 -989
- package/dist/index.d.ts +91 -989
- package/dist/index.js +627 -1887
- package/dist/index.mjs +633 -1888
- package/package.json +9 -11
- package/AGENTS.md +0 -37
- package/docs/v1/BaseApi-explanation.md +0 -63
- package/docs/v1/BasePage-explanation.md +0 -96
- package/docs/v1/LocatorSchema-explanation.md +0 -271
- package/docs/v1/LocatorSchemaPath-explanation.md +0 -165
- package/docs/v1/PlaywrightReportLogger-explanation.md +0 -56
- package/docs/v1/get-locator-methods-explanation.md +0 -250
- package/docs/v1/intro-to-using-pomwright.md +0 -899
- package/docs/v1/sessionStorage-methods-explanation.md +0 -38
- package/docs/v1/tips-folder-structure.md +0 -38
- package/docs/v1-to-v2-migration/bridge-migration-guide.md +0 -159
- package/docs/v1-to-v2-migration/direct-migration-guide.md +0 -238
- package/docs/v1-to-v2-migration/v1-to-v2-comparison.md +0 -547
- package/docs/v2/PageObject.md +0 -293
- package/docs/v2/composing-locator-modules.md +0 -93
- package/docs/v2/locator-registry.md +0 -693
- package/docs/v2/logging.md +0 -168
- package/docs/v2/overview.md +0 -515
- package/docs/v2/session-storage.md +0 -160
- package/index.ts +0 -75
- package/intTestV2/.env +0 -0
- package/intTestV2/fixtures/testApp.fixtures.ts +0 -43
- package/intTestV2/package.json +0 -22
- package/intTestV2/page-object-models/testApp/pages/iframe/iframe.locatorSchema.ts +0 -24
- package/intTestV2/page-object-models/testApp/pages/iframe/iframe.page.ts +0 -17
- package/intTestV2/page-object-models/testApp/pages/testPage.locatorSchema.ts +0 -32
- package/intTestV2/page-object-models/testApp/pages/testPage.page.ts +0 -119
- package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.locatorSchema.ts +0 -29
- package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.page.ts +0 -48
- package/intTestV2/page-object-models/testApp/pages/testPath/testPath.locatorSchema.ts +0 -9
- package/intTestV2/page-object-models/testApp/pages/testPath/testPath.page.ts +0 -23
- package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.locatorSchema.ts +0 -114
- package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.page.ts +0 -23
- package/intTestV2/page-object-models/testApp/testApp.base.ts +0 -20
- package/intTestV2/playwright.config.ts +0 -54
- package/intTestV2/server.js +0 -216
- package/intTestV2/test-data/staticPage/index.html +0 -280
- package/intTestV2/test-data/staticPage/w3images/avatar2.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar3.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar5.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar6.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/forest.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/lights.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/mountains.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/nature.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/snow.jpg +0 -0
- package/intTestV2/tests/locatorRegistry/add/add.describe.spec.ts +0 -54
- package/intTestV2/tests/locatorRegistry/add/add.filter.spec.ts +0 -143
- package/intTestV2/tests/locatorRegistry/add/add.frameLocator.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByAltText.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +0 -45
- package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +0 -66
- package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +0 -311
- package/intTestV2/tests/locatorRegistry/add/add.spec.ts +0 -159
- package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +0 -39
- package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +0 -253
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +0 -105
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +0 -368
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +0 -56
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +0 -175
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +0 -60
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +0 -32
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +0 -24
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +0 -110
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +0 -322
- package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +0 -412
- package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +0 -50
- package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +0 -115
- package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +0 -45
- package/intTestV2/tests/step/step.spec.ts +0 -49
- package/intTestV2/tests/testApp/color.spec.ts +0 -15
- package/intTestV2/tests/testApp/iframe.spec.ts +0 -57
- package/intTestV2/tests/testApp/testFilters.spec.ts +0 -24
- package/intTestV2/tests/testApp/testPage.spec.ts +0 -161
- package/intTestV2/tests/testApp/testPath.spec.ts +0 -18
- package/pack-build.sh +0 -11
- package/pack-test-v2.sh +0 -36
- package/playwright.base.ts +0 -42
- package/skills/README.md +0 -56
- package/skills/pomwright-v1-5-bridge-migration/SKILL.md +0 -40
- package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +0 -178
- package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +0 -183
- package/skills/pomwright-v2-migration/SKILL.md +0 -63
- package/skills/pomwright-v2-migration/references/call-site-migration.md +0 -265
- package/skills/pomwright-v2-migration/references/class-migration.md +0 -266
- package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +0 -423
- package/skills/pomwright-v2-migration/references/locator-registration.md +0 -344
- package/srcV2/fixture/base.fixtures.ts +0 -23
- package/srcV2/helpers/navigation.ts +0 -153
- package/srcV2/helpers/playwrightReportLogger.ts +0 -196
- package/srcV2/helpers/sessionStorage.ts +0 -251
- package/srcV2/helpers/stepDecorator.ts +0 -106
- package/srcV2/locators/index.ts +0 -15
- package/srcV2/locators/locatorQueryBuilder.ts +0 -427
- package/srcV2/locators/locatorRegistrationBuilder.ts +0 -558
- package/srcV2/locators/locatorRegistry.ts +0 -541
- package/srcV2/locators/locatorUpdateBuilder.ts +0 -602
- package/srcV2/locators/reusableLocatorBuilder.ts +0 -200
- package/srcV2/locators/types.ts +0 -256
- package/srcV2/locators/utils.ts +0 -309
- package/srcV2/locators/v1SchemaTranslator.ts +0 -178
- package/srcV2/pageObject.ts +0 -105
package/dist/index.mjs
CHANGED
|
@@ -1,1201 +1,311 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
GetByMethod2["role"] = "role";
|
|
4
|
-
GetByMethod2["text"] = "text";
|
|
5
|
-
GetByMethod2["label"] = "label";
|
|
6
|
-
GetByMethod2["placeholder"] = "placeholder";
|
|
7
|
-
GetByMethod2["altText"] = "altText";
|
|
8
|
-
GetByMethod2["title"] = "title";
|
|
9
|
-
GetByMethod2["locator"] = "locator";
|
|
10
|
-
GetByMethod2["frameLocator"] = "frameLocator";
|
|
11
|
-
GetByMethod2["testId"] = "testId";
|
|
12
|
-
GetByMethod2["dataCy"] = "dataCy";
|
|
13
|
-
GetByMethod2["id"] = "id";
|
|
14
|
-
return GetByMethod2;
|
|
15
|
-
})(GetByMethod || {});
|
|
16
|
-
var locatorSchemaDummy = {
|
|
17
|
-
role: void 0,
|
|
18
|
-
roleOptions: {
|
|
19
|
-
checked: void 0,
|
|
20
|
-
disabled: void 0,
|
|
21
|
-
exact: void 0,
|
|
22
|
-
expanded: void 0,
|
|
23
|
-
includeHidden: void 0,
|
|
24
|
-
level: void 0,
|
|
25
|
-
name: void 0,
|
|
26
|
-
pressed: void 0,
|
|
27
|
-
selected: void 0
|
|
28
|
-
},
|
|
29
|
-
text: void 0,
|
|
30
|
-
textOptions: {
|
|
31
|
-
exact: void 0
|
|
32
|
-
},
|
|
33
|
-
label: void 0,
|
|
34
|
-
labelOptions: {
|
|
35
|
-
exact: void 0
|
|
36
|
-
},
|
|
37
|
-
placeholder: void 0,
|
|
38
|
-
placeholderOptions: {
|
|
39
|
-
exact: void 0
|
|
40
|
-
},
|
|
41
|
-
altText: void 0,
|
|
42
|
-
altTextOptions: {
|
|
43
|
-
exact: void 0
|
|
44
|
-
},
|
|
45
|
-
title: void 0,
|
|
46
|
-
titleOptions: {
|
|
47
|
-
exact: void 0
|
|
48
|
-
},
|
|
49
|
-
locator: void 0,
|
|
50
|
-
locatorOptions: {
|
|
51
|
-
has: void 0,
|
|
52
|
-
hasNot: void 0,
|
|
53
|
-
hasNotText: void 0,
|
|
54
|
-
hasText: void 0
|
|
55
|
-
},
|
|
56
|
-
frameLocator: void 0,
|
|
57
|
-
testId: void 0,
|
|
58
|
-
dataCy: void 0,
|
|
59
|
-
id: void 0,
|
|
60
|
-
filter: {
|
|
61
|
-
has: void 0,
|
|
62
|
-
hasNot: void 0,
|
|
63
|
-
hasNotText: void 0,
|
|
64
|
-
hasText: void 0
|
|
65
|
-
},
|
|
66
|
-
locatorMethod: void 0,
|
|
67
|
-
locatorSchemaPath: void 0
|
|
68
|
-
};
|
|
69
|
-
function getLocatorSchemaDummy() {
|
|
70
|
-
return locatorSchemaDummy;
|
|
71
|
-
}
|
|
1
|
+
// src/fixture/base.fixtures.ts
|
|
2
|
+
import { test as base } from "@playwright/test";
|
|
72
3
|
|
|
73
|
-
// src/helpers/
|
|
74
|
-
var
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (Array.isArray(maybeSharedLogEntries)) {
|
|
81
|
-
return maybeSharedLogEntries;
|
|
4
|
+
// src/helpers/playwrightReportLogger.ts
|
|
5
|
+
var PlaywrightReportLogger = class _PlaywrightReportLogger {
|
|
6
|
+
// Initializes the logger with shared log level, log entries, and a context name.
|
|
7
|
+
constructor(sharedLogLevel, sharedLogEntry, contextName) {
|
|
8
|
+
this.sharedLogLevel = sharedLogLevel;
|
|
9
|
+
this.sharedLogEntry = sharedLogEntry;
|
|
10
|
+
this.contextName = contextName;
|
|
82
11
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
logger?.warn(message);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// src/api/baseApi.ts
|
|
97
|
-
var BaseApi = class {
|
|
98
|
-
baseUrl;
|
|
99
|
-
apiName;
|
|
100
|
-
log;
|
|
101
|
-
request;
|
|
102
|
-
constructor(baseUrl, apiName, context, pwrl) {
|
|
103
|
-
this.baseUrl = baseUrl;
|
|
104
|
-
this.apiName = apiName;
|
|
105
|
-
this.log = pwrl.getNewChildLogger(apiName);
|
|
106
|
-
this.request = context;
|
|
107
|
-
const classDeprecationMessage = "[POMWright] BaseApi is depricated and will be removed in 2.0.0 with no replacement. If you need a base API class, you can use the v1 pomwright/src/api/baseApi.ts implementation for reference to implement your own.";
|
|
108
|
-
warnDeprecationOncePerTest(`${this.constructor.name}-class-deprecation`, classDeprecationMessage, this.log);
|
|
12
|
+
contextName;
|
|
13
|
+
logLevels = ["debug", "info", "warn", "error"];
|
|
14
|
+
/**
|
|
15
|
+
* Creates a child logger with a new contextual name, sharing the same log level and log entries with the parent logger.
|
|
16
|
+
*
|
|
17
|
+
* The root loggers log "level" is referenced by all child loggers and their child loggers and so on...
|
|
18
|
+
* Changing the log "level" of one, will change it for all.
|
|
19
|
+
*/
|
|
20
|
+
getNewChildLogger(prefix) {
|
|
21
|
+
return new _PlaywrightReportLogger(this.sharedLogLevel, this.sharedLogEntry, `${this.contextName} -> ${prefix}`);
|
|
109
22
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Logs a message with the specified log level, prefix, and additional arguments if the current log level permits.
|
|
25
|
+
*/
|
|
26
|
+
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
27
|
+
log(level, message, ...args) {
|
|
28
|
+
const logLevelIndex = this.logLevels.indexOf(level);
|
|
29
|
+
if (logLevelIndex < this.getCurrentLogLevelIndex()) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.sharedLogEntry.push({
|
|
33
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
34
|
+
logLevel: level,
|
|
35
|
+
prefix: this.contextName,
|
|
36
|
+
message: `${message}
|
|
117
37
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const json = JSON.stringify(path);
|
|
121
|
-
return json.slice(1, -1);
|
|
122
|
-
};
|
|
123
|
-
var RUNTIME_WHITESPACE_REGEX = /[\s\u0085]/u;
|
|
124
|
-
var validateLocatorSchemaPath = (path) => {
|
|
125
|
-
if (!path) {
|
|
126
|
-
throw new Error("LocatorSchemaPath string cannot be empty");
|
|
38
|
+
${args.join("\n\n")}`
|
|
39
|
+
});
|
|
127
40
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Logs a debug-level message with the specified message and arguments.
|
|
43
|
+
*/
|
|
44
|
+
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
45
|
+
debug(message, ...args) {
|
|
46
|
+
this.log("debug", message, ...args);
|
|
131
47
|
}
|
|
132
|
-
|
|
133
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Logs a info-level message with the specified message and arguments.
|
|
50
|
+
*/
|
|
51
|
+
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
52
|
+
info(message, ...args) {
|
|
53
|
+
this.log("info", message, ...args);
|
|
134
54
|
}
|
|
135
|
-
|
|
136
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Logs a warn-level message with the specified message and arguments.
|
|
57
|
+
*/
|
|
58
|
+
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
59
|
+
warn(message, ...args) {
|
|
60
|
+
this.log("warn", message, ...args);
|
|
137
61
|
}
|
|
138
|
-
|
|
139
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Logs a error-level message with the specified message and arguments.
|
|
64
|
+
*/
|
|
65
|
+
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
66
|
+
error(message, ...args) {
|
|
67
|
+
this.log("error", message, ...args);
|
|
140
68
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
};
|
|
147
|
-
var cssEscape = (value) => {
|
|
148
|
-
return value.replace(/([\\"'#.:;,?*+<>{}[\\]()])/g, "\\$1");
|
|
149
|
-
};
|
|
150
|
-
var normalizeSteps = (steps) => steps ? steps.map((step2) => ({ ...step2 })) : [];
|
|
151
|
-
function normalizeIdValue(id) {
|
|
152
|
-
if (typeof id !== "string") {
|
|
153
|
-
return id;
|
|
69
|
+
/**
|
|
70
|
+
* Sets the current log level to the specified level during runTime.
|
|
71
|
+
*/
|
|
72
|
+
setLogLevel(level) {
|
|
73
|
+
this.sharedLogLevel.current = level;
|
|
154
74
|
}
|
|
155
|
-
|
|
156
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Retrieves the current log level during runtime.
|
|
77
|
+
*/
|
|
78
|
+
getCurrentLogLevel() {
|
|
79
|
+
return this.sharedLogLevel.current;
|
|
157
80
|
}
|
|
158
|
-
|
|
159
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Retrieves the index of the current log level in the logLevels array during runtime.
|
|
83
|
+
*/
|
|
84
|
+
getCurrentLogLevelIndex() {
|
|
85
|
+
return this.logLevels.indexOf(this.sharedLogLevel.current);
|
|
160
86
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
value,
|
|
167
|
-
(_key, current) => {
|
|
168
|
-
if (typeof current === "object" && current !== null) {
|
|
169
|
-
if (seen.has(current)) {
|
|
170
|
-
return "[Circular]";
|
|
171
|
-
}
|
|
172
|
-
seen.add(current);
|
|
173
|
-
}
|
|
174
|
-
if (current instanceof RegExp) {
|
|
175
|
-
return { type: "RegExp", source: current.source, flags: current.flags };
|
|
176
|
-
}
|
|
177
|
-
return current;
|
|
178
|
-
},
|
|
179
|
-
2
|
|
180
|
-
);
|
|
181
|
-
};
|
|
182
|
-
var applyIndexSelector = (locator, selector) => {
|
|
183
|
-
if (selector === void 0 || selector === null) {
|
|
184
|
-
return locator;
|
|
87
|
+
/**
|
|
88
|
+
* Resets the current log level to the initial level during runtime.
|
|
89
|
+
*/
|
|
90
|
+
resetLogLevel() {
|
|
91
|
+
this.sharedLogLevel.current = this.sharedLogLevel.initial;
|
|
185
92
|
}
|
|
186
|
-
|
|
187
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Checks if the input log level is equal to the current log level of the PlaywrightReportLogger instance.
|
|
95
|
+
*/
|
|
96
|
+
isCurrentLogLevel(level) {
|
|
97
|
+
return this.sharedLogLevel.current === level;
|
|
188
98
|
}
|
|
189
|
-
|
|
190
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Returns 'true' if the "level" parameter provided has an equal or greater index than the current logLevel.
|
|
101
|
+
*/
|
|
102
|
+
isLogLevelEnabled(level) {
|
|
103
|
+
const logLevelIndex = this.logLevels.indexOf(level);
|
|
104
|
+
if (logLevelIndex < this.getCurrentLogLevelIndex()) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
191
108
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Attaches the recorded log entries to the Playwright HTML report in a sorted and formatted manner.
|
|
111
|
+
*/
|
|
112
|
+
attachLogsToTest(testInfo) {
|
|
113
|
+
this.sharedLogEntry.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
114
|
+
for (const log of this.sharedLogEntry) {
|
|
115
|
+
const printTime = log.timestamp.toLocaleTimeString("nb-NO", {
|
|
116
|
+
hour: "2-digit",
|
|
117
|
+
minute: "2-digit",
|
|
118
|
+
second: "2-digit"
|
|
119
|
+
});
|
|
120
|
+
const printDate = log.timestamp.toLocaleDateString("nb-NO", {
|
|
121
|
+
day: "2-digit",
|
|
122
|
+
month: "2-digit",
|
|
123
|
+
year: "numeric"
|
|
124
|
+
});
|
|
125
|
+
const printLogLevel = `${log.logLevel.toUpperCase()}`;
|
|
126
|
+
const printPrefix = log.prefix ? `: [${log.prefix}]` : "";
|
|
127
|
+
let messageBody = "";
|
|
128
|
+
let messageContentType = "";
|
|
129
|
+
try {
|
|
130
|
+
const parsedMessage = JSON.parse(log.message);
|
|
131
|
+
messageContentType = "application/json";
|
|
132
|
+
messageBody = JSON.stringify(parsedMessage, null, 2);
|
|
133
|
+
} catch (_error) {
|
|
134
|
+
messageContentType = "text/plain";
|
|
135
|
+
messageBody = log.message;
|
|
218
136
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
default: {
|
|
224
|
-
const exhaustive = definition;
|
|
225
|
-
return exhaustive;
|
|
137
|
+
testInfo.attach(`${printTime} ${printDate} - ${printLogLevel} ${printPrefix}`, {
|
|
138
|
+
contentType: messageContentType,
|
|
139
|
+
body: Buffer.from(messageBody)
|
|
140
|
+
});
|
|
226
141
|
}
|
|
227
142
|
}
|
|
228
143
|
};
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
text: definition.text,
|
|
241
|
-
...definition.options ? { options: { ...definition.options } } : {}
|
|
242
|
-
};
|
|
243
|
-
case "label":
|
|
244
|
-
return {
|
|
245
|
-
type: "label",
|
|
246
|
-
text: definition.text,
|
|
247
|
-
...definition.options ? { options: { ...definition.options } } : {}
|
|
248
|
-
};
|
|
249
|
-
case "placeholder":
|
|
250
|
-
return {
|
|
251
|
-
type: "placeholder",
|
|
252
|
-
text: definition.text,
|
|
253
|
-
...definition.options ? { options: { ...definition.options } } : {}
|
|
254
|
-
};
|
|
255
|
-
case "altText":
|
|
256
|
-
return {
|
|
257
|
-
type: "altText",
|
|
258
|
-
text: definition.text,
|
|
259
|
-
...definition.options ? { options: { ...definition.options } } : {}
|
|
260
|
-
};
|
|
261
|
-
case "title":
|
|
262
|
-
return {
|
|
263
|
-
type: "title",
|
|
264
|
-
text: definition.text,
|
|
265
|
-
...definition.options ? { options: { ...definition.options } } : {}
|
|
266
|
-
};
|
|
267
|
-
case "locator":
|
|
268
|
-
return {
|
|
269
|
-
type: "locator",
|
|
270
|
-
selector: definition.selector,
|
|
271
|
-
...definition.options ? { options: { ...definition.options } } : {}
|
|
272
|
-
};
|
|
273
|
-
case "frameLocator":
|
|
274
|
-
return { type: "frameLocator", selector: definition.selector };
|
|
275
|
-
case "testId":
|
|
276
|
-
return { type: "testId", testId: definition.testId };
|
|
277
|
-
case "id":
|
|
278
|
-
return { type: "id", id: definition.id };
|
|
279
|
-
default: {
|
|
280
|
-
const exhaustive = definition;
|
|
281
|
-
return exhaustive;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
var applyDefinitionPatch = (seed, patch) => {
|
|
286
|
-
const base2 = cloneLocatorStrategyDefinition(seed);
|
|
287
|
-
switch (patch.type) {
|
|
288
|
-
case "locator": {
|
|
289
|
-
const selector = patch.selector !== void 0 ? patch.selector : base2.selector;
|
|
290
|
-
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
291
|
-
return { type: "locator", selector, ...options ? { options } : {} };
|
|
292
|
-
}
|
|
293
|
-
case "role": {
|
|
294
|
-
const role = patch.role ?? base2.role;
|
|
295
|
-
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
296
|
-
return { type: "role", role, ...options ? { options } : {} };
|
|
297
|
-
}
|
|
298
|
-
case "text": {
|
|
299
|
-
const text = patch.text ?? base2.text;
|
|
300
|
-
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
301
|
-
return { type: "text", text, ...options ? { options } : {} };
|
|
302
|
-
}
|
|
303
|
-
case "label": {
|
|
304
|
-
const text = patch.text ?? base2.text;
|
|
305
|
-
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
306
|
-
return { type: "label", text, ...options ? { options } : {} };
|
|
307
|
-
}
|
|
308
|
-
case "placeholder": {
|
|
309
|
-
const text = patch.text ?? base2.text;
|
|
310
|
-
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
311
|
-
return { type: "placeholder", text, ...options ? { options } : {} };
|
|
312
|
-
}
|
|
313
|
-
case "altText": {
|
|
314
|
-
const text = patch.text ?? base2.text;
|
|
315
|
-
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
316
|
-
return { type: "altText", text, ...options ? { options } : {} };
|
|
317
|
-
}
|
|
318
|
-
case "title": {
|
|
319
|
-
const text = patch.text ?? base2.text;
|
|
320
|
-
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
321
|
-
return { type: "title", text, ...options ? { options } : {} };
|
|
322
|
-
}
|
|
323
|
-
case "frameLocator": {
|
|
324
|
-
const selector = patch.selector !== void 0 ? patch.selector : base2.selector;
|
|
325
|
-
return { type: "frameLocator", selector };
|
|
326
|
-
}
|
|
327
|
-
case "testId": {
|
|
328
|
-
const testId = patch.testId !== void 0 ? patch.testId : base2.testId;
|
|
329
|
-
return { type: "testId", testId };
|
|
330
|
-
}
|
|
331
|
-
case "id": {
|
|
332
|
-
const id = patch.id !== void 0 ? normalizeIdValue(patch.id) ?? base2.id : base2.id;
|
|
333
|
-
return { type: "id", id };
|
|
334
|
-
}
|
|
335
|
-
default: {
|
|
336
|
-
const exhaustive = patch;
|
|
337
|
-
return exhaustive;
|
|
338
|
-
}
|
|
144
|
+
|
|
145
|
+
// src/fixture/base.fixtures.ts
|
|
146
|
+
var test = base.extend({
|
|
147
|
+
// biome-ignore lint/correctness/noEmptyPattern: Playwright does not support the use of _
|
|
148
|
+
log: async ({}, use, testInfo) => {
|
|
149
|
+
const contextName = "TestCase";
|
|
150
|
+
const sharedLogEntry = [];
|
|
151
|
+
const sharedLogLevel = testInfo.retry === 0 ? { current: "warn", initial: "warn" } : { current: "debug", initial: "debug" };
|
|
152
|
+
const log = new PlaywrightReportLogger(sharedLogLevel, sharedLogEntry, contextName);
|
|
153
|
+
await use(log);
|
|
154
|
+
log.attachLogsToTest(testInfo);
|
|
339
155
|
}
|
|
340
|
-
};
|
|
341
|
-
var isFrameLocatorDefinition = (definition) => definition.type === "frameLocator";
|
|
342
|
-
var isLocatorInstance = (value) => {
|
|
343
|
-
return !!value && typeof value === "object" && typeof value.filter === "function";
|
|
344
|
-
};
|
|
156
|
+
});
|
|
345
157
|
|
|
346
|
-
//
|
|
347
|
-
|
|
348
|
-
var
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
var logLocatorInstanceWarning = (path) => {
|
|
354
|
-
console.warn(
|
|
355
|
-
`[POMWright] Skipping v2 translation for "${path}" because v1 LocatorSchema.locator is a Locator instance. Rewrite this path in defineLocators() to avoid runtime gaps during migration.`
|
|
356
|
-
);
|
|
357
|
-
};
|
|
358
|
-
var addV1SchemaToV2Registry = (registry, locatorSchema) => {
|
|
359
|
-
const path = locatorSchema.locatorSchemaPath;
|
|
360
|
-
validateLocatorSchemaPath(path);
|
|
361
|
-
const registryWithLookup = getRegistryLookup(registry);
|
|
362
|
-
const existing = registryWithLookup.getIfExists?.(path);
|
|
363
|
-
if (existing) {
|
|
364
|
-
return;
|
|
158
|
+
// src/helpers/sessionStorage.ts
|
|
159
|
+
import { test as test2 } from "@playwright/test";
|
|
160
|
+
var SessionStorage = class {
|
|
161
|
+
// Initializes the class with a Playwright Page object and an optional label for step titles.
|
|
162
|
+
constructor(page, options = {}) {
|
|
163
|
+
this.page = page;
|
|
164
|
+
this.options = options;
|
|
365
165
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
case "text" /* text */: {
|
|
384
|
-
if (!locatorSchema.text) {
|
|
385
|
-
logMissingDefinition(path, "text");
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
postDefinition = registration.getByText(locatorSchema.text, locatorSchema.textOptions);
|
|
389
|
-
break;
|
|
390
|
-
}
|
|
391
|
-
case "label" /* label */: {
|
|
392
|
-
if (!locatorSchema.label) {
|
|
393
|
-
logMissingDefinition(path, "label");
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
postDefinition = registration.getByLabel(locatorSchema.label, locatorSchema.labelOptions);
|
|
397
|
-
break;
|
|
398
|
-
}
|
|
399
|
-
case "placeholder" /* placeholder */: {
|
|
400
|
-
if (!locatorSchema.placeholder) {
|
|
401
|
-
logMissingDefinition(path, "placeholder");
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
postDefinition = registration.getByPlaceholder(locatorSchema.placeholder, locatorSchema.placeholderOptions);
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
case "altText" /* altText */: {
|
|
408
|
-
if (!locatorSchema.altText) {
|
|
409
|
-
logMissingDefinition(path, "altText");
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
postDefinition = registration.getByAltText(locatorSchema.altText, locatorSchema.altTextOptions);
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
case "title" /* title */: {
|
|
416
|
-
if (!locatorSchema.title) {
|
|
417
|
-
logMissingDefinition(path, "title");
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
postDefinition = registration.getByTitle(locatorSchema.title, locatorSchema.titleOptions);
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
case "locator" /* locator */: {
|
|
424
|
-
if (!locatorSchema.locator) {
|
|
425
|
-
logMissingDefinition(path, "locator");
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
if (isLocatorInstance(locatorSchema.locator)) {
|
|
429
|
-
logLocatorInstanceWarning(path);
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
postDefinition = registration.locator(locatorSchema.locator, locatorSchema.locatorOptions);
|
|
433
|
-
break;
|
|
434
|
-
}
|
|
435
|
-
case "frameLocator" /* frameLocator */: {
|
|
436
|
-
if (!locatorSchema.frameLocator) {
|
|
437
|
-
logMissingDefinition(path, "frameLocator");
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
postDefinition = registration.frameLocator(locatorSchema.frameLocator);
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
case "testId" /* testId */: {
|
|
444
|
-
if (!locatorSchema.testId) {
|
|
445
|
-
logMissingDefinition(path, "testId");
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
postDefinition = registration.getByTestId(locatorSchema.testId);
|
|
449
|
-
break;
|
|
450
|
-
}
|
|
451
|
-
case "dataCy" /* dataCy */: {
|
|
452
|
-
if (!locatorSchema.dataCy) {
|
|
453
|
-
logMissingDefinition(path, "dataCy");
|
|
166
|
+
// Defines an object to hold states to be set in session storage, allowing any value type.
|
|
167
|
+
queuedStates = {};
|
|
168
|
+
// Indicates if the session storage manipulation has been initiated.
|
|
169
|
+
isInitiated = false;
|
|
170
|
+
getStepLabel(methodName) {
|
|
171
|
+
const prefix = this.options.label ? `${this.options.label}.` : "";
|
|
172
|
+
return `${prefix}SessionStorage.${methodName}:`;
|
|
173
|
+
}
|
|
174
|
+
async hasContext() {
|
|
175
|
+
return await this.page.evaluate(() => {
|
|
176
|
+
return typeof window !== "undefined" && window.sessionStorage !== void 0;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async waitForContextAvailability() {
|
|
180
|
+
try {
|
|
181
|
+
const contextExists = await this.hasContext();
|
|
182
|
+
if (contextExists) {
|
|
454
183
|
return;
|
|
455
184
|
}
|
|
456
|
-
|
|
457
|
-
break;
|
|
185
|
+
} catch (_e) {
|
|
458
186
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
187
|
+
await new Promise((resolve) => {
|
|
188
|
+
const handler = async (frame) => {
|
|
189
|
+
if (frame !== this.page.mainFrame()) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const contextExists = await this.hasContext();
|
|
194
|
+
if (!contextExists) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
} catch (_e) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
this.page.off("framenavigated", handler);
|
|
201
|
+
resolve();
|
|
202
|
+
};
|
|
203
|
+
this.page.on("framenavigated", handler);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async ensureContext({ waitForContext = false } = {}) {
|
|
207
|
+
try {
|
|
208
|
+
const contextExists = await this.hasContext();
|
|
209
|
+
if (contextExists) {
|
|
462
210
|
return;
|
|
463
211
|
}
|
|
464
|
-
|
|
465
|
-
break;
|
|
212
|
+
} catch (_e) {
|
|
466
213
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return exhaustive;
|
|
214
|
+
if (!waitForContext) {
|
|
215
|
+
throw new Error("SessionStorage context is not available.");
|
|
470
216
|
}
|
|
217
|
+
await this.waitForContextAvailability();
|
|
471
218
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
219
|
+
/** Writes states to session storage. Accepts an object with key-value pairs representing the states. */
|
|
220
|
+
async writeToSessionStorage(states) {
|
|
221
|
+
await this.page.evaluate((storage) => {
|
|
222
|
+
for (const [key, value] of Object.entries(storage)) {
|
|
223
|
+
window.sessionStorage.setItem(key, JSON.stringify(value));
|
|
224
|
+
}
|
|
225
|
+
}, states);
|
|
478
226
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
["id" /* id */]: this.id
|
|
498
|
-
};
|
|
499
|
-
this.subMethodMap = {
|
|
500
|
-
["role" /* role */]: this.page.getByRole,
|
|
501
|
-
["text" /* text */]: this.page.getByText,
|
|
502
|
-
["label" /* label */]: this.page.getByLabel,
|
|
503
|
-
["placeholder" /* placeholder */]: this.page.getByPlaceholder,
|
|
504
|
-
["altText" /* altText */]: this.page.getByAltText,
|
|
505
|
-
["title" /* title */]: this.page.getByTitle,
|
|
506
|
-
["locator" /* locator */]: this.page.locator
|
|
507
|
-
};
|
|
227
|
+
/** Reads all states from session storage and returns them as an object. */
|
|
228
|
+
async readFromSessionStorage() {
|
|
229
|
+
const storage = await this.page.evaluate(() => {
|
|
230
|
+
const storage2 = {};
|
|
231
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
232
|
+
const key = sessionStorage.key(i);
|
|
233
|
+
if (key !== null) {
|
|
234
|
+
const item = sessionStorage.getItem(key);
|
|
235
|
+
try {
|
|
236
|
+
storage2[key] = item ? JSON.parse(item) : null;
|
|
237
|
+
} catch (_e) {
|
|
238
|
+
storage2[key] = item;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return storage2;
|
|
243
|
+
});
|
|
244
|
+
return storage;
|
|
508
245
|
}
|
|
509
|
-
log;
|
|
510
|
-
methodMap;
|
|
511
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
512
|
-
subMethodMap;
|
|
513
246
|
/**
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
return method(locatorSchema);
|
|
523
|
-
}
|
|
524
|
-
throw new Error(`Unsupported locator method: ${methodName}`);
|
|
525
|
-
};
|
|
526
|
-
/**
|
|
527
|
-
* Internal method to retrieve a Locator using a specified GetByMethodSubset and LocatorSchema.
|
|
528
|
-
* It identifies the appropriate locator creation function from subMethodMap and invokes it.
|
|
529
|
-
* Throws an error if the caller is unknown or if the initial locator is not found.
|
|
247
|
+
* Sets the specified states in session storage.
|
|
248
|
+
* Optionally waits for the next main-frame navigation to establish a valid context before writing,
|
|
249
|
+
* and reloads the page after setting the data.
|
|
250
|
+
*
|
|
251
|
+
* Parameters:
|
|
252
|
+
* states: Object representing the states to set in session storage.
|
|
253
|
+
* reload: Boolean indicating whether to reload the page after setting the session storage data.
|
|
254
|
+
* waitForContext: Boolean indicating whether to wait for a main-frame navigation and a valid context.
|
|
530
255
|
*/
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const errorText = `Locator "${locator.locatorSchemaPath}" .${caller} is undefined.`;
|
|
541
|
-
this.log.warn(errorText);
|
|
542
|
-
throw new Error(errorText);
|
|
543
|
-
}
|
|
544
|
-
return initialPWLocator;
|
|
545
|
-
};
|
|
256
|
+
async set(states, options = {}) {
|
|
257
|
+
await test2.step(this.getStepLabel("set"), async () => {
|
|
258
|
+
await this.ensureContext({ waitForContext: options.waitForContext });
|
|
259
|
+
await this.writeToSessionStorage(states);
|
|
260
|
+
if (options.reload) {
|
|
261
|
+
await this.page.reload();
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
546
265
|
/**
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
266
|
+
* Queues states to be set in the sessionStorage before the next navigation occurs.
|
|
267
|
+
* Handles different scenarios based on multiple calls made before the navigation occurs.
|
|
268
|
+
*
|
|
269
|
+
* 1. No Context, Single Call: Queues and sets states upon the next navigation.
|
|
270
|
+
* 2. No Context, Multiple Calls: Merges states from multiple calls and sets them upon the next navigation.
|
|
271
|
+
* 3. With Context: Still queues until the next navigation.
|
|
272
|
+
*
|
|
273
|
+
* Parameters:
|
|
274
|
+
* states: Object representing the states to queue for setting in session storage.
|
|
550
275
|
*/
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
276
|
+
async setOnNextNavigation(states) {
|
|
277
|
+
this.queuedStates = { ...this.queuedStates, ...states };
|
|
278
|
+
const populateStorage = async () => {
|
|
279
|
+
await test2.step(this.getStepLabel("setOnNextNavigation"), async () => {
|
|
280
|
+
await this.writeToSessionStorage(this.queuedStates);
|
|
281
|
+
});
|
|
282
|
+
this.queuedStates = {};
|
|
554
283
|
};
|
|
555
|
-
};
|
|
556
|
-
// Methods for creating locators using different locator methods.
|
|
557
|
-
// These methods are generated using createByMethod and provide a unified way to create locators based on LocatorSchema.
|
|
558
|
-
// Each method is responsible for creating a Locator based on a specific attribute (role, text, label, etc.) provided in LocatorSchema.
|
|
559
|
-
// These methods return a Locator and throw an error if the necessary attribute is not defined in the LocatorSchema.
|
|
560
|
-
role = this.createByMethod("role" /* role */);
|
|
561
|
-
text = this.createByMethod("text" /* text */);
|
|
562
|
-
label = this.createByMethod("label" /* label */);
|
|
563
|
-
placeholder = this.createByMethod("placeholder" /* placeholder */);
|
|
564
|
-
altText = this.createByMethod("altText" /* altText */);
|
|
565
|
-
title = this.createByMethod("title" /* title */);
|
|
566
|
-
locator = this.createByMethod("locator" /* locator */);
|
|
567
|
-
/**
|
|
568
|
-
* Returns a FrameLocator using the 'frameLocator' selector from a LocatorSchema.
|
|
569
|
-
* Throws an error if the frameLocator is not defined.
|
|
570
|
-
*/
|
|
571
|
-
frameLocator = (locatorSchema) => {
|
|
572
|
-
const initialFrameLocator = locatorSchema.frameLocator ? this.page.frameLocator(locatorSchema.frameLocator) : null;
|
|
573
|
-
if (!initialFrameLocator) {
|
|
574
|
-
const errorText = `Locator "${locatorSchema.locatorSchemaPath}" .frameLocator is not defined.`;
|
|
575
|
-
this.log.warn(errorText);
|
|
576
|
-
throw new Error(errorText);
|
|
577
|
-
}
|
|
578
|
-
return initialFrameLocator;
|
|
579
|
-
};
|
|
580
|
-
/**
|
|
581
|
-
* Returns a Locator using the 'testId' selector from a LocatorSchema.
|
|
582
|
-
* Throws an error if the testId is not defined.
|
|
583
|
-
*/
|
|
584
|
-
testId = (locator) => {
|
|
585
|
-
const initialPWLocator = locator.testId ? this.page.getByTestId(locator.testId) : null;
|
|
586
|
-
if (!initialPWLocator) {
|
|
587
|
-
const errorText = `Locator "${locator.locatorSchemaPath}" .testId is not defined.`;
|
|
588
|
-
this.log.warn(`Locator "${locator.locatorSchemaPath}" .testId is not defined.`);
|
|
589
|
-
throw new Error(errorText);
|
|
590
|
-
}
|
|
591
|
-
return initialPWLocator;
|
|
592
|
-
};
|
|
593
|
-
/**
|
|
594
|
-
* Returns a Locator using the 'dataCy' selector from a LocatorSchema.
|
|
595
|
-
* Throws an error if the dataCy is undefined.
|
|
596
|
-
*/
|
|
597
|
-
dataCy = (locator) => {
|
|
598
|
-
let initialPWLocator = null;
|
|
599
|
-
if (locator.dataCy) {
|
|
600
|
-
initialPWLocator = locator.dataCy.startsWith("data-cy=") ? this.page.locator(locator.dataCy) : this.page.locator(`data-cy=${locator.dataCy}`);
|
|
601
|
-
} else {
|
|
602
|
-
const errorText = `Locator "${locator.locatorSchemaPath}" .dataCy is undefined.`;
|
|
603
|
-
this.log.warn(errorText);
|
|
604
|
-
throw new Error(errorText);
|
|
605
|
-
}
|
|
606
|
-
return initialPWLocator;
|
|
607
|
-
};
|
|
608
|
-
/**
|
|
609
|
-
* Returns a Locator using the 'id' selector from a LocatorSchema.
|
|
610
|
-
* Throws an error if the id is not defined or the id type is unsupported.
|
|
611
|
-
*/
|
|
612
|
-
id = (locator) => {
|
|
613
|
-
let initialPWLocator = null;
|
|
614
|
-
let selector;
|
|
615
|
-
let regexPattern;
|
|
616
|
-
if (!locator.id) {
|
|
617
|
-
const errorText = `Locator "${locator.locatorSchemaPath}" .id is not defined.`;
|
|
618
|
-
this.log.warn(errorText);
|
|
619
|
-
throw new Error(errorText);
|
|
620
|
-
}
|
|
621
|
-
if (typeof locator.id === "string") {
|
|
622
|
-
if (locator.id.startsWith("#")) {
|
|
623
|
-
selector = locator.id;
|
|
624
|
-
} else if (locator.id.startsWith("id=")) {
|
|
625
|
-
selector = `#${locator.id.slice("id=".length)}`;
|
|
626
|
-
} else {
|
|
627
|
-
selector = `#${locator.id}`;
|
|
628
|
-
}
|
|
629
|
-
} else if (locator.id instanceof RegExp) {
|
|
630
|
-
regexPattern = locator.id.source;
|
|
631
|
-
selector = `*[id^="${regexPattern}"]`;
|
|
632
|
-
} else {
|
|
633
|
-
const errorText = `Unsupported id type: ${typeof locator.id}`;
|
|
634
|
-
this.log.error(errorText);
|
|
635
|
-
throw new Error(errorText);
|
|
636
|
-
}
|
|
637
|
-
initialPWLocator = this.page.locator(selector);
|
|
638
|
-
return initialPWLocator;
|
|
639
|
-
};
|
|
640
|
-
};
|
|
641
|
-
|
|
642
|
-
// src/helpers/getLocatorBase.ts
|
|
643
|
-
var REQUIRED_PROPERTIES_FOR_LOCATOR_SCHEMA_WITH_METHODS = [
|
|
644
|
-
"update",
|
|
645
|
-
"addFilter",
|
|
646
|
-
"getNestedLocator",
|
|
647
|
-
"getLocator",
|
|
648
|
-
"locatorSchemaPath",
|
|
649
|
-
"locatorMethod",
|
|
650
|
-
"schemasMap",
|
|
651
|
-
"filterMap"
|
|
652
|
-
];
|
|
653
|
-
var safeStringifyOfNestedLocatorResults = (obj) => {
|
|
654
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
655
|
-
return JSON.stringify(
|
|
656
|
-
obj,
|
|
657
|
-
(key, value) => {
|
|
658
|
-
if (value instanceof Map) {
|
|
659
|
-
return Array.from(value.entries());
|
|
660
|
-
}
|
|
661
|
-
if (value instanceof RegExp) {
|
|
662
|
-
return { type: "RegExp", source: value.source, flags: value.flags };
|
|
663
|
-
}
|
|
664
|
-
if (value && typeof value === "object" && value.constructor && value.constructor.name === "Locator") {
|
|
665
|
-
return { type: "Locator", note: "Custom placeholder - Locators are complex." };
|
|
666
|
-
}
|
|
667
|
-
if (typeof value === "object" && value !== null) {
|
|
668
|
-
if (seen.has(value)) return "[Circular]";
|
|
669
|
-
seen.add(value);
|
|
670
|
-
}
|
|
671
|
-
return value;
|
|
672
|
-
},
|
|
673
|
-
2
|
|
674
|
-
);
|
|
675
|
-
};
|
|
676
|
-
var GetLocatorBase = class {
|
|
677
|
-
constructor(pageObjectClass, log, locatorSubstring) {
|
|
678
|
-
this.pageObjectClass = pageObjectClass;
|
|
679
|
-
this.log = log;
|
|
680
|
-
this.locatorSubstring = locatorSubstring;
|
|
681
|
-
this.locatorSchemas = /* @__PURE__ */ new Map();
|
|
682
|
-
this.getBy = new GetBy(this.pageObjectClass.page, this.log.getNewChildLogger("GetBy"));
|
|
683
|
-
}
|
|
684
|
-
getBy;
|
|
685
|
-
locatorSchemas;
|
|
686
|
-
/**
|
|
687
|
-
* getLocatorSchema:
|
|
688
|
-
* Given a path P, we:
|
|
689
|
-
* 1. Collect deep copies of the schemas involved.
|
|
690
|
-
* 2. Create a WithMethodsClass instance with LocatorSubstring = P.
|
|
691
|
-
* 3. Return a locator schema copy enriched with chainable methods.
|
|
692
|
-
*/
|
|
693
|
-
getLocatorSchema(locatorSchemaPath) {
|
|
694
|
-
const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath);
|
|
695
|
-
const schemasMap = this.collectDeepCopies(locatorSchemaPath, pathIndexPairs);
|
|
696
|
-
const locatorSchemaCopy = schemasMap.get(locatorSchemaPath);
|
|
697
|
-
locatorSchemaCopy.schemasMap = schemasMap;
|
|
698
|
-
locatorSchemaCopy.filterMap = /* @__PURE__ */ new Map();
|
|
699
|
-
const wrapper = new WithMethodsClass(
|
|
700
|
-
this.pageObjectClass,
|
|
701
|
-
this.log,
|
|
702
|
-
locatorSchemaPath,
|
|
703
|
-
schemasMap
|
|
704
|
-
);
|
|
705
|
-
return wrapper.init(locatorSchemaPath, locatorSchemaCopy);
|
|
706
|
-
}
|
|
707
|
-
/**
|
|
708
|
-
* collectDeepCopies:
|
|
709
|
-
* Clones and stores all schemas related to the chosen path and its sub-paths.
|
|
710
|
-
* Ensures updates and filters don't affect original schema definitions.
|
|
711
|
-
*/
|
|
712
|
-
collectDeepCopies(locatorSchemaPath, pathIndexPairs) {
|
|
713
|
-
const schemasMap = /* @__PURE__ */ new Map();
|
|
714
|
-
const fullSchemaFunc = this.safeGetLocatorSchema(locatorSchemaPath);
|
|
715
|
-
if (!fullSchemaFunc) {
|
|
716
|
-
const errorMessage = `LocatorSchema not found for path: '${locatorSchemaPath}'`;
|
|
717
|
-
this.log.error(errorMessage);
|
|
718
|
-
throw new Error(`[${this.pageObjectClass.pocName}] ${errorMessage}`);
|
|
719
|
-
}
|
|
720
|
-
schemasMap.set(locatorSchemaPath, structuredClone(fullSchemaFunc()));
|
|
721
|
-
for (const { path } of pathIndexPairs) {
|
|
722
|
-
if (path !== locatorSchemaPath) {
|
|
723
|
-
const schemaFunc = this.safeGetLocatorSchema(path);
|
|
724
|
-
if (schemaFunc) {
|
|
725
|
-
schemasMap.set(path, structuredClone(schemaFunc()));
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
return schemasMap;
|
|
730
|
-
}
|
|
731
|
-
isLocatorSchemaWithMethods(schema) {
|
|
732
|
-
return REQUIRED_PROPERTIES_FOR_LOCATOR_SCHEMA_WITH_METHODS.every((p) => p in schema);
|
|
733
|
-
}
|
|
734
|
-
/**
|
|
735
|
-
* applyUpdateToSubPath:
|
|
736
|
-
* Applies updates to a specific sub-path schema within schemasMap.
|
|
737
|
-
* Similar to applyUpdate, but we locate the sub-path schema directly by its path.
|
|
738
|
-
*/
|
|
739
|
-
applyUpdateToSubPath(schemasMap, subPath, updates) {
|
|
740
|
-
const schema = schemasMap.get(subPath);
|
|
741
|
-
if (!schema) {
|
|
742
|
-
throw new Error(`No schema found for sub-path: '${subPath}'`);
|
|
743
|
-
}
|
|
744
|
-
const updatedSchema = this.deepMerge(schema, updates);
|
|
745
|
-
if (this.isLocatorSchemaWithMethods(schema)) {
|
|
746
|
-
Object.assign(schema, updatedSchema);
|
|
747
|
-
} else {
|
|
748
|
-
schemasMap.set(subPath, updatedSchema);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* createLocatorSchema:
|
|
753
|
-
* Creates a fresh LocatorSchema object by merging provided schemaDetails with a required locatorSchemaPath.
|
|
754
|
-
*/
|
|
755
|
-
createLocatorSchema(schemaDetails, locatorSchemaPath) {
|
|
756
|
-
const schema = { ...schemaDetails, locatorSchemaPath };
|
|
757
|
-
return schema;
|
|
758
|
-
}
|
|
759
|
-
/**
|
|
760
|
-
* addSchema:
|
|
761
|
-
* Registers a new LocatorSchema under the given locatorSchemaPath.
|
|
762
|
-
* Throws an error if a schema already exists at that path.
|
|
763
|
-
*/
|
|
764
|
-
addSchema(locatorSchemaPath, schemaDetails) {
|
|
765
|
-
if (locatorSchemaPath.length === 0 || locatorSchemaPath.startsWith(".") || locatorSchemaPath.endsWith(".") || locatorSchemaPath.includes("..")) {
|
|
766
|
-
throw new Error(
|
|
767
|
-
`[${this.pageObjectClass.pocName}] Invalid LocatorSchemaPath '${locatorSchemaPath}'. LocatorSchemaPath must not be empty, start or end with a '.', or contain consecutive '.'.`
|
|
768
|
-
);
|
|
769
|
-
}
|
|
770
|
-
const newLocatorSchema = this.createLocatorSchema(schemaDetails, locatorSchemaPath);
|
|
771
|
-
const existingSchemaFunc = this.safeGetLocatorSchema(locatorSchemaPath);
|
|
772
|
-
if (existingSchemaFunc) {
|
|
773
|
-
const existingLocatorSchema = existingSchemaFunc();
|
|
774
|
-
throw new Error(
|
|
775
|
-
`[${this.pageObjectClass.pocName}] A LocatorSchema with the path '${locatorSchemaPath}' already exists.
|
|
776
|
-
Existing Schema: ${JSON.stringify(existingLocatorSchema, null, 2)}
|
|
777
|
-
Attempted to Add Schema: ${JSON.stringify(newLocatorSchema, null, 2)}`
|
|
778
|
-
);
|
|
779
|
-
}
|
|
780
|
-
this.locatorSchemas.set(locatorSchemaPath, () => newLocatorSchema);
|
|
781
|
-
const v2Registry = this.pageObjectClass.locatorRegistry;
|
|
782
|
-
if (v2Registry) {
|
|
783
|
-
addV1SchemaToV2Registry(v2Registry, newLocatorSchema);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
/**
|
|
787
|
-
* safeGetLocatorSchema:
|
|
788
|
-
* Safely retrieves a schema function if available for the given path.
|
|
789
|
-
*/
|
|
790
|
-
safeGetLocatorSchema(path) {
|
|
791
|
-
return this.locatorSchemas.get(path);
|
|
792
|
-
}
|
|
793
|
-
/**
|
|
794
|
-
* extractPathsFromSchema:
|
|
795
|
-
* Splits a path into incremental sub-paths and associates them with optional indices.
|
|
796
|
-
* Used by getNestedLocator methods.
|
|
797
|
-
*/
|
|
798
|
-
extractPathsFromSchema = (paths, indices = {}) => {
|
|
799
|
-
const schemaParts = paths.split(".");
|
|
800
|
-
let cumulativePath = "";
|
|
801
|
-
return schemaParts.map((part, index) => {
|
|
802
|
-
cumulativePath = cumulativePath ? `${cumulativePath}.${part}` : part;
|
|
803
|
-
return {
|
|
804
|
-
path: cumulativePath,
|
|
805
|
-
index: indices[index] ?? void 0
|
|
806
|
-
};
|
|
807
|
-
});
|
|
808
|
-
};
|
|
809
|
-
/**
|
|
810
|
-
* logError:
|
|
811
|
-
* Logs detailed error information and re-throws the error to ensure tests fail as expected.
|
|
812
|
-
*/
|
|
813
|
-
logError = (error, locatorSchemaPath, currentLocator, currentPath, pathIndexPairs, nestedLocatorResults) => {
|
|
814
|
-
const errorDetails = {
|
|
815
|
-
error: error.message,
|
|
816
|
-
locatorSchemaPath,
|
|
817
|
-
currentPath,
|
|
818
|
-
pathIndexPairs: JSON.stringify(pathIndexPairs, null, 2),
|
|
819
|
-
currentLocatorDetails: currentLocator ? {
|
|
820
|
-
locatorString: currentLocator,
|
|
821
|
-
isNotNull: true
|
|
822
|
-
} : { isNotNull: false },
|
|
823
|
-
nestedLocatorResults: safeStringifyOfNestedLocatorResults(nestedLocatorResults)
|
|
824
|
-
};
|
|
825
|
-
this.log.error(
|
|
826
|
-
"An error occurred during nested locator construction.\n",
|
|
827
|
-
"Error details:\n",
|
|
828
|
-
JSON.stringify(errorDetails, null, 2)
|
|
829
|
-
);
|
|
830
|
-
throw error;
|
|
831
|
-
};
|
|
832
|
-
/**
|
|
833
|
-
* deepMerge:
|
|
834
|
-
* Recursively merges source properties into target, validating them against LocatorSchema to ensure no invalid keys.
|
|
835
|
-
* Ensures immutability by creating a new object rather than modifying in place.
|
|
836
|
-
*/
|
|
837
|
-
deepMerge(target, source, schema = getLocatorSchemaDummy()) {
|
|
838
|
-
const merged = { ...target };
|
|
839
|
-
for (const key of Object.keys(source)) {
|
|
840
|
-
if (key === "locatorSchemaPath") {
|
|
841
|
-
throw new Error(
|
|
842
|
-
`[${this.pageObjectClass.pocName}] Invalid property: 'locatorSchemaPath' cannot be updated. Attempted to update LocatorSchemaPath from '${target[key]}' to '${source[key]}'.`
|
|
843
|
-
);
|
|
844
|
-
}
|
|
845
|
-
if (!(key in schema)) {
|
|
846
|
-
throw new Error(`Invalid property: '${key}' is not a valid property of LocatorSchema`);
|
|
847
|
-
}
|
|
848
|
-
const sourceValue = source[key];
|
|
849
|
-
const targetValue = target[key];
|
|
850
|
-
if (typeof sourceValue === "object" && sourceValue !== null && schema[key] && typeof schema[key] === "object") {
|
|
851
|
-
if (targetValue && typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
852
|
-
merged[key] = this.deepMerge(
|
|
853
|
-
targetValue,
|
|
854
|
-
// Updated type here
|
|
855
|
-
sourceValue,
|
|
856
|
-
schema[key]
|
|
857
|
-
);
|
|
858
|
-
} else {
|
|
859
|
-
merged[key] = this.deepMerge(
|
|
860
|
-
{},
|
|
861
|
-
sourceValue,
|
|
862
|
-
schema[key]
|
|
863
|
-
);
|
|
864
|
-
}
|
|
865
|
-
} else {
|
|
866
|
-
if (Array.isArray(sourceValue)) {
|
|
867
|
-
merged[key] = Array.isArray(targetValue) ? targetValue.concat(sourceValue) : [...sourceValue];
|
|
868
|
-
} else if (typeof sourceValue === "object" && sourceValue !== null && Object.prototype.toString.call(sourceValue) === "[object RegExp]") {
|
|
869
|
-
merged[key] = new RegExp(
|
|
870
|
-
sourceValue.source,
|
|
871
|
-
sourceValue.flags
|
|
872
|
-
);
|
|
873
|
-
} else {
|
|
874
|
-
merged[key] = sourceValue;
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
return merged;
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* buildNestedLocator:
|
|
882
|
-
* Constructs a nested locator by iterating through each sub-path of locatorSchemaPath and chaining locators.
|
|
883
|
-
* Applies filters, indexing (nth), and logs details for debugging during test retries.
|
|
884
|
-
*/
|
|
885
|
-
buildNestedLocator = async (locatorSchemaPath, schemasMap, filterMap, indices = {}) => {
|
|
886
|
-
return await test.step(`${this.pageObjectClass.pocName}: Build Nested Locator`, async () => {
|
|
887
|
-
const pathIndexPairs = this.extractPathsFromSchema(locatorSchemaPath, indices);
|
|
888
|
-
let currentLocator = null;
|
|
889
|
-
let currentIFrame = null;
|
|
890
|
-
const nestedLocatorResults = {
|
|
891
|
-
LocatorSchema: null,
|
|
892
|
-
NestingSteps: []
|
|
893
|
-
};
|
|
894
|
-
for (const { path, index } of pathIndexPairs) {
|
|
895
|
-
const currentSchema = schemasMap.get(path);
|
|
896
|
-
if (!currentSchema) continue;
|
|
897
|
-
try {
|
|
898
|
-
const nextLocator = this.getBy.getLocator(currentSchema);
|
|
899
|
-
currentLocator = currentLocator ? currentLocator.locator(nextLocator) : nextLocator;
|
|
900
|
-
if (currentSchema.locatorMethod !== "frameLocator" /* frameLocator */ && currentSchema.filter) {
|
|
901
|
-
currentLocator = currentLocator.filter({
|
|
902
|
-
has: currentSchema.filter.has,
|
|
903
|
-
hasNot: currentSchema.filter.hasNot,
|
|
904
|
-
hasNotText: currentSchema.filter.hasNotText,
|
|
905
|
-
hasText: currentSchema.filter.hasText
|
|
906
|
-
});
|
|
907
|
-
}
|
|
908
|
-
const filterEntries = filterMap.get(path);
|
|
909
|
-
if (filterEntries) {
|
|
910
|
-
for (const filterData of filterEntries) {
|
|
911
|
-
currentLocator = currentLocator.filter({
|
|
912
|
-
has: filterData.has,
|
|
913
|
-
hasNot: filterData.hasNot,
|
|
914
|
-
hasNotText: filterData.hasNotText,
|
|
915
|
-
hasText: filterData.hasText
|
|
916
|
-
});
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
if (index != null) {
|
|
920
|
-
currentLocator = currentLocator.nth(index);
|
|
921
|
-
}
|
|
922
|
-
if (this.log.isLogLevelEnabled("debug")) {
|
|
923
|
-
if (!nestedLocatorResults.LocatorSchema) {
|
|
924
|
-
const schemaFromMap = schemasMap.get(locatorSchemaPath);
|
|
925
|
-
if (schemaFromMap) {
|
|
926
|
-
nestedLocatorResults.LocatorSchema = schemaFromMap;
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
if (currentSchema.locatorMethod === "frameLocator" /* frameLocator */) {
|
|
930
|
-
if (!currentIFrame) {
|
|
931
|
-
currentIFrame = currentSchema.frameLocator;
|
|
932
|
-
}
|
|
933
|
-
if (currentIFrame && currentSchema.frameLocator && currentIFrame.endsWith(currentSchema.frameLocator)) {
|
|
934
|
-
currentIFrame += ` -> ${currentSchema.frameLocator}`;
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
if (currentIFrame !== void 0) {
|
|
938
|
-
await this.evaluateCurrentLocator(currentLocator, nestedLocatorResults.NestingSteps, currentIFrame);
|
|
939
|
-
} else {
|
|
940
|
-
await this.evaluateCurrentLocator(currentLocator, nestedLocatorResults.NestingSteps, null);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
} catch (error) {
|
|
944
|
-
this.logError(error, locatorSchemaPath, currentLocator, path, pathIndexPairs, nestedLocatorResults);
|
|
945
|
-
break;
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
if (!currentLocator) {
|
|
949
|
-
this.logError(
|
|
950
|
-
new Error(`Failed to build nested locator for path: ${locatorSchemaPath}`),
|
|
951
|
-
locatorSchemaPath,
|
|
952
|
-
currentLocator,
|
|
953
|
-
locatorSchemaPath,
|
|
954
|
-
pathIndexPairs
|
|
955
|
-
);
|
|
956
|
-
}
|
|
957
|
-
if (this.log.isLogLevelEnabled("debug")) {
|
|
958
|
-
this.log.debug("Nested locator evaluation results:", safeStringifyOfNestedLocatorResults(nestedLocatorResults));
|
|
959
|
-
}
|
|
960
|
-
if (currentLocator != null) {
|
|
961
|
-
return currentLocator;
|
|
962
|
-
}
|
|
963
|
-
});
|
|
964
|
-
};
|
|
965
|
-
/**
|
|
966
|
-
* evaluateCurrentLocator:
|
|
967
|
-
* Gathers debug information about the current locator's resolved elements.
|
|
968
|
-
* Helps with logging and debugging complex locator chains.
|
|
969
|
-
*/
|
|
970
|
-
evaluateCurrentLocator = async (currentLocator, resultsArray, currentIFrame) => {
|
|
971
|
-
if (currentIFrame) {
|
|
972
|
-
resultsArray.push({
|
|
973
|
-
currentLocatorString: currentLocator,
|
|
974
|
-
currentIFrame,
|
|
975
|
-
Note: "iFrame locators evaluation not implemented"
|
|
976
|
-
});
|
|
977
|
-
} else {
|
|
978
|
-
const elementCount = await currentLocator.count();
|
|
979
|
-
resultsArray.push({
|
|
980
|
-
currentLocatorString: `${currentLocator}`,
|
|
981
|
-
resolved: elementCount > 0,
|
|
982
|
-
elementCount
|
|
983
|
-
});
|
|
984
|
-
}
|
|
985
|
-
};
|
|
986
|
-
};
|
|
987
|
-
var WithMethodsClass = class extends GetLocatorBase {
|
|
988
|
-
constructor(pageObjectClass, log, locatorSubstring, schemasMap) {
|
|
989
|
-
super(pageObjectClass, log, locatorSubstring);
|
|
990
|
-
this.pageObjectClass = pageObjectClass;
|
|
991
|
-
this.log = log;
|
|
992
|
-
this.schemasMap = schemasMap;
|
|
993
|
-
}
|
|
994
|
-
locatorSchemaPath;
|
|
995
|
-
/**
|
|
996
|
-
* init:
|
|
997
|
-
* Assigns the locatorSchemaPath and binds methods (update, addFilter, getNestedLocator, getLocator)
|
|
998
|
-
* directly on the locatorSchemaCopy. Returns the modified copy, now fully chainable and type-safe.
|
|
999
|
-
*/
|
|
1000
|
-
init(locatorSchemaPath, locatorSchemaCopy) {
|
|
1001
|
-
this.locatorSchemaPath = locatorSchemaPath;
|
|
1002
|
-
const self = this;
|
|
1003
|
-
locatorSchemaCopy.update = function(subPath, updates) {
|
|
1004
|
-
const fullPath = this.locatorSchemaPath;
|
|
1005
|
-
if (!(subPath === fullPath || fullPath.startsWith(`${subPath}.`))) {
|
|
1006
|
-
throw new Error(`Invalid sub-path: '${subPath}' is not a valid sub-path of '${fullPath}'.`);
|
|
1007
|
-
}
|
|
1008
|
-
self.applyUpdateToSubPath(self.schemasMap, subPath, updates);
|
|
1009
|
-
return this;
|
|
1010
|
-
};
|
|
1011
|
-
locatorSchemaCopy.addFilter = function(subPath, filterData) {
|
|
1012
|
-
const fullPath = this.locatorSchemaPath;
|
|
1013
|
-
if (!self.schemasMap.has(subPath)) {
|
|
1014
|
-
const allowedPaths = self.extractPathsFromSchema(fullPath).map((p) => p.path).filter((path) => self.schemasMap.has(path));
|
|
1015
|
-
throw new Error(
|
|
1016
|
-
`Invalid sub-path '${subPath}' in addFilter. Allowed sub-paths are:
|
|
1017
|
-
${allowedPaths.join(",\n")}`
|
|
1018
|
-
);
|
|
1019
|
-
}
|
|
1020
|
-
if (!this.filterMap) {
|
|
1021
|
-
this.filterMap = /* @__PURE__ */ new Map();
|
|
1022
|
-
}
|
|
1023
|
-
const existingFilters = this.filterMap.get(subPath) || [];
|
|
1024
|
-
existingFilters.push(filterData);
|
|
1025
|
-
this.filterMap.set(subPath, existingFilters);
|
|
1026
|
-
return this;
|
|
1027
|
-
};
|
|
1028
|
-
locatorSchemaCopy.getNestedLocator = async function(arg) {
|
|
1029
|
-
if (arg !== void 0 && arg !== null && typeof arg !== "object") {
|
|
1030
|
-
throw new Error("Invalid argument passed to getNestedLocator: Expected an object or null.");
|
|
1031
|
-
}
|
|
1032
|
-
if (!arg || Object.keys(arg).length === 0) {
|
|
1033
|
-
return await self.buildNestedLocator(
|
|
1034
|
-
self.locatorSchemaPath,
|
|
1035
|
-
self.schemasMap,
|
|
1036
|
-
this.filterMap,
|
|
1037
|
-
{}
|
|
1038
|
-
);
|
|
1039
|
-
}
|
|
1040
|
-
const numericIndices = {};
|
|
1041
|
-
const pathIndexPairs = self.extractPathsFromSchema(self.locatorSchemaPath);
|
|
1042
|
-
const pathToIndexMap = new Map(pathIndexPairs.map((pair, idx) => [pair.path, idx]));
|
|
1043
|
-
for (const [subPath, value] of Object.entries(arg)) {
|
|
1044
|
-
if (!self.schemasMap.has(subPath)) {
|
|
1045
|
-
const validPaths = Array.from(self.schemasMap.keys());
|
|
1046
|
-
throw new Error(
|
|
1047
|
-
`Invalid sub-path '${subPath}' in getNestedLocator. Allowed sub-paths are:
|
|
1048
|
-
${validPaths.join(",\n")}`
|
|
1049
|
-
);
|
|
1050
|
-
}
|
|
1051
|
-
if (!pathToIndexMap.has(subPath)) {
|
|
1052
|
-
const validPaths = pathIndexPairs.map((p) => p.path).filter((path) => self.schemasMap.has(path));
|
|
1053
|
-
throw new Error(
|
|
1054
|
-
`Invalid sub-path '${subPath}' in getNestedLocator. Allowed sub-paths are:
|
|
1055
|
-
${validPaths.join(",\n")}`
|
|
1056
|
-
);
|
|
1057
|
-
}
|
|
1058
|
-
const numericIndex = pathToIndexMap.get(subPath);
|
|
1059
|
-
if (numericIndex === void 0) {
|
|
1060
|
-
throw new Error(`Sub-path '${subPath}' not found in pathToIndexMap.`);
|
|
1061
|
-
}
|
|
1062
|
-
if (value !== null && (typeof value !== "number" || value < 0)) {
|
|
1063
|
-
throw new Error(`Invalid index for sub-path '${subPath}': Expected a positive number or null.`);
|
|
1064
|
-
}
|
|
1065
|
-
if (value !== null) {
|
|
1066
|
-
numericIndices[numericIndex] = value;
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
return await self.buildNestedLocator(
|
|
1070
|
-
self.locatorSchemaPath,
|
|
1071
|
-
self.schemasMap,
|
|
1072
|
-
this.filterMap,
|
|
1073
|
-
numericIndices
|
|
1074
|
-
);
|
|
1075
|
-
};
|
|
1076
|
-
locatorSchemaCopy.getLocator = async () => {
|
|
1077
|
-
return self.getBy.getLocator(locatorSchemaCopy);
|
|
1078
|
-
};
|
|
1079
|
-
return locatorSchemaCopy;
|
|
1080
|
-
}
|
|
1081
|
-
};
|
|
1082
|
-
|
|
1083
|
-
// src/helpers/sessionStorage.actions.ts
|
|
1084
|
-
import { test as test2 } from "@playwright/test";
|
|
1085
|
-
var SessionStorage = class {
|
|
1086
|
-
// Initializes the class with a Playwright Page object and a name for the Page Object Class.
|
|
1087
|
-
constructor(page, pocName) {
|
|
1088
|
-
this.page = page;
|
|
1089
|
-
this.pocName = pocName;
|
|
1090
|
-
}
|
|
1091
|
-
// Defines an object to hold states to be set in session storage, allowing any value type.
|
|
1092
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1093
|
-
queuedStates = {};
|
|
1094
|
-
// Indicates if the session storage manipulation has been initiated.
|
|
1095
|
-
isInitiated = false;
|
|
1096
|
-
/** Writes states to session storage. Accepts an object with key-value pairs representing the states. */
|
|
1097
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1098
|
-
async writeToSessionStorage(states) {
|
|
1099
|
-
await this.page.evaluate((storage) => {
|
|
1100
|
-
for (const [key, value] of Object.entries(storage)) {
|
|
1101
|
-
window.sessionStorage.setItem(key, JSON.stringify(value));
|
|
1102
|
-
}
|
|
1103
|
-
}, states);
|
|
1104
|
-
}
|
|
1105
|
-
/** Reads all states from session storage and returns them as an object. */
|
|
1106
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1107
|
-
async readFromSessionStorage() {
|
|
1108
|
-
return await this.page.evaluate(() => {
|
|
1109
|
-
const storage = {};
|
|
1110
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
1111
|
-
const key = sessionStorage.key(i);
|
|
1112
|
-
if (key !== null) {
|
|
1113
|
-
const item = sessionStorage.getItem(key);
|
|
1114
|
-
try {
|
|
1115
|
-
storage[key] = item ? JSON.parse(item) : null;
|
|
1116
|
-
} catch (_e) {
|
|
1117
|
-
storage[key] = item;
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
return storage;
|
|
1122
|
-
});
|
|
1123
|
-
}
|
|
1124
|
-
/**
|
|
1125
|
-
* Sets the specified states in session storage.
|
|
1126
|
-
* Optionally reloads the page after setting the data to ensure the new session storage state is active.
|
|
1127
|
-
*
|
|
1128
|
-
* Parameters:
|
|
1129
|
-
* states: Object representing the states to set in session storage.
|
|
1130
|
-
* reload: Boolean indicating whether to reload the page after setting the session storage data.
|
|
1131
|
-
*/
|
|
1132
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1133
|
-
async set(states, reload) {
|
|
1134
|
-
await test2.step(`${this.pocName}: setSessionStorage`, async () => {
|
|
1135
|
-
await this.writeToSessionStorage(states);
|
|
1136
|
-
if (reload) {
|
|
1137
|
-
await this.page.reload();
|
|
1138
|
-
}
|
|
1139
|
-
});
|
|
1140
|
-
}
|
|
1141
|
-
/**
|
|
1142
|
-
* Queues states to be set in the sessionStorage before the next navigation occurs.
|
|
1143
|
-
* Handles different scenarios based on whether the context exists or multiple calls are made.
|
|
1144
|
-
*
|
|
1145
|
-
* 1. No Context, Single Call: Queues and sets states upon the next navigation.
|
|
1146
|
-
* 2. No Context, Multiple Calls: Merges states from multiple calls and sets them upon the next navigation.
|
|
1147
|
-
* 3. With Context: Directly sets states in sessionStorage if the context already exists.
|
|
1148
|
-
*
|
|
1149
|
-
* Parameters:
|
|
1150
|
-
* states: Object representing the states to queue for setting in session storage.
|
|
1151
|
-
*/
|
|
1152
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1153
|
-
async setOnNextNavigation(states) {
|
|
1154
|
-
this.queuedStates = { ...this.queuedStates, ...states };
|
|
1155
|
-
const populateStorage = async () => {
|
|
1156
|
-
await test2.step(`${this.pocName}: setSessionStorageBeforeNavigation`, async () => {
|
|
1157
|
-
await this.writeToSessionStorage(this.queuedStates);
|
|
1158
|
-
});
|
|
1159
|
-
this.queuedStates = {};
|
|
1160
|
-
};
|
|
1161
|
-
let contextExists = false;
|
|
1162
|
-
try {
|
|
1163
|
-
contextExists = await this.page.evaluate(() => {
|
|
1164
|
-
return typeof window !== "undefined" && window.sessionStorage !== void 0;
|
|
1165
|
-
});
|
|
1166
|
-
} catch (_e) {
|
|
1167
|
-
contextExists = false;
|
|
1168
|
-
}
|
|
1169
|
-
if (contextExists) {
|
|
1170
|
-
await populateStorage();
|
|
1171
|
-
return;
|
|
1172
|
-
}
|
|
1173
284
|
if (!this.isInitiated) {
|
|
1174
285
|
this.isInitiated = true;
|
|
1175
|
-
|
|
286
|
+
const handler = async (frame) => {
|
|
287
|
+
if (frame !== this.page.mainFrame()) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
1176
290
|
await populateStorage();
|
|
1177
|
-
|
|
291
|
+
this.page.off("framenavigated", handler);
|
|
292
|
+
this.isInitiated = false;
|
|
293
|
+
};
|
|
294
|
+
this.page.on("framenavigated", handler);
|
|
1178
295
|
}
|
|
1179
296
|
}
|
|
1180
|
-
|
|
1181
|
-
* Fetches states from session storage.
|
|
1182
|
-
* If specific keys are provided, fetches only those states; otherwise, fetches all states.
|
|
1183
|
-
*
|
|
1184
|
-
* Parameters:
|
|
1185
|
-
* keys: Optional array of keys to specify which states to fetch from session storage.
|
|
1186
|
-
*
|
|
1187
|
-
* Returns:
|
|
1188
|
-
* Object containing the fetched states.
|
|
1189
|
-
*/
|
|
1190
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
1191
|
-
async get(keys) {
|
|
297
|
+
async get(keys, options = {}) {
|
|
1192
298
|
let result = {};
|
|
1193
|
-
await test2.step(
|
|
299
|
+
await test2.step(this.getStepLabel("get"), async () => {
|
|
300
|
+
await this.ensureContext(options);
|
|
1194
301
|
const allData = await this.readFromSessionStorage();
|
|
1195
302
|
if (keys && keys.length > 0) {
|
|
1196
303
|
for (const key of keys) {
|
|
1197
304
|
if (Object.hasOwn(allData, key)) {
|
|
1198
|
-
|
|
305
|
+
const value = allData[key];
|
|
306
|
+
if (value !== void 0) {
|
|
307
|
+
result[key] = value;
|
|
308
|
+
}
|
|
1199
309
|
}
|
|
1200
310
|
}
|
|
1201
311
|
} else {
|
|
@@ -1204,216 +314,301 @@ var SessionStorage = class {
|
|
|
1204
314
|
});
|
|
1205
315
|
return result;
|
|
1206
316
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
317
|
+
async clear(keyOrOptions, options = {}) {
|
|
318
|
+
const { keys, waitForContext } = (() => {
|
|
319
|
+
if (Array.isArray(keyOrOptions)) {
|
|
320
|
+
return { keys: keyOrOptions, waitForContext: options.waitForContext };
|
|
321
|
+
}
|
|
322
|
+
if (typeof keyOrOptions === "string") {
|
|
323
|
+
return { keys: [keyOrOptions], waitForContext: options.waitForContext };
|
|
324
|
+
}
|
|
325
|
+
if (keyOrOptions) {
|
|
326
|
+
return { keys: void 0, waitForContext: keyOrOptions.waitForContext };
|
|
327
|
+
}
|
|
328
|
+
return { keys: void 0, waitForContext: options.waitForContext };
|
|
329
|
+
})();
|
|
330
|
+
await test2.step(this.getStepLabel("clear"), async () => {
|
|
331
|
+
await this.ensureContext({ waitForContext });
|
|
332
|
+
if (!keys || keys.length === 0) {
|
|
333
|
+
await this.page.evaluate(() => sessionStorage.clear());
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
await this.page.evaluate((keysToClear) => {
|
|
337
|
+
for (const key of keysToClear) {
|
|
338
|
+
sessionStorage.removeItem(key);
|
|
339
|
+
}
|
|
340
|
+
}, keys);
|
|
1213
341
|
});
|
|
1214
342
|
}
|
|
1215
343
|
};
|
|
1216
344
|
|
|
1217
|
-
// src/
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
var
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
345
|
+
// src/helpers/stepDecorator.ts
|
|
346
|
+
import { test as test3 } from "@playwright/test";
|
|
347
|
+
var isMethodDecoratorArgs = (args) => args.length === 3 && typeof args[0] === "object" && (typeof args[1] === "string" || typeof args[1] === "symbol");
|
|
348
|
+
var isStage3MethodDecoratorArgs = (args) => args.length === 2 && typeof args[0] === "function" && args[1] !== null && typeof args[1] === "object";
|
|
349
|
+
var normalizeStepArguments = (args) => {
|
|
350
|
+
const [titleOrOptions, maybeOptions] = args;
|
|
351
|
+
const title = typeof titleOrOptions === "string" ? titleOrOptions : void 0;
|
|
352
|
+
const options = typeof titleOrOptions === "string" ? maybeOptions : titleOrOptions;
|
|
353
|
+
return { title, options };
|
|
354
|
+
};
|
|
355
|
+
var createWrappedMethod = (original, methodName, title, options) => function(...methodArgs) {
|
|
356
|
+
const rawClassName = this.constructor?.name ?? "";
|
|
357
|
+
const className = rawClassName && rawClassName !== "Object" ? rawClassName : "Anonymous";
|
|
358
|
+
const resolvedTitle = title ?? `${className}.${String(methodName)}`;
|
|
359
|
+
return test3.step(resolvedTitle, () => original.apply(this, methodArgs), options);
|
|
360
|
+
};
|
|
361
|
+
var createStepDecorator = ({ title, options }) => (valueOrTarget, contextOrKey, descriptor) => {
|
|
362
|
+
if (typeof valueOrTarget === "function" && isStage3MethodDecoratorArgs([valueOrTarget, contextOrKey])) {
|
|
363
|
+
const [original2, context] = [valueOrTarget, contextOrKey];
|
|
364
|
+
return createWrappedMethod(original2, context.name, title, options);
|
|
365
|
+
}
|
|
366
|
+
if (!descriptor || typeof descriptor.value !== "function") {
|
|
367
|
+
throw new Error("@step decorator can only be applied to methods.");
|
|
368
|
+
}
|
|
369
|
+
const original = descriptor.value;
|
|
370
|
+
descriptor.value = createWrappedMethod(original, contextOrKey, title, options);
|
|
371
|
+
return descriptor;
|
|
372
|
+
};
|
|
373
|
+
function step(...args) {
|
|
374
|
+
if (isStage3MethodDecoratorArgs(args)) {
|
|
375
|
+
return createStepDecorator(normalizeStepArguments([]))(...args);
|
|
376
|
+
}
|
|
377
|
+
if (isMethodDecoratorArgs(args)) {
|
|
378
|
+
return createStepDecorator(normalizeStepArguments([]))(...args);
|
|
379
|
+
}
|
|
380
|
+
return createStepDecorator(normalizeStepArguments(args));
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/locators/utils.ts
|
|
384
|
+
var formatLocatorSchemaPathForError = (path) => {
|
|
385
|
+
const json = JSON.stringify(path);
|
|
386
|
+
return json.slice(1, -1);
|
|
387
|
+
};
|
|
388
|
+
var RUNTIME_WHITESPACE_REGEX = /[\s\u0085]/u;
|
|
389
|
+
var validateLocatorSchemaPath = (path) => {
|
|
390
|
+
if (!path) {
|
|
391
|
+
throw new Error("LocatorSchemaPath string cannot be empty");
|
|
392
|
+
}
|
|
393
|
+
if (RUNTIME_WHITESPACE_REGEX.test(path)) {
|
|
394
|
+
const escaped = formatLocatorSchemaPathForError(path);
|
|
395
|
+
throw new Error(`LocatorSchemaPath string cannot contain whitespace chars: ${escaped}`);
|
|
396
|
+
}
|
|
397
|
+
if (path.startsWith(".")) {
|
|
398
|
+
throw new Error(`LocatorSchemaPath string cannot start with a dot: ${path}`);
|
|
399
|
+
}
|
|
400
|
+
if (path.endsWith(".")) {
|
|
401
|
+
throw new Error(`LocatorSchemaPath string cannot end with a dot: ${path}`);
|
|
402
|
+
}
|
|
403
|
+
if (path.includes("..")) {
|
|
404
|
+
throw new Error(`LocatorSchemaPath string cannot contain consecutive dots: ${path}`);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
var expandSchemaPath = (path) => {
|
|
408
|
+
validateLocatorSchemaPath(path);
|
|
409
|
+
const parts = path.split(".");
|
|
410
|
+
return parts.map((_part, index) => parts.slice(0, index + 1).join("."));
|
|
411
|
+
};
|
|
412
|
+
var cssEscape = (value) => {
|
|
413
|
+
return value.replace(/([\\"'#.:;,?*+<>{}[\\]()])/g, "\\$1");
|
|
414
|
+
};
|
|
415
|
+
var normalizeSteps = (steps) => steps ? steps.map((step2) => ({ ...step2 })) : [];
|
|
416
|
+
function normalizeIdValue(id) {
|
|
417
|
+
if (typeof id !== "string") {
|
|
418
|
+
return id;
|
|
419
|
+
}
|
|
420
|
+
if (id.startsWith("#")) {
|
|
421
|
+
return id.slice(1);
|
|
422
|
+
}
|
|
423
|
+
if (id.startsWith("id=")) {
|
|
424
|
+
return id.slice("id=".length);
|
|
425
|
+
}
|
|
426
|
+
return id;
|
|
427
|
+
}
|
|
428
|
+
var stringifyForLog = (value) => {
|
|
429
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
430
|
+
return JSON.stringify(
|
|
431
|
+
value,
|
|
432
|
+
(_key, current) => {
|
|
433
|
+
if (typeof current === "object" && current !== null) {
|
|
434
|
+
if (seen.has(current)) {
|
|
435
|
+
return "[Circular]";
|
|
436
|
+
}
|
|
437
|
+
seen.add(current);
|
|
438
|
+
}
|
|
439
|
+
if (current instanceof RegExp) {
|
|
440
|
+
return { type: "RegExp", source: current.source, flags: current.flags };
|
|
441
|
+
}
|
|
442
|
+
return current;
|
|
443
|
+
},
|
|
444
|
+
2
|
|
445
|
+
);
|
|
446
|
+
};
|
|
447
|
+
var applyIndexSelector = (locator, selector) => {
|
|
448
|
+
if (selector === void 0 || selector === null) {
|
|
449
|
+
return locator;
|
|
450
|
+
}
|
|
451
|
+
if (selector === "first") {
|
|
452
|
+
return locator.first();
|
|
453
|
+
}
|
|
454
|
+
if (selector === "last") {
|
|
455
|
+
return locator.last();
|
|
456
|
+
}
|
|
457
|
+
return locator.nth(selector);
|
|
458
|
+
};
|
|
459
|
+
var createLocator = (target, definition) => {
|
|
460
|
+
switch (definition.type) {
|
|
461
|
+
case "role":
|
|
462
|
+
return target.getByRole(definition.role, definition.options);
|
|
463
|
+
case "text":
|
|
464
|
+
return target.getByText(definition.text, definition.options);
|
|
465
|
+
case "label":
|
|
466
|
+
return target.getByLabel(definition.text, definition.options);
|
|
467
|
+
case "placeholder":
|
|
468
|
+
return target.getByPlaceholder(definition.text, definition.options);
|
|
469
|
+
case "altText":
|
|
470
|
+
return target.getByAltText(definition.text, definition.options);
|
|
471
|
+
case "title":
|
|
472
|
+
return target.getByTitle(definition.text, definition.options);
|
|
473
|
+
case "locator":
|
|
474
|
+
return target.locator(definition.selector, definition.options);
|
|
475
|
+
case "frameLocator":
|
|
476
|
+
return target.frameLocator(definition.selector);
|
|
477
|
+
case "testId":
|
|
478
|
+
return target.getByTestId(definition.testId);
|
|
479
|
+
case "id": {
|
|
480
|
+
if (typeof definition.id === "string") {
|
|
481
|
+
const normalized = normalizeIdValue(definition.id);
|
|
482
|
+
return target.locator(`#${cssEscape(normalized ?? "")}`);
|
|
483
|
+
}
|
|
484
|
+
const pattern = definition.id.source;
|
|
485
|
+
const safePattern = cssEscape(pattern);
|
|
486
|
+
return target.locator(`[id*="${safePattern}"]`);
|
|
487
|
+
}
|
|
488
|
+
default: {
|
|
489
|
+
const exhaustive = definition;
|
|
490
|
+
return exhaustive;
|
|
1303
491
|
}
|
|
1304
492
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
493
|
+
};
|
|
494
|
+
var cloneLocatorStrategyDefinition = (definition) => {
|
|
495
|
+
switch (definition.type) {
|
|
496
|
+
case "role":
|
|
497
|
+
return {
|
|
498
|
+
type: "role",
|
|
499
|
+
role: definition.role,
|
|
500
|
+
...definition.options ? { options: { ...definition.options } } : {}
|
|
501
|
+
};
|
|
502
|
+
case "text":
|
|
503
|
+
return {
|
|
504
|
+
type: "text",
|
|
505
|
+
text: definition.text,
|
|
506
|
+
...definition.options ? { options: { ...definition.options } } : {}
|
|
507
|
+
};
|
|
508
|
+
case "label":
|
|
509
|
+
return {
|
|
510
|
+
type: "label",
|
|
511
|
+
text: definition.text,
|
|
512
|
+
...definition.options ? { options: { ...definition.options } } : {}
|
|
513
|
+
};
|
|
514
|
+
case "placeholder":
|
|
515
|
+
return {
|
|
516
|
+
type: "placeholder",
|
|
517
|
+
text: definition.text,
|
|
518
|
+
...definition.options ? { options: { ...definition.options } } : {}
|
|
519
|
+
};
|
|
520
|
+
case "altText":
|
|
521
|
+
return {
|
|
522
|
+
type: "altText",
|
|
523
|
+
text: definition.text,
|
|
524
|
+
...definition.options ? { options: { ...definition.options } } : {}
|
|
525
|
+
};
|
|
526
|
+
case "title":
|
|
527
|
+
return {
|
|
528
|
+
type: "title",
|
|
529
|
+
text: definition.text,
|
|
530
|
+
...definition.options ? { options: { ...definition.options } } : {}
|
|
531
|
+
};
|
|
532
|
+
case "locator":
|
|
533
|
+
return {
|
|
534
|
+
type: "locator",
|
|
535
|
+
selector: definition.selector,
|
|
536
|
+
...definition.options ? { options: { ...definition.options } } : {}
|
|
537
|
+
};
|
|
538
|
+
case "frameLocator":
|
|
539
|
+
return { type: "frameLocator", selector: definition.selector };
|
|
540
|
+
case "testId":
|
|
541
|
+
return { type: "testId", testId: definition.testId };
|
|
542
|
+
case "id":
|
|
543
|
+
return { type: "id", id: definition.id };
|
|
544
|
+
default: {
|
|
545
|
+
const exhaustive = definition;
|
|
546
|
+
return exhaustive;
|
|
1314
547
|
}
|
|
1315
|
-
|
|
1316
|
-
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
var applyDefinitionPatch = (seed, patch) => {
|
|
551
|
+
const base2 = cloneLocatorStrategyDefinition(seed);
|
|
552
|
+
switch (patch.type) {
|
|
553
|
+
case "locator": {
|
|
554
|
+
const selector = patch.selector !== void 0 ? patch.selector : base2.selector;
|
|
555
|
+
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
556
|
+
return { type: "locator", selector, ...options ? { options } : {} };
|
|
1317
557
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
558
|
+
case "role": {
|
|
559
|
+
const role = patch.role ?? base2.role;
|
|
560
|
+
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
561
|
+
return { type: "role", role, ...options ? { options } : {} };
|
|
1320
562
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
563
|
+
case "text": {
|
|
564
|
+
const text = patch.text ?? base2.text;
|
|
565
|
+
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
566
|
+
return { type: "text", text, ...options ? { options } : {} };
|
|
567
|
+
}
|
|
568
|
+
case "label": {
|
|
569
|
+
const text = patch.text ?? base2.text;
|
|
570
|
+
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
571
|
+
return { type: "label", text, ...options ? { options } : {} };
|
|
572
|
+
}
|
|
573
|
+
case "placeholder": {
|
|
574
|
+
const text = patch.text ?? base2.text;
|
|
575
|
+
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
576
|
+
return { type: "placeholder", text, ...options ? { options } : {} };
|
|
577
|
+
}
|
|
578
|
+
case "altText": {
|
|
579
|
+
const text = patch.text ?? base2.text;
|
|
580
|
+
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
581
|
+
return { type: "altText", text, ...options ? { options } : {} };
|
|
582
|
+
}
|
|
583
|
+
case "title": {
|
|
584
|
+
const text = patch.text ?? base2.text;
|
|
585
|
+
const options = patch.options || base2.options ? { ...base2.options, ...patch.options } : void 0;
|
|
586
|
+
return { type: "title", text, ...options ? { options } : {} };
|
|
587
|
+
}
|
|
588
|
+
case "frameLocator": {
|
|
589
|
+
const selector = patch.selector !== void 0 ? patch.selector : base2.selector;
|
|
590
|
+
return { type: "frameLocator", selector };
|
|
591
|
+
}
|
|
592
|
+
case "testId": {
|
|
593
|
+
const testId = patch.testId !== void 0 ? patch.testId : base2.testId;
|
|
594
|
+
return { type: "testId", testId };
|
|
595
|
+
}
|
|
596
|
+
case "id": {
|
|
597
|
+
const id = patch.id !== void 0 ? normalizeIdValue(patch.id) ?? base2.id : base2.id;
|
|
598
|
+
return { type: "id", id };
|
|
599
|
+
}
|
|
600
|
+
default: {
|
|
601
|
+
const exhaustive = patch;
|
|
602
|
+
return exhaustive;
|
|
1323
603
|
}
|
|
1324
|
-
throw new Error("Invalid baseUrl or urlPath types. Expected string or RegExp.");
|
|
1325
|
-
}
|
|
1326
|
-
/**
|
|
1327
|
-
* Implementation of getNestedLocator.
|
|
1328
|
-
*/
|
|
1329
|
-
async getNestedLocator(locatorSchemaPath, subPathIndices) {
|
|
1330
|
-
const withValidation = new WithSubPathValidation(
|
|
1331
|
-
this,
|
|
1332
|
-
this.log.getNewChildLogger("SubPathValidation"),
|
|
1333
|
-
locatorSchemaPath
|
|
1334
|
-
);
|
|
1335
|
-
return await withValidation.getNestedLocator(subPathIndices);
|
|
1336
|
-
}
|
|
1337
|
-
/**
|
|
1338
|
-
* Short-hand wrapper method for calling .getLocatorSchema(LocatorSchemaPath).getLocator()
|
|
1339
|
-
*
|
|
1340
|
-
* This method does not perform nesting,and will return the locator for which the full LocatorSchemaPath resolves to,
|
|
1341
|
-
* provided by getLocatorSchema("...")
|
|
1342
|
-
*
|
|
1343
|
-
* Note: This short-hand wrapper method is useful for quickly getting a locator without having to call
|
|
1344
|
-
* getLocatorSchema("...") first. On the other hand, it can't be used to update or add filters to the LocatorSchema.
|
|
1345
|
-
*
|
|
1346
|
-
* @example
|
|
1347
|
-
* // Usage:
|
|
1348
|
-
* const submitButton = await poc.getLocator("main.form.button@submit");
|
|
1349
|
-
* await expect(submitButton, "should only exist one submit button").toHaveCount(1);
|
|
1350
|
-
*/
|
|
1351
|
-
getLocator = async (locatorSchemaPath) => {
|
|
1352
|
-
return await this.getLocatorSchema(locatorSchemaPath).getLocator();
|
|
1353
|
-
};
|
|
1354
|
-
/**
|
|
1355
|
-
* getLocatorSchema:
|
|
1356
|
-
* Delegates to this.locators.getLocatorSchema.
|
|
1357
|
-
* Returns a chainable schema object for the given path.
|
|
1358
|
-
* Once called with a specific path P, the update and addFilter methods are restricted to sub-paths of P.
|
|
1359
|
-
*
|
|
1360
|
-
* The "getLocatorSchema" method is used to retrieve an updatable deep copy of a LocatorSchema defined in the
|
|
1361
|
-
* GetLocatorBase class. It enriches the returned schema with additional methods to handle updates and retrieval of
|
|
1362
|
-
* deep copy locators.
|
|
1363
|
-
*
|
|
1364
|
-
* getLocatorSchema adds the following chainable methods to the returned LocatorSchemaWithMethods object:
|
|
1365
|
-
*
|
|
1366
|
-
* update
|
|
1367
|
-
* - Allows updating any schema in the chain by specifying the subPath directly.
|
|
1368
|
-
* - Gives compile-time suggestions for valid sub-paths of the LocatorSchemaPath provided to .getLocatorSchema().
|
|
1369
|
-
* - If you want to update multiple schemas, chain multiple .update() calls.
|
|
1370
|
-
*
|
|
1371
|
-
* addFilter(subPath: SubPaths<LocatorSchemaPathType, LocatorSubstring>, filterData: FilterEntry)
|
|
1372
|
-
* - The equivalent of the Playwright locator.filter() method
|
|
1373
|
-
* - This method is used for filtering the specified locator based on the provided filterData.
|
|
1374
|
-
* - Can be chained multiple times to add multiple filters to the same or different LocatorSchema.
|
|
1375
|
-
*
|
|
1376
|
-
* getNestedLocator
|
|
1377
|
-
* - Asynchronously builds a nested locator based on the LocatorSchemaPath provided by getLocatorSchema("...")
|
|
1378
|
-
* - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
|
|
1379
|
-
* - getNestedLocator will end the method chain and return a nested Playwright Locator.
|
|
1380
|
-
* - Optionally parameter takes a list of key(subPath)-value(index) pairs, the locator constructed from the LocatorSchema
|
|
1381
|
-
* with the specified subPath will resolve to the .nth(n) occurrence of the element, within the chain.
|
|
1382
|
-
*
|
|
1383
|
-
* getLocator()
|
|
1384
|
-
* - Asynchronously retrieves a locator based on the current LocatorSchemaPath.
|
|
1385
|
-
* - This method does not perform nesting and will return the locator for which the full LocatorSchemaPath resolves to, provided by getLocatorSchema("...")
|
|
1386
|
-
* - Can be chained once after the update and addFilter methods or directly on the .getLocatorSchema method.
|
|
1387
|
-
* - getLocator will end the method chain and return a Playwright Locator.
|
|
1388
|
-
*
|
|
1389
|
-
* Note: Calling getLocator() and getNestedLocator() on the same LocatorSchemaPath will return a Locator for the same
|
|
1390
|
-
* element, but the Locator returned by getNestedLocator() will be a locator resolving to said same element through
|
|
1391
|
-
* a chain of locators. While the Locator returned by getLocator() will be a single locator which resolves directly
|
|
1392
|
-
* to said element. Thus getLocator() is rarely used, while getNestedLocator() is used extensively.
|
|
1393
|
-
*
|
|
1394
|
-
* That said, for certain use cases, getLocator() can be useful, and you could use it to manually chain locators
|
|
1395
|
-
* yourself if some edge case required it. Though, it would be likely be more prudent to expand your LocatorSchemaPath
|
|
1396
|
-
* type and initLocatorSchemas() method to include the additional locators you need for the given POC, and then use
|
|
1397
|
-
* getNestedLocator() instead, or by implementing a helper method on your Page Object Class.
|
|
1398
|
-
*/
|
|
1399
|
-
getLocatorSchema(path) {
|
|
1400
|
-
return this.locators.getLocatorSchema(path);
|
|
1401
604
|
}
|
|
1402
605
|
};
|
|
1403
|
-
var
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
this.locatorSchemaPath = locatorSchemaPath;
|
|
1407
|
-
}
|
|
1408
|
-
async getNestedLocator(arg) {
|
|
1409
|
-
return await this.pageObjectClass.getLocatorSchema(this.locatorSchemaPath).getNestedLocator(arg);
|
|
1410
|
-
}
|
|
606
|
+
var isFrameLocatorDefinition = (definition) => definition.type === "frameLocator";
|
|
607
|
+
var isLocatorInstance = (value) => {
|
|
608
|
+
return !!value && typeof value === "object" && typeof value.filter === "function";
|
|
1411
609
|
};
|
|
1412
610
|
|
|
1413
|
-
// src/
|
|
1414
|
-
import { selectors as selectors2 } from "@playwright/test";
|
|
1415
|
-
|
|
1416
|
-
// srcV2/locators/locatorUpdateBuilder.ts
|
|
611
|
+
// src/locators/locatorUpdateBuilder.ts
|
|
1417
612
|
var parseUpdateArguments = (primaryOrOptions, options, optionsProvided) => {
|
|
1418
613
|
let primary;
|
|
1419
614
|
let parsedOptions;
|
|
@@ -1903,7 +1098,7 @@ var LocatorUpdateBuilder = class {
|
|
|
1903
1098
|
}
|
|
1904
1099
|
};
|
|
1905
1100
|
|
|
1906
|
-
//
|
|
1101
|
+
// src/locators/locatorQueryBuilder.ts
|
|
1907
1102
|
var LocatorQueryBuilder = class {
|
|
1908
1103
|
constructor(registry, path) {
|
|
1909
1104
|
this.registry = registry;
|
|
@@ -2126,7 +1321,7 @@ var LocatorQueryBuilder = class {
|
|
|
2126
1321
|
}
|
|
2127
1322
|
};
|
|
2128
1323
|
|
|
2129
|
-
//
|
|
1324
|
+
// src/locators/locatorRegistrationBuilder.ts
|
|
2130
1325
|
var LocatorRegistrationBuilder = class {
|
|
2131
1326
|
constructor(registry, path, seed) {
|
|
2132
1327
|
this.registry = registry;
|
|
@@ -2350,7 +1545,7 @@ var LocatorRegistrationBuilder = class {
|
|
|
2350
1545
|
}
|
|
2351
1546
|
};
|
|
2352
1547
|
|
|
2353
|
-
//
|
|
1548
|
+
// src/locators/reusableLocatorBuilder.ts
|
|
2354
1549
|
var ReusableLocatorBuilder = class {
|
|
2355
1550
|
stepsList;
|
|
2356
1551
|
definitionValue;
|
|
@@ -2426,7 +1621,7 @@ var ReusableLocatorFactory = class {
|
|
|
2426
1621
|
}
|
|
2427
1622
|
};
|
|
2428
1623
|
|
|
2429
|
-
//
|
|
1624
|
+
// src/locators/locatorRegistry.ts
|
|
2430
1625
|
var LocatorRegistryInternal = class {
|
|
2431
1626
|
constructor(page) {
|
|
2432
1627
|
this.page = page;
|
|
@@ -2462,20 +1657,45 @@ var LocatorRegistryInternal = class {
|
|
|
2462
1657
|
...record.description !== void 0 ? { description: record.description } : {}
|
|
2463
1658
|
};
|
|
2464
1659
|
}
|
|
2465
|
-
|
|
1660
|
+
/**
|
|
1661
|
+
* Registers a locator schema at the provided dot-delimited path.
|
|
1662
|
+
* Accepts exactly one locator strategy (`getByRole`, `locator`, etc.) and any number of
|
|
1663
|
+
* `filter`/`nth` steps chained in call order. When `{ reuse }` is supplied, the seeded
|
|
1664
|
+
* definition is applied first and one matching override is allowed as a PATCH of the seed;
|
|
1665
|
+
* otherwise, calling multiple locator strategies will throw.
|
|
1666
|
+
*
|
|
1667
|
+
* @example
|
|
1668
|
+
* ```ts
|
|
1669
|
+
* registry
|
|
1670
|
+
* .add("list.item")
|
|
1671
|
+
* .getByRole("listitem", { name: /Row/ })
|
|
1672
|
+
* .filter({ hasText: "Row" })
|
|
1673
|
+
* .nth("last");
|
|
1674
|
+
* ```
|
|
1675
|
+
*/
|
|
1676
|
+
add(path, ...args) {
|
|
1677
|
+
const options = args[0];
|
|
2466
1678
|
const reuse = options?.reuse;
|
|
2467
|
-
if (
|
|
1679
|
+
if (reuse === void 0) {
|
|
2468
1680
|
return new LocatorRegistrationBuilder(
|
|
2469
1681
|
this,
|
|
2470
1682
|
path
|
|
2471
1683
|
);
|
|
2472
1684
|
}
|
|
2473
1685
|
if (typeof reuse === "string") {
|
|
1686
|
+
const targetPath = path;
|
|
1687
|
+
if (reuse === targetPath) {
|
|
1688
|
+
throw new Error(`Locator reuse path cannot be the same as registration path: "${targetPath}".`);
|
|
1689
|
+
}
|
|
2474
1690
|
const sourceRecord = this.get(reuse);
|
|
2475
|
-
const cloned = this.cloneRecordForReuse(sourceRecord,
|
|
2476
|
-
this.register(
|
|
1691
|
+
const cloned = this.cloneRecordForReuse(sourceRecord, targetPath);
|
|
1692
|
+
this.register(targetPath, cloned);
|
|
2477
1693
|
return void 0;
|
|
2478
1694
|
}
|
|
1695
|
+
if (Array.isArray(reuse)) {
|
|
1696
|
+
const [reason] = reuse;
|
|
1697
|
+
throw new Error(`Invalid reuse path configuration for "${path}": ${reason}`);
|
|
1698
|
+
}
|
|
2479
1699
|
const reusedRecord = this.cloneRecordForReuse(
|
|
2480
1700
|
{
|
|
2481
1701
|
locatorSchemaPath: path,
|
|
@@ -2526,7 +1746,7 @@ Existing Schema: ${errorDetails}`);
|
|
|
2526
1746
|
}
|
|
2527
1747
|
return {
|
|
2528
1748
|
locatorSchemaPath: record.locatorSchemaPath,
|
|
2529
|
-
definition: record.definition,
|
|
1749
|
+
definition: cloneLocatorStrategyDefinition(record.definition),
|
|
2530
1750
|
steps: normalizeSteps(record.steps),
|
|
2531
1751
|
...record.description !== void 0 ? { description: record.description } : {}
|
|
2532
1752
|
};
|
|
@@ -2541,7 +1761,7 @@ Existing Schema: ${errorDetails}`);
|
|
|
2541
1761
|
}
|
|
2542
1762
|
return {
|
|
2543
1763
|
locatorSchemaPath: record.locatorSchemaPath,
|
|
2544
|
-
definition: record.definition,
|
|
1764
|
+
definition: cloneLocatorStrategyDefinition(record.definition),
|
|
2545
1765
|
steps: normalizeSteps(record.steps),
|
|
2546
1766
|
...record.description !== void 0 ? { description: record.description } : {}
|
|
2547
1767
|
};
|
|
@@ -2620,7 +1840,10 @@ Existing Schema: ${errorDetails}`);
|
|
|
2620
1840
|
* ```
|
|
2621
1841
|
*/
|
|
2622
1842
|
getLocatorSchema(path) {
|
|
2623
|
-
return new LocatorQueryBuilder(
|
|
1843
|
+
return new LocatorQueryBuilder(
|
|
1844
|
+
this,
|
|
1845
|
+
path
|
|
1846
|
+
);
|
|
2624
1847
|
}
|
|
2625
1848
|
/**
|
|
2626
1849
|
* Resolves the Playwright {@link Locator} for the provided path, applying only the terminal
|
|
@@ -2723,7 +1946,6 @@ Existing Schema: ${errorDetails}`);
|
|
|
2723
1946
|
}
|
|
2724
1947
|
};
|
|
2725
1948
|
var createRegistryWithAccessors = (page) => {
|
|
2726
|
-
const _assertValidPaths = true;
|
|
2727
1949
|
const registryInstance = new LocatorRegistryInternal(page);
|
|
2728
1950
|
const add = registryInstance.add.bind(registryInstance);
|
|
2729
1951
|
const getLocator = registryInstance.getLocator.bind(registryInstance);
|
|
@@ -2733,98 +1955,8 @@ var createRegistryWithAccessors = (page) => {
|
|
|
2733
1955
|
return { registry, add, getLocator, getNestedLocator, getLocatorSchema };
|
|
2734
1956
|
};
|
|
2735
1957
|
|
|
2736
|
-
// src/
|
|
2737
|
-
|
|
2738
|
-
/** Provides Playwright page methods */
|
|
2739
|
-
page;
|
|
2740
|
-
/** Playwright TestInfo contains information about currently running test, available to any test function */
|
|
2741
|
-
testInfo;
|
|
2742
|
-
/** Selectors can be used to install custom selector engines.*/
|
|
2743
|
-
selector;
|
|
2744
|
-
/** The base URL of the Page Object Class */
|
|
2745
|
-
baseUrl;
|
|
2746
|
-
/** The URL path of the Page Object Class */
|
|
2747
|
-
urlPath;
|
|
2748
|
-
/** The full URL of the Page Object Class */
|
|
2749
|
-
fullUrl;
|
|
2750
|
-
/** The name of the Page Object Class */
|
|
2751
|
-
pocName;
|
|
2752
|
-
/** The Page Object Class' PlaywrightReportLogger instance, prefixed with its name. Log levels: debug, info, warn, and error. */
|
|
2753
|
-
log;
|
|
2754
|
-
/** The SessionStorage class provides methods for setting and getting session storage data in Playwright.*/
|
|
2755
|
-
sessionStorage;
|
|
2756
|
-
/**
|
|
2757
|
-
* locators:
|
|
2758
|
-
* An instance of GetLocatorBase that handles schema management and provides getLocatorSchema calls.
|
|
2759
|
-
* Initially, LocatorSubstring is undefined. Once getLocatorSchema(path) is called,
|
|
2760
|
-
* we get a chainable object typed with LocatorSubstring = P.
|
|
2761
|
-
*/
|
|
2762
|
-
locators;
|
|
2763
|
-
/**
|
|
2764
|
-
* v2 locator registry and accessors, used for migration to the fluent registry DSL.
|
|
2765
|
-
*/
|
|
2766
|
-
locatorRegistry;
|
|
2767
|
-
add;
|
|
2768
|
-
getLocator;
|
|
2769
|
-
getLocatorSchema;
|
|
2770
|
-
getNestedLocator;
|
|
2771
|
-
constructor(page, testInfo, baseUrl, urlPath, pocName, pwrl, locatorSubstring) {
|
|
2772
|
-
this.page = page;
|
|
2773
|
-
this.testInfo = testInfo;
|
|
2774
|
-
this.selector = selectors2;
|
|
2775
|
-
this.baseUrl = baseUrl;
|
|
2776
|
-
this.urlPath = urlPath;
|
|
2777
|
-
this.fullUrl = this.constructFullUrl(baseUrl, urlPath);
|
|
2778
|
-
this.pocName = pocName;
|
|
2779
|
-
this.log = pwrl.getNewChildLogger(pocName);
|
|
2780
|
-
const classDeprecationMessage = "[POMWright] BasePageV1toV2 is a transitional bridge and will be removed in 2.0.0. Prefer PageObject for new work and migrate existing classes to PageObject.";
|
|
2781
|
-
warnDeprecationOncePerTest(`${this.constructor.name}-class-deprecation`, classDeprecationMessage, this.log);
|
|
2782
|
-
const { registry, add, getLocator, getNestedLocator, getLocatorSchema } = createRegistryWithAccessors(page);
|
|
2783
|
-
this.locatorRegistry = registry;
|
|
2784
|
-
this.add = add;
|
|
2785
|
-
this.getLocator = getLocator;
|
|
2786
|
-
this.getLocatorSchema = getLocatorSchema;
|
|
2787
|
-
this.getNestedLocator = getNestedLocator;
|
|
2788
|
-
this.defineLocators();
|
|
2789
|
-
this.locators = new GetLocatorBase(
|
|
2790
|
-
this,
|
|
2791
|
-
this.log.getNewChildLogger("GetLocator"),
|
|
2792
|
-
locatorSubstring
|
|
2793
|
-
);
|
|
2794
|
-
const initLocatorSchemasDeprecationMessage = "[POMWright] initLocatorSchemas is deprecated and will be removed in 2.0.0. Define locators with the v2 registry DSL in defineLocators instead.";
|
|
2795
|
-
warnDeprecationOncePerTest(
|
|
2796
|
-
`${this.constructor.name}-initLocatorSchemas-deprecation`,
|
|
2797
|
-
initLocatorSchemasDeprecationMessage,
|
|
2798
|
-
this.log
|
|
2799
|
-
);
|
|
2800
|
-
this.initLocatorSchemas();
|
|
2801
|
-
this.sessionStorage = new SessionStorage(this.page, this.pocName);
|
|
2802
|
-
}
|
|
2803
|
-
/**
|
|
2804
|
-
* constructFullUrl:
|
|
2805
|
-
* Combines baseUrl and urlPath, handling both strings and RegExps.
|
|
2806
|
-
* Ensures a flexible approach to URL matching (string or regex-based).
|
|
2807
|
-
*/
|
|
2808
|
-
constructFullUrl(baseUrl, urlPath) {
|
|
2809
|
-
const escapeStringForRegExp = (str) => str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
2810
|
-
if (typeof baseUrl === "string" && typeof urlPath === "string") {
|
|
2811
|
-
return `${baseUrl}${urlPath}`;
|
|
2812
|
-
}
|
|
2813
|
-
if (typeof baseUrl === "string" && urlPath instanceof RegExp) {
|
|
2814
|
-
return new RegExp(`^${escapeStringForRegExp(baseUrl)}${urlPath.source}`);
|
|
2815
|
-
}
|
|
2816
|
-
if (baseUrl instanceof RegExp && typeof urlPath === "string") {
|
|
2817
|
-
return new RegExp(`${baseUrl.source}${escapeStringForRegExp(urlPath)}$`);
|
|
2818
|
-
}
|
|
2819
|
-
if (baseUrl instanceof RegExp && urlPath instanceof RegExp) {
|
|
2820
|
-
return new RegExp(`${baseUrl.source}${urlPath.source}`);
|
|
2821
|
-
}
|
|
2822
|
-
throw new Error("Invalid baseUrl or urlPath types. Expected string or RegExp.");
|
|
2823
|
-
}
|
|
2824
|
-
};
|
|
2825
|
-
|
|
2826
|
-
// srcV2/helpers/navigation.ts
|
|
2827
|
-
import { expect, test as test3 } from "@playwright/test";
|
|
1958
|
+
// src/helpers/navigation.ts
|
|
1959
|
+
import { expect, test as test4 } from "@playwright/test";
|
|
2828
1960
|
var DEFAULT_WAIT_UNTIL = "load";
|
|
2829
1961
|
var DEFAULT_LOAD_STATE = "load";
|
|
2830
1962
|
var Navigation = class {
|
|
@@ -2859,7 +1991,7 @@ var Navigation = class {
|
|
|
2859
1991
|
if (typeof this.baseUrl !== "string" || typeof this.urlPath !== "string") {
|
|
2860
1992
|
throw new Error("goto() is not supported when baseUrl or urlPath is a RegExp.");
|
|
2861
1993
|
}
|
|
2862
|
-
await
|
|
1994
|
+
await test4.step(`${this.label}: Navigate to the provided URL or URL Path`, async () => {
|
|
2863
1995
|
const targetUrl = urlPathOrUrl.startsWith("/") ? `${this.baseUrl}${urlPathOrUrl}` : urlPathOrUrl;
|
|
2864
1996
|
await this.page.goto(targetUrl, { waitUntil });
|
|
2865
1997
|
});
|
|
@@ -2869,247 +2001,60 @@ var Navigation = class {
|
|
|
2869
2001
|
* Available only when fullUrl is a string.
|
|
2870
2002
|
*/
|
|
2871
2003
|
async gotoThisPage(options) {
|
|
2872
|
-
if (typeof this.fullUrl !== "string") {
|
|
2873
|
-
throw new Error("gotoThisPage() is not supported when fullUrl is a RegExp.");
|
|
2874
|
-
}
|
|
2875
|
-
const waitUntil = this.resolveWaitUntil(options);
|
|
2876
|
-
const fullUrl = this.fullUrl;
|
|
2877
|
-
await
|
|
2878
|
-
await this.page.goto(fullUrl, { waitUntil });
|
|
2879
|
-
await this.executeActions();
|
|
2880
|
-
});
|
|
2881
|
-
}
|
|
2882
|
-
/**
|
|
2883
|
-
* Expect to be on this page. Works with both string and RegExp fullUrl values.
|
|
2884
|
-
* Uses waitUntil from navigation options when waiting for URL.
|
|
2885
|
-
*/
|
|
2886
|
-
async expectThisPage(options) {
|
|
2887
|
-
const waitUntil = this.resolveWaitUntil(options);
|
|
2888
|
-
await
|
|
2889
|
-
await this.page.waitForURL(this.fullUrl, { waitUntil });
|
|
2890
|
-
await expect(async () => {
|
|
2891
|
-
if (this.fullUrl instanceof RegExp) {
|
|
2892
|
-
expect(this.page.url(), `expected '${this.fullUrl}', found '${this.page.url()}'`).toMatch(this.fullUrl);
|
|
2893
|
-
} else {
|
|
2894
|
-
expect(this.page.url(), `expected '${this.fullUrl}', found '${this.page.url()}'`).toBe(this.fullUrl);
|
|
2895
|
-
}
|
|
2896
|
-
}).toPass();
|
|
2897
|
-
await this.executeActions();
|
|
2898
|
-
});
|
|
2899
|
-
}
|
|
2900
|
-
/**
|
|
2901
|
-
* Expect to be on any other page (i.e. not this page).
|
|
2902
|
-
* Uses waitForLoadState from navigation options before validating URL.
|
|
2903
|
-
*/
|
|
2904
|
-
async expectAnotherPage(options) {
|
|
2905
|
-
const waitForLoadState = this.resolveWaitForLoadState(options);
|
|
2906
|
-
await test3.step(`${this.label}: Expect any other Page`, async () => {
|
|
2907
|
-
await this.page.waitForLoadState(waitForLoadState);
|
|
2908
|
-
if (this.fullUrl instanceof RegExp) {
|
|
2909
|
-
await expect.poll(async () => this.page.url(), {
|
|
2910
|
-
message: `expected url to not match '${this.fullUrl}'`
|
|
2911
|
-
}).not.toMatch(this.fullUrl);
|
|
2912
|
-
} else {
|
|
2913
|
-
await expect.poll(async () => this.page.url(), {
|
|
2914
|
-
message: `expected url to not be '${this.fullUrl}', found '${this.page.url()}'`
|
|
2915
|
-
}).not.toBe(this.fullUrl);
|
|
2916
|
-
}
|
|
2917
|
-
});
|
|
2918
|
-
}
|
|
2919
|
-
};
|
|
2920
|
-
function createNavigation(page, baseUrl, urlPath, fullUrl, label, actions = null, defaultOptions) {
|
|
2921
|
-
const navigation = new Navigation(page, baseUrl, urlPath, fullUrl, label, actions, defaultOptions);
|
|
2922
|
-
return navigation;
|
|
2923
|
-
}
|
|
2924
|
-
|
|
2925
|
-
// srcV2/helpers/sessionStorage.ts
|
|
2926
|
-
import { test as test4 } from "@playwright/test";
|
|
2927
|
-
var SessionStorage2 = class {
|
|
2928
|
-
// Initializes the class with a Playwright Page object and an optional label for step titles.
|
|
2929
|
-
constructor(page, options = {}) {
|
|
2930
|
-
this.page = page;
|
|
2931
|
-
this.options = options;
|
|
2932
|
-
}
|
|
2933
|
-
// Defines an object to hold states to be set in session storage, allowing any value type.
|
|
2934
|
-
queuedStates = {};
|
|
2935
|
-
// Indicates if the session storage manipulation has been initiated.
|
|
2936
|
-
isInitiated = false;
|
|
2937
|
-
getStepLabel(methodName) {
|
|
2938
|
-
const prefix = this.options.label ? `${this.options.label}.` : "";
|
|
2939
|
-
return `${prefix}SessionStorage.${methodName}:`;
|
|
2940
|
-
}
|
|
2941
|
-
async hasContext() {
|
|
2942
|
-
return await this.page.evaluate(() => {
|
|
2943
|
-
return typeof window !== "undefined" && window.sessionStorage !== void 0;
|
|
2944
|
-
});
|
|
2945
|
-
}
|
|
2946
|
-
async waitForContextAvailability() {
|
|
2947
|
-
try {
|
|
2948
|
-
const contextExists = await this.hasContext();
|
|
2949
|
-
if (contextExists) {
|
|
2950
|
-
return;
|
|
2951
|
-
}
|
|
2952
|
-
} catch (_e) {
|
|
2953
|
-
}
|
|
2954
|
-
await new Promise((resolve) => {
|
|
2955
|
-
const handler = async (frame) => {
|
|
2956
|
-
if (frame !== this.page.mainFrame()) {
|
|
2957
|
-
return;
|
|
2958
|
-
}
|
|
2959
|
-
try {
|
|
2960
|
-
const contextExists = await this.hasContext();
|
|
2961
|
-
if (!contextExists) {
|
|
2962
|
-
return;
|
|
2963
|
-
}
|
|
2964
|
-
} catch (_e) {
|
|
2965
|
-
return;
|
|
2966
|
-
}
|
|
2967
|
-
this.page.off("framenavigated", handler);
|
|
2968
|
-
resolve();
|
|
2969
|
-
};
|
|
2970
|
-
this.page.on("framenavigated", handler);
|
|
2971
|
-
});
|
|
2972
|
-
}
|
|
2973
|
-
async ensureContext({ waitForContext = false } = {}) {
|
|
2974
|
-
try {
|
|
2975
|
-
const contextExists = await this.hasContext();
|
|
2976
|
-
if (contextExists) {
|
|
2977
|
-
return;
|
|
2978
|
-
}
|
|
2979
|
-
} catch (_e) {
|
|
2980
|
-
}
|
|
2981
|
-
if (!waitForContext) {
|
|
2982
|
-
throw new Error("SessionStorage context is not available.");
|
|
2983
|
-
}
|
|
2984
|
-
await this.waitForContextAvailability();
|
|
2985
|
-
}
|
|
2986
|
-
/** Writes states to session storage. Accepts an object with key-value pairs representing the states. */
|
|
2987
|
-
async writeToSessionStorage(states) {
|
|
2988
|
-
await this.page.evaluate((storage) => {
|
|
2989
|
-
for (const [key, value] of Object.entries(storage)) {
|
|
2990
|
-
window.sessionStorage.setItem(key, JSON.stringify(value));
|
|
2991
|
-
}
|
|
2992
|
-
}, states);
|
|
2993
|
-
}
|
|
2994
|
-
/** Reads all states from session storage and returns them as an object. */
|
|
2995
|
-
async readFromSessionStorage() {
|
|
2996
|
-
const storage = await this.page.evaluate(() => {
|
|
2997
|
-
const storage2 = {};
|
|
2998
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
2999
|
-
const key = sessionStorage.key(i);
|
|
3000
|
-
if (key !== null) {
|
|
3001
|
-
const item = sessionStorage.getItem(key);
|
|
3002
|
-
try {
|
|
3003
|
-
storage2[key] = item ? JSON.parse(item) : null;
|
|
3004
|
-
} catch (_e) {
|
|
3005
|
-
storage2[key] = item;
|
|
3006
|
-
}
|
|
3007
|
-
}
|
|
3008
|
-
}
|
|
3009
|
-
return storage2;
|
|
3010
|
-
});
|
|
3011
|
-
return storage;
|
|
3012
|
-
}
|
|
3013
|
-
/**
|
|
3014
|
-
* Sets the specified states in session storage.
|
|
3015
|
-
* Optionally waits for the next main-frame navigation to establish a valid context before writing,
|
|
3016
|
-
* and reloads the page after setting the data.
|
|
3017
|
-
*
|
|
3018
|
-
* Parameters:
|
|
3019
|
-
* states: Object representing the states to set in session storage.
|
|
3020
|
-
* reload: Boolean indicating whether to reload the page after setting the session storage data.
|
|
3021
|
-
* waitForContext: Boolean indicating whether to wait for a main-frame navigation and a valid context.
|
|
3022
|
-
*/
|
|
3023
|
-
async set(states, options = {}) {
|
|
3024
|
-
await test4.step(this.getStepLabel("set"), async () => {
|
|
3025
|
-
await this.ensureContext({ waitForContext: options.waitForContext });
|
|
3026
|
-
await this.writeToSessionStorage(states);
|
|
3027
|
-
if (options.reload) {
|
|
3028
|
-
await this.page.reload();
|
|
3029
|
-
}
|
|
3030
|
-
});
|
|
3031
|
-
}
|
|
3032
|
-
/**
|
|
3033
|
-
* Queues states to be set in the sessionStorage before the next navigation occurs.
|
|
3034
|
-
* Handles different scenarios based on multiple calls made before the navigation occurs.
|
|
3035
|
-
*
|
|
3036
|
-
* 1. No Context, Single Call: Queues and sets states upon the next navigation.
|
|
3037
|
-
* 2. No Context, Multiple Calls: Merges states from multiple calls and sets them upon the next navigation.
|
|
3038
|
-
* 3. With Context: Still queues until the next navigation.
|
|
3039
|
-
*
|
|
3040
|
-
* Parameters:
|
|
3041
|
-
* states: Object representing the states to queue for setting in session storage.
|
|
3042
|
-
*/
|
|
3043
|
-
async setOnNextNavigation(states) {
|
|
3044
|
-
this.queuedStates = { ...this.queuedStates, ...states };
|
|
3045
|
-
const populateStorage = async () => {
|
|
3046
|
-
await test4.step(this.getStepLabel("setOnNextNavigation"), async () => {
|
|
3047
|
-
await this.writeToSessionStorage(this.queuedStates);
|
|
3048
|
-
});
|
|
3049
|
-
this.queuedStates = {};
|
|
3050
|
-
};
|
|
3051
|
-
if (!this.isInitiated) {
|
|
3052
|
-
this.isInitiated = true;
|
|
3053
|
-
const handler = async (frame) => {
|
|
3054
|
-
if (frame !== this.page.mainFrame()) {
|
|
3055
|
-
return;
|
|
3056
|
-
}
|
|
3057
|
-
await populateStorage();
|
|
3058
|
-
this.page.off("framenavigated", handler);
|
|
3059
|
-
this.isInitiated = false;
|
|
3060
|
-
};
|
|
3061
|
-
this.page.on("framenavigated", handler);
|
|
3062
|
-
}
|
|
3063
|
-
}
|
|
3064
|
-
async get(keys, options = {}) {
|
|
3065
|
-
let result = {};
|
|
3066
|
-
await test4.step(this.getStepLabel("get"), async () => {
|
|
3067
|
-
await this.ensureContext(options);
|
|
3068
|
-
const allData = await this.readFromSessionStorage();
|
|
3069
|
-
if (keys && keys.length > 0) {
|
|
3070
|
-
for (const key of keys) {
|
|
3071
|
-
if (Object.hasOwn(allData, key)) {
|
|
3072
|
-
const value = allData[key];
|
|
3073
|
-
if (value !== void 0) {
|
|
3074
|
-
result[key] = value;
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
2004
|
+
if (typeof this.fullUrl !== "string") {
|
|
2005
|
+
throw new Error("gotoThisPage() is not supported when fullUrl is a RegExp.");
|
|
2006
|
+
}
|
|
2007
|
+
const waitUntil = this.resolveWaitUntil(options);
|
|
2008
|
+
const fullUrl = this.fullUrl;
|
|
2009
|
+
await test4.step(`${this.label}: Navigate to this Page`, async () => {
|
|
2010
|
+
await this.page.goto(fullUrl, { waitUntil });
|
|
2011
|
+
await this.executeActions();
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* Expect to be on this page. Works with both string and RegExp fullUrl values.
|
|
2016
|
+
* Uses waitUntil from navigation options when waiting for URL.
|
|
2017
|
+
*/
|
|
2018
|
+
async expectThisPage(options) {
|
|
2019
|
+
const waitUntil = this.resolveWaitUntil(options);
|
|
2020
|
+
await test4.step(`${this.label}: Expect this Page`, async () => {
|
|
2021
|
+
await this.page.waitForURL(this.fullUrl, { waitUntil });
|
|
2022
|
+
await expect(async () => {
|
|
2023
|
+
if (this.fullUrl instanceof RegExp) {
|
|
2024
|
+
expect(this.page.url(), `expected '${this.fullUrl}', found '${this.page.url()}'`).toMatch(this.fullUrl);
|
|
2025
|
+
} else {
|
|
2026
|
+
expect(this.page.url(), `expected '${this.fullUrl}', found '${this.page.url()}'`).toBe(this.fullUrl);
|
|
3077
2027
|
}
|
|
3078
|
-
}
|
|
3079
|
-
|
|
3080
|
-
}
|
|
2028
|
+
}).toPass();
|
|
2029
|
+
await this.executeActions();
|
|
3081
2030
|
});
|
|
3082
|
-
return result;
|
|
3083
2031
|
}
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
if (
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
await this.page.evaluate(() => sessionStorage.clear());
|
|
3101
|
-
return;
|
|
2032
|
+
/**
|
|
2033
|
+
* Expect to be on any other page (i.e. not this page).
|
|
2034
|
+
* Uses waitForLoadState from navigation options before validating URL.
|
|
2035
|
+
*/
|
|
2036
|
+
async expectAnotherPage(options) {
|
|
2037
|
+
const waitForLoadState = this.resolveWaitForLoadState(options);
|
|
2038
|
+
await test4.step(`${this.label}: Expect any other Page`, async () => {
|
|
2039
|
+
await this.page.waitForLoadState(waitForLoadState);
|
|
2040
|
+
if (this.fullUrl instanceof RegExp) {
|
|
2041
|
+
await expect.poll(async () => this.page.url(), {
|
|
2042
|
+
message: `expected url to not match '${this.fullUrl}'`
|
|
2043
|
+
}).not.toMatch(this.fullUrl);
|
|
2044
|
+
} else {
|
|
2045
|
+
await expect.poll(async () => this.page.url(), {
|
|
2046
|
+
message: `expected url to not be '${this.fullUrl}', found '${this.page.url()}'`
|
|
2047
|
+
}).not.toBe(this.fullUrl);
|
|
3102
2048
|
}
|
|
3103
|
-
await this.page.evaluate((keysToClear) => {
|
|
3104
|
-
for (const key of keysToClear) {
|
|
3105
|
-
sessionStorage.removeItem(key);
|
|
3106
|
-
}
|
|
3107
|
-
}, keys);
|
|
3108
2049
|
});
|
|
3109
2050
|
}
|
|
3110
2051
|
};
|
|
2052
|
+
function createNavigation(page, baseUrl, urlPath, fullUrl, label, actions = null, defaultOptions) {
|
|
2053
|
+
const navigation = new Navigation(page, baseUrl, urlPath, fullUrl, label, actions, defaultOptions);
|
|
2054
|
+
return navigation;
|
|
2055
|
+
}
|
|
3111
2056
|
|
|
3112
|
-
//
|
|
2057
|
+
// src/pageObject.ts
|
|
3113
2058
|
var PageObject = class {
|
|
3114
2059
|
page;
|
|
3115
2060
|
baseUrl;
|
|
@@ -3136,7 +2081,7 @@ var PageObject = class {
|
|
|
3136
2081
|
this.getLocator = getLocator;
|
|
3137
2082
|
this.getLocatorSchema = getLocatorSchema;
|
|
3138
2083
|
this.getNestedLocator = getNestedLocator;
|
|
3139
|
-
this.sessionStorage = new
|
|
2084
|
+
this.sessionStorage = new SessionStorage(page, { label });
|
|
3140
2085
|
this.defineLocators();
|
|
3141
2086
|
this.navigation = createNavigation(
|
|
3142
2087
|
this.page,
|
|
@@ -3165,211 +2110,11 @@ var PageObject = class {
|
|
|
3165
2110
|
throw new Error("Invalid baseUrl or urlPath types. Expected string or RegExp.");
|
|
3166
2111
|
}
|
|
3167
2112
|
};
|
|
3168
|
-
|
|
3169
|
-
// srcV2/fixture/base.fixtures.ts
|
|
3170
|
-
import { test as base } from "@playwright/test";
|
|
3171
|
-
|
|
3172
|
-
// srcV2/helpers/playwrightReportLogger.ts
|
|
3173
|
-
var PlaywrightReportLogger = class _PlaywrightReportLogger {
|
|
3174
|
-
// Initializes the logger with shared log level, log entries, and a context name.
|
|
3175
|
-
constructor(sharedLogLevel, sharedLogEntry, contextName) {
|
|
3176
|
-
this.sharedLogLevel = sharedLogLevel;
|
|
3177
|
-
this.sharedLogEntry = sharedLogEntry;
|
|
3178
|
-
this.contextName = contextName;
|
|
3179
|
-
}
|
|
3180
|
-
contextName;
|
|
3181
|
-
logLevels = ["debug", "info", "warn", "error"];
|
|
3182
|
-
/**
|
|
3183
|
-
* Creates a child logger with a new contextual name, sharing the same log level and log entries with the parent logger.
|
|
3184
|
-
*
|
|
3185
|
-
* The root loggers log "level" is referenced by all child loggers and their child loggers and so on...
|
|
3186
|
-
* Changing the log "level" of one, will change it for all.
|
|
3187
|
-
*/
|
|
3188
|
-
getNewChildLogger(prefix) {
|
|
3189
|
-
return new _PlaywrightReportLogger(this.sharedLogLevel, this.sharedLogEntry, `${this.contextName} -> ${prefix}`);
|
|
3190
|
-
}
|
|
3191
|
-
/**
|
|
3192
|
-
* Logs a message with the specified log level, prefix, and additional arguments if the current log level permits.
|
|
3193
|
-
*/
|
|
3194
|
-
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
3195
|
-
log(level, message, ...args) {
|
|
3196
|
-
const logLevelIndex = this.logLevels.indexOf(level);
|
|
3197
|
-
if (logLevelIndex < this.getCurrentLogLevelIndex()) {
|
|
3198
|
-
return;
|
|
3199
|
-
}
|
|
3200
|
-
this.sharedLogEntry.push({
|
|
3201
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
3202
|
-
logLevel: level,
|
|
3203
|
-
prefix: this.contextName,
|
|
3204
|
-
message: `${message}
|
|
3205
|
-
|
|
3206
|
-
${args.join("\n\n")}`
|
|
3207
|
-
});
|
|
3208
|
-
}
|
|
3209
|
-
/**
|
|
3210
|
-
* Logs a debug-level message with the specified message and arguments.
|
|
3211
|
-
*/
|
|
3212
|
-
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
3213
|
-
debug(message, ...args) {
|
|
3214
|
-
this.log("debug", message, ...args);
|
|
3215
|
-
}
|
|
3216
|
-
/**
|
|
3217
|
-
* Logs a info-level message with the specified message and arguments.
|
|
3218
|
-
*/
|
|
3219
|
-
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
3220
|
-
info(message, ...args) {
|
|
3221
|
-
this.log("info", message, ...args);
|
|
3222
|
-
}
|
|
3223
|
-
/**
|
|
3224
|
-
* Logs a warn-level message with the specified message and arguments.
|
|
3225
|
-
*/
|
|
3226
|
-
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
3227
|
-
warn(message, ...args) {
|
|
3228
|
-
this.log("warn", message, ...args);
|
|
3229
|
-
}
|
|
3230
|
-
/**
|
|
3231
|
-
* Logs a error-level message with the specified message and arguments.
|
|
3232
|
-
*/
|
|
3233
|
-
// biome-ignore lint/suspicious/noExplicitAny: logger accepts arbitrary payloads for debug output.
|
|
3234
|
-
error(message, ...args) {
|
|
3235
|
-
this.log("error", message, ...args);
|
|
3236
|
-
}
|
|
3237
|
-
/**
|
|
3238
|
-
* Sets the current log level to the specified level during runTime.
|
|
3239
|
-
*/
|
|
3240
|
-
setLogLevel(level) {
|
|
3241
|
-
this.sharedLogLevel.current = level;
|
|
3242
|
-
}
|
|
3243
|
-
/**
|
|
3244
|
-
* Retrieves the current log level during runtime.
|
|
3245
|
-
*/
|
|
3246
|
-
getCurrentLogLevel() {
|
|
3247
|
-
return this.sharedLogLevel.current;
|
|
3248
|
-
}
|
|
3249
|
-
/**
|
|
3250
|
-
* Retrieves the index of the current log level in the logLevels array during runtime.
|
|
3251
|
-
*/
|
|
3252
|
-
getCurrentLogLevelIndex() {
|
|
3253
|
-
return this.logLevels.indexOf(this.sharedLogLevel.current);
|
|
3254
|
-
}
|
|
3255
|
-
/**
|
|
3256
|
-
* Resets the current log level to the initial level during runtime.
|
|
3257
|
-
*/
|
|
3258
|
-
resetLogLevel() {
|
|
3259
|
-
this.sharedLogLevel.current = this.sharedLogLevel.initial;
|
|
3260
|
-
}
|
|
3261
|
-
/**
|
|
3262
|
-
* Checks if the input log level is equal to the current log level of the PlaywrightReportLogger instance.
|
|
3263
|
-
*/
|
|
3264
|
-
isCurrentLogLevel(level) {
|
|
3265
|
-
return this.sharedLogLevel.current === level;
|
|
3266
|
-
}
|
|
3267
|
-
/**
|
|
3268
|
-
* Returns 'true' if the "level" parameter provided has an equal or greater index than the current logLevel.
|
|
3269
|
-
*/
|
|
3270
|
-
isLogLevelEnabled(level) {
|
|
3271
|
-
const logLevelIndex = this.logLevels.indexOf(level);
|
|
3272
|
-
if (logLevelIndex < this.getCurrentLogLevelIndex()) {
|
|
3273
|
-
return false;
|
|
3274
|
-
}
|
|
3275
|
-
return true;
|
|
3276
|
-
}
|
|
3277
|
-
/**
|
|
3278
|
-
* Attaches the recorded log entries to the Playwright HTML report in a sorted and formatted manner.
|
|
3279
|
-
*/
|
|
3280
|
-
attachLogsToTest(testInfo) {
|
|
3281
|
-
this.sharedLogEntry.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
3282
|
-
for (const log of this.sharedLogEntry) {
|
|
3283
|
-
const printTime = log.timestamp.toLocaleTimeString("nb-NO", {
|
|
3284
|
-
hour: "2-digit",
|
|
3285
|
-
minute: "2-digit",
|
|
3286
|
-
second: "2-digit"
|
|
3287
|
-
});
|
|
3288
|
-
const printDate = log.timestamp.toLocaleDateString("nb-NO", {
|
|
3289
|
-
day: "2-digit",
|
|
3290
|
-
month: "2-digit",
|
|
3291
|
-
year: "numeric"
|
|
3292
|
-
});
|
|
3293
|
-
const printLogLevel = `${log.logLevel.toUpperCase()}`;
|
|
3294
|
-
const printPrefix = log.prefix ? `: [${log.prefix}]` : "";
|
|
3295
|
-
let messageBody = "";
|
|
3296
|
-
let messageContentType = "";
|
|
3297
|
-
try {
|
|
3298
|
-
const parsedMessage = JSON.parse(log.message);
|
|
3299
|
-
messageContentType = "application/json";
|
|
3300
|
-
messageBody = JSON.stringify(parsedMessage, null, 2);
|
|
3301
|
-
} catch (_error) {
|
|
3302
|
-
messageContentType = "text/plain";
|
|
3303
|
-
messageBody = log.message;
|
|
3304
|
-
}
|
|
3305
|
-
testInfo.attach(`${printTime} ${printDate} - ${printLogLevel} ${printPrefix}`, {
|
|
3306
|
-
contentType: messageContentType,
|
|
3307
|
-
body: Buffer.from(messageBody)
|
|
3308
|
-
});
|
|
3309
|
-
}
|
|
3310
|
-
}
|
|
3311
|
-
};
|
|
3312
|
-
|
|
3313
|
-
// srcV2/fixture/base.fixtures.ts
|
|
3314
|
-
var test5 = base.extend({
|
|
3315
|
-
// biome-ignore lint/correctness/noEmptyPattern: Playwright does not support the use of _
|
|
3316
|
-
log: async ({}, use, testInfo) => {
|
|
3317
|
-
const contextName = "TestCase";
|
|
3318
|
-
const sharedLogEntry = [];
|
|
3319
|
-
const sharedLogLevel = testInfo.retry === 0 ? { current: "warn", initial: "warn" } : { current: "debug", initial: "debug" };
|
|
3320
|
-
const log = new PlaywrightReportLogger(sharedLogLevel, sharedLogEntry, contextName);
|
|
3321
|
-
await use(log);
|
|
3322
|
-
log.attachLogsToTest(testInfo);
|
|
3323
|
-
}
|
|
3324
|
-
});
|
|
3325
|
-
|
|
3326
|
-
// srcV2/helpers/stepDecorator.ts
|
|
3327
|
-
import { test as test6 } from "@playwright/test";
|
|
3328
|
-
var isMethodDecoratorArgs = (args) => args.length === 3 && typeof args[0] === "object" && (typeof args[1] === "string" || typeof args[1] === "symbol");
|
|
3329
|
-
var isStage3MethodDecoratorArgs = (args) => args.length === 2 && typeof args[0] === "function" && args[1] !== null && typeof args[1] === "object";
|
|
3330
|
-
var normalizeStepArguments = (args) => {
|
|
3331
|
-
const [titleOrOptions, maybeOptions] = args;
|
|
3332
|
-
const title = typeof titleOrOptions === "string" ? titleOrOptions : void 0;
|
|
3333
|
-
const options = typeof titleOrOptions === "string" ? maybeOptions : titleOrOptions;
|
|
3334
|
-
return { title, options };
|
|
3335
|
-
};
|
|
3336
|
-
var createWrappedMethod = (original, methodName, title, options) => function(...methodArgs) {
|
|
3337
|
-
const rawClassName = this.constructor?.name ?? "";
|
|
3338
|
-
const className = rawClassName && rawClassName !== "Object" ? rawClassName : "Anonymous";
|
|
3339
|
-
const resolvedTitle = title ?? `${className}.${String(methodName)}`;
|
|
3340
|
-
return test6.step(resolvedTitle, () => original.apply(this, methodArgs), options);
|
|
3341
|
-
};
|
|
3342
|
-
var createStepDecorator = ({ title, options }) => (valueOrTarget, contextOrKey, descriptor) => {
|
|
3343
|
-
if (typeof valueOrTarget === "function" && isStage3MethodDecoratorArgs([valueOrTarget, contextOrKey])) {
|
|
3344
|
-
const [original2, context] = [valueOrTarget, contextOrKey];
|
|
3345
|
-
return createWrappedMethod(original2, context.name, title, options);
|
|
3346
|
-
}
|
|
3347
|
-
if (!descriptor || typeof descriptor.value !== "function") {
|
|
3348
|
-
throw new Error("@step decorator can only be applied to methods.");
|
|
3349
|
-
}
|
|
3350
|
-
const original = descriptor.value;
|
|
3351
|
-
descriptor.value = createWrappedMethod(original, contextOrKey, title, options);
|
|
3352
|
-
return descriptor;
|
|
3353
|
-
};
|
|
3354
|
-
function step(...args) {
|
|
3355
|
-
if (isStage3MethodDecoratorArgs(args)) {
|
|
3356
|
-
return createStepDecorator(normalizeStepArguments([]))(...args);
|
|
3357
|
-
}
|
|
3358
|
-
if (isMethodDecoratorArgs(args)) {
|
|
3359
|
-
return createStepDecorator(normalizeStepArguments([]))(...args);
|
|
3360
|
-
}
|
|
3361
|
-
return createStepDecorator(normalizeStepArguments(args));
|
|
3362
|
-
}
|
|
3363
2113
|
export {
|
|
3364
|
-
BaseApi,
|
|
3365
|
-
BasePage,
|
|
3366
|
-
BasePageV1toV2,
|
|
3367
|
-
GetByMethod,
|
|
3368
|
-
GetLocatorBase,
|
|
3369
2114
|
PageObject,
|
|
3370
2115
|
PlaywrightReportLogger,
|
|
3371
|
-
|
|
2116
|
+
SessionStorage,
|
|
3372
2117
|
createRegistryWithAccessors,
|
|
3373
2118
|
step,
|
|
3374
|
-
|
|
2119
|
+
test
|
|
3375
2120
|
};
|