@immense/vue-pom-generator 1.0.10 → 1.0.11
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/RELEASE_NOTES.md +16 -18
- package/class-generation/BasePage.ts +13 -2
- package/class-generation/index.ts +56 -134
- package/click-instrumentation.ts +14 -0
- package/dist/index.cjs +61 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +61 -95
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
● ## Highlights
|
|
2
|
-
-
|
|
3
|
-
-
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
- Updates across class-generation and plugin modules.
|
|
2
|
+
- TestID click instrumentation is now optional by default.
|
|
3
|
+
- Fixed packaged runtime type path resolution.
|
|
4
|
+
- Aggregate mode now emits runtime dependencies.
|
|
5
|
+
- Updates across BasePage and method generation to support new behavior.
|
|
7
6
|
|
|
8
7
|
## Changes
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- Update package.json
|
|
8
|
+
- Instrumentation
|
|
9
|
+
- Make TestID click instrumentation optional by default (method-generation.ts updates).
|
|
10
|
+
- Runtime packaging and dependencies
|
|
11
|
+
- Fix resolution of packaged runtime type paths.
|
|
12
|
+
- Emit runtime dependencies in aggregate mode.
|
|
13
|
+
- Code updates
|
|
14
|
+
- Modified class-generation/BasePage.ts and class-generation/index.ts.
|
|
15
|
+
- package.json updated.
|
|
16
|
+
|
|
17
|
+
## Breaking Changes
|
|
18
|
+
- None noted.
|
|
21
19
|
|
|
22
20
|
## Pull Requests Included
|
|
23
21
|
- #1 Add PR release-notes preview comments — https://github.com/immense/vue-pom-generator/pull/1
|
|
24
22
|
(by @dkattan)
|
|
25
23
|
|
|
26
24
|
## Testing
|
|
27
|
-
-
|
|
25
|
+
- No testing changes or additions mentioned.
|
|
28
26
|
|
|
@@ -4,8 +4,15 @@ import type { TestIdClickEventDetail } from "../click-instrumentation";
|
|
|
4
4
|
import { Pointer } from "./Pointer";
|
|
5
5
|
import type { AfterPointerClick, AfterPointerClickInfo } from "./Pointer";
|
|
6
6
|
|
|
7
|
-
// Click instrumentation is
|
|
8
|
-
|
|
7
|
+
// Click instrumentation is optional for generated POMs.
|
|
8
|
+
//
|
|
9
|
+
// When enabled, POM click/fill helpers will wait for the app to emit
|
|
10
|
+
// `__testid_event__` { testId, phase: "after" } after interacting with an
|
|
11
|
+
// element that has a data-testid.
|
|
12
|
+
//
|
|
13
|
+
// Default: disabled. (Playwright already has robust auto-waiting; requiring a
|
|
14
|
+
// custom event makes tests depend on app runtime instrumentation.)
|
|
15
|
+
const REQUIRE_CLICK_EVENT = false;
|
|
9
16
|
|
|
10
17
|
// Keep logging off by default.
|
|
11
18
|
const CLICK_EVENT_DEBUG = false;
|
|
@@ -97,6 +104,10 @@ export class BasePage {
|
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
private async waitForTestIdClickEventAfter(testId: string, options?: { timeoutMs?: number }): Promise<void> {
|
|
107
|
+
if (!REQUIRE_CLICK_EVENT) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
100
111
|
const timeoutMs = options?.timeoutMs ?? 2_000;
|
|
101
112
|
const requireEvent = REQUIRE_CLICK_EVENT;
|
|
102
113
|
|
|
@@ -1163,133 +1163,21 @@ async function generateAggregatedFiles(
|
|
|
1163
1163
|
throw new Error("basePageClassPath is required for aggregated generation");
|
|
1164
1164
|
}
|
|
1165
1165
|
|
|
1166
|
-
//
|
|
1166
|
+
// Aggregate mode goal: consolidate all generated POM classes into one file.
|
|
1167
|
+
// Instead of inlining BasePage/Pointer helpers and stripping imports via regex, we
|
|
1168
|
+
// emit/copy those dependencies into the output folder and import them normally.
|
|
1167
1169
|
//
|
|
1168
|
-
//
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
" testId?: string;",
|
|
1180
|
-
" phase?: \"before\" | \"after\" | \"error\" | string;",
|
|
1181
|
-
" err?: string;",
|
|
1182
|
-
"}",
|
|
1183
|
-
].join("\n");
|
|
1184
|
-
|
|
1185
|
-
const inlinePlaywrightTypesModule = () => {
|
|
1186
|
-
const typesPath = fileURLToPath(new URL("./playwright-types.ts", import.meta.url));
|
|
1187
|
-
|
|
1188
|
-
let typesSource = "";
|
|
1189
|
-
try {
|
|
1190
|
-
typesSource = fs.readFileSync(typesPath, "utf8");
|
|
1191
|
-
}
|
|
1192
|
-
catch {
|
|
1193
|
-
throw new Error(`Failed to read playwright-types.ts at ${typesPath}`);
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
// Strip the import from the inlined types.
|
|
1197
|
-
typesSource = typesSource.replace(
|
|
1198
|
-
/import\s+type\s*\{\s*Locator\s+as\s+PwLocator\s*,\s*Page\s+as\s+PwPage\s*\}\s*from\s*["']@playwright\/test["'];?\s*/,
|
|
1199
|
-
"",
|
|
1200
|
-
);
|
|
1201
|
-
|
|
1202
|
-
return typesSource.trim();
|
|
1203
|
-
};
|
|
1204
|
-
|
|
1205
|
-
const inlinePointerModule = () => {
|
|
1206
|
-
// Inline Pointer.ts from this package so generated POMs are self-contained and do not
|
|
1207
|
-
// rely on runtime TS module resolution within workspace packages.
|
|
1208
|
-
const pointerPath = fileURLToPath(new URL("./Pointer.ts", import.meta.url));
|
|
1209
|
-
|
|
1210
|
-
let pointerSource = "";
|
|
1211
|
-
try {
|
|
1212
|
-
pointerSource = fs.readFileSync(pointerPath, "utf8");
|
|
1213
|
-
}
|
|
1214
|
-
catch {
|
|
1215
|
-
throw new Error(`Failed to read Pointer.ts at ${pointerPath}`);
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
// Replace the click-instrumentation import with an inline copy.
|
|
1219
|
-
pointerSource = pointerSource.replace(
|
|
1220
|
-
/import\s*\{[\s\S]*?\}\s*from\s*["']\.\.\/click-instrumentation["'];?\s*/,
|
|
1221
|
-
`${clickInstrumentationInline}\n\n`,
|
|
1222
|
-
);
|
|
1223
|
-
|
|
1224
|
-
// If Pointer uses a split value import + type-only import, remove the type-only import too.
|
|
1225
|
-
// The inline block already declares TestIdClickEventDetail.
|
|
1226
|
-
pointerSource = pointerSource.replace(
|
|
1227
|
-
/import\s+type\s*\{\s*TestIdClickEventDetail\s*\}\s*from\s*["']\.\.\/click-instrumentation["'];?\s*/g,
|
|
1228
|
-
"",
|
|
1229
|
-
);
|
|
1230
|
-
|
|
1231
|
-
// The aggregated file already imports these Playwright types once at the top.
|
|
1232
|
-
pointerSource = pointerSource.replace(
|
|
1233
|
-
/import\s+type\s*\{\s*Locator\s+as\s+PwLocator\s*,\s*Page\s+as\s+PwPage\s*\}\s*from\s*["']@playwright\/test["'];?\s*/,
|
|
1234
|
-
"",
|
|
1235
|
-
);
|
|
1236
|
-
|
|
1237
|
-
// The aggregated file inlines these structural types once at the top.
|
|
1238
|
-
pointerSource = pointerSource.replace(
|
|
1239
|
-
/import\s+type\s*\{\s*PwLocator\s*,\s*PwPage\s*\}\s*from\s*["']\.\/playwright-types["'];?\s*/,
|
|
1240
|
-
"",
|
|
1241
|
-
);
|
|
1242
|
-
|
|
1243
|
-
return pointerSource.trim();
|
|
1244
|
-
};
|
|
1245
|
-
|
|
1246
|
-
const inlineBasePageModule = () => {
|
|
1247
|
-
let basePageSource = "";
|
|
1248
|
-
try {
|
|
1249
|
-
basePageSource = fs.readFileSync(basePageClassPath, "utf8");
|
|
1250
|
-
}
|
|
1251
|
-
catch {
|
|
1252
|
-
throw new Error(`Failed to read BasePage.ts at ${basePageClassPath}`);
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
// Replace the click-instrumentation import with an inline copy.
|
|
1256
|
-
basePageSource = basePageSource.replace(
|
|
1257
|
-
/import\s*\{[\s\S]*?\}\s*from\s*["']\.\.\/click-instrumentation["'];?\s*/,
|
|
1258
|
-
`${clickInstrumentationInline}\n\n`,
|
|
1259
|
-
);
|
|
1260
|
-
|
|
1261
|
-
// If BasePage uses a split value import + type-only import, remove the type-only import too.
|
|
1262
|
-
// The inline block already declares TestIdClickEventDetail.
|
|
1263
|
-
basePageSource = basePageSource.replace(
|
|
1264
|
-
/import\s+type\s*\{\s*TestIdClickEventDetail\s*\}\s*from\s*["']\.\.\/click-instrumentation["'];?\s*/g,
|
|
1265
|
-
"",
|
|
1266
|
-
);
|
|
1267
|
-
|
|
1268
|
-
// The aggregated file already imports these Playwright types once at the top.
|
|
1269
|
-
// Remove BasePage's own import to avoid duplicate identifiers.
|
|
1270
|
-
basePageSource = basePageSource.replace(
|
|
1271
|
-
/import\s+type\s*\{\s*Locator\s+as\s+PwLocator\s*,\s*Page\s+as\s+PwPage\s*\}\s*from\s*["']@playwright\/test["'];?\s*/,
|
|
1272
|
-
"",
|
|
1273
|
-
);
|
|
1274
|
-
|
|
1275
|
-
// The aggregated file inlines these structural types once at the top.
|
|
1276
|
-
basePageSource = basePageSource.replace(
|
|
1277
|
-
/import\s+type\s*\{\s*PwLocator\s*,\s*PwPage\s*\}\s*from\s*["']\.\/playwright-types["'];?\s*/,
|
|
1278
|
-
"",
|
|
1279
|
-
);
|
|
1280
|
-
|
|
1281
|
-
// BasePage references Pointer, but in aggregated output we inline Pointer above.
|
|
1282
|
-
basePageSource = basePageSource.replace(
|
|
1283
|
-
/import\s+(?:type\s*)?\{[\s\S]*?\}\s*from\s*["']\.\/Pointer["'];?\s*/g,
|
|
1284
|
-
"",
|
|
1285
|
-
);
|
|
1286
|
-
|
|
1287
|
-
return basePageSource.trim();
|
|
1288
|
-
};
|
|
1289
|
-
|
|
1290
|
-
const playwrightTypesInline = inlinePlaywrightTypesModule();
|
|
1291
|
-
const pointerInline = inlinePointerModule();
|
|
1292
|
-
const basePageInline = inlineBasePageModule();
|
|
1170
|
+
// This keeps output deterministic and avoids fragile source rewriting.
|
|
1171
|
+
const runtimeDirRel = "./_pom-runtime";
|
|
1172
|
+
const runtimeClassGenRel = `${runtimeDirRel}/class-generation`;
|
|
1173
|
+
|
|
1174
|
+
imports.push(`import type { PwLocator, PwPage } from "${runtimeClassGenRel}/playwright-types";`);
|
|
1175
|
+
imports.push(`import { BasePage } from "${runtimeClassGenRel}/BasePage";`);
|
|
1176
|
+
imports.push(`import type { Fluent } from "${runtimeClassGenRel}/BasePage";`);
|
|
1177
|
+
imports.push(`export * from "${runtimeDirRel}/click-instrumentation";`);
|
|
1178
|
+
imports.push(`export * from "${runtimeClassGenRel}/playwright-types";`);
|
|
1179
|
+
imports.push(`export * from "${runtimeClassGenRel}/Pointer";`);
|
|
1180
|
+
imports.push(`export * from "${runtimeClassGenRel}/BasePage";`);
|
|
1293
1181
|
|
|
1294
1182
|
// Handwritten POM helpers for complicated/third-party widgets.
|
|
1295
1183
|
// Convention: place them in `tests/playwright/pom/custom/*.ts`.
|
|
@@ -1545,13 +1433,6 @@ async function generateAggregatedFiles(
|
|
|
1545
1433
|
const baseContent = [
|
|
1546
1434
|
header,
|
|
1547
1435
|
...imports,
|
|
1548
|
-
"",
|
|
1549
|
-
playwrightTypesInline,
|
|
1550
|
-
"",
|
|
1551
|
-
pointerInline,
|
|
1552
|
-
"",
|
|
1553
|
-
basePageInline,
|
|
1554
|
-
"",
|
|
1555
1436
|
...classes,
|
|
1556
1437
|
...(stubs.length ? ["", ...stubs] : []),
|
|
1557
1438
|
].filter(Boolean).join("\n\n");
|
|
@@ -1561,15 +1442,56 @@ async function generateAggregatedFiles(
|
|
|
1561
1442
|
|
|
1562
1443
|
const base = ensureDir(outDir);
|
|
1563
1444
|
const outputFile = path.join(base, "page-object-models.g.ts");
|
|
1564
|
-
const header = `/// <reference lib="es2015" />\n${eslintSuppressionHeader}
|
|
1445
|
+
const header = `/// <reference lib="es2015" />\n${eslintSuppressionHeader}/**\n * Aggregated generated POMs\n${AUTO_GENERATED_COMMENT}`;
|
|
1565
1446
|
const content = makeAggregatedContent(header, path.dirname(outputFile), [...views, ...components]);
|
|
1566
1447
|
|
|
1567
1448
|
const indexFile = path.join(base, "index.ts");
|
|
1568
1449
|
const indexContent = `${eslintSuppressionHeader}/**\n * POM exports\n${AUTO_GENERATED_COMMENT}\n\nexport * from "./page-object-models.g";\n`;
|
|
1569
1450
|
|
|
1451
|
+
const runtimeDirAbs = path.join(base, "_pom-runtime");
|
|
1452
|
+
const runtimeClassGenAbs = path.join(runtimeDirAbs, "class-generation");
|
|
1453
|
+
|
|
1454
|
+
const readText = (absPath: string, description: string) => {
|
|
1455
|
+
try {
|
|
1456
|
+
return fs.readFileSync(absPath, "utf8");
|
|
1457
|
+
}
|
|
1458
|
+
catch {
|
|
1459
|
+
throw new Error(`Failed to read ${description} at ${absPath}`);
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1463
|
+
// Copy runtime dependencies into the output folder so the aggregated POM file can
|
|
1464
|
+
// import them without relying on workspace package resolution.
|
|
1465
|
+
const clickInstrumentationAbs = fileURLToPath(new URL("../click-instrumentation.ts", import.meta.url));
|
|
1466
|
+
// These runtime .ts files must resolve correctly both when running from source
|
|
1467
|
+
// (e.g. during development/tests) and when bundled into dist/index.* for npm.
|
|
1468
|
+
// Resolving via package-root `class-generation/*` is stable across both.
|
|
1469
|
+
const pointerAbs = fileURLToPath(new URL("../class-generation/Pointer.ts", import.meta.url));
|
|
1470
|
+
const playwrightTypesAbs = fileURLToPath(new URL("../class-generation/playwright-types.ts", import.meta.url));
|
|
1471
|
+
|
|
1472
|
+
const runtimeFiles: Array<{ filePath: string; content: string }> = [
|
|
1473
|
+
{
|
|
1474
|
+
filePath: path.join(runtimeDirAbs, "click-instrumentation.ts"),
|
|
1475
|
+
content: readText(clickInstrumentationAbs, "click-instrumentation.ts"),
|
|
1476
|
+
},
|
|
1477
|
+
{
|
|
1478
|
+
filePath: path.join(runtimeClassGenAbs, "Pointer.ts"),
|
|
1479
|
+
content: readText(pointerAbs, "Pointer.ts"),
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
filePath: path.join(runtimeClassGenAbs, "playwright-types.ts"),
|
|
1483
|
+
content: readText(playwrightTypesAbs, "playwright-types.ts"),
|
|
1484
|
+
},
|
|
1485
|
+
{
|
|
1486
|
+
filePath: path.join(runtimeClassGenAbs, "BasePage.ts"),
|
|
1487
|
+
content: readText(basePageClassPath, "BasePage.ts"),
|
|
1488
|
+
},
|
|
1489
|
+
];
|
|
1490
|
+
|
|
1570
1491
|
return [
|
|
1571
1492
|
{ filePath: outputFile, content },
|
|
1572
1493
|
{ filePath: indexFile, content: indexContent },
|
|
1494
|
+
...runtimeFiles,
|
|
1573
1495
|
];
|
|
1574
1496
|
}
|
|
1575
1497
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Shared click-instrumentation contract between the Vue template transform and
|
|
2
|
+
// the generated Playwright Page Object Model runtime.
|
|
3
|
+
|
|
4
|
+
export const TESTID_CLICK_EVENT_NAME = "__testid_event__";
|
|
5
|
+
|
|
6
|
+
// When strict mode is enabled, the injected click wrapper will fail fast if it
|
|
7
|
+
// cannot emit the expected event.
|
|
8
|
+
export const TESTID_CLICK_EVENT_STRICT_FLAG = "__testid_click_event_strict__";
|
|
9
|
+
|
|
10
|
+
export interface TestIdClickEventDetail {
|
|
11
|
+
testId?: string;
|
|
12
|
+
phase?: "before" | "after" | "error" | string;
|
|
13
|
+
err?: string;
|
|
14
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -151,8 +151,10 @@ function testIdExpression(formattedDataTestId) {
|
|
|
151
151
|
function generateClickMethod(methodName, formattedDataTestId, alternateFormattedDataTestIds, params) {
|
|
152
152
|
let content;
|
|
153
153
|
const name = `click${methodName}`;
|
|
154
|
+
const noWaitName = `${name}NoWait`;
|
|
154
155
|
const paramBlock = formatParams(params);
|
|
155
156
|
const paramBlockWithWait = paramBlock ? `${paramBlock}, wait: boolean = true` : "wait: boolean = true";
|
|
157
|
+
const argsForForward = Object.keys(params).join(", ");
|
|
156
158
|
const alternates = uniqueAlternates(formattedDataTestId, alternateFormattedDataTestIds);
|
|
157
159
|
if (alternates.length > 0) {
|
|
158
160
|
const candidatesExpr = [formattedDataTestId, ...alternates].map(testIdExpression).join(", ");
|
|
@@ -174,6 +176,13 @@ ${INDENT3}}
|
|
|
174
176
|
${INDENT2}}
|
|
175
177
|
${INDENT2}throw (lastError instanceof Error) ? lastError : new Error("[pom] Failed to click any candidate locator for ${name}.");
|
|
176
178
|
${INDENT}}
|
|
179
|
+
`;
|
|
180
|
+
const noWaitSig = hasParam(params, "key") ? paramBlock : "";
|
|
181
|
+
const noWaitArgs = argsForForward ? `${argsForForward}, false` : "false";
|
|
182
|
+
content += `
|
|
183
|
+
${INDENT}async ${noWaitName}(${noWaitSig}) {
|
|
184
|
+
${INDENT2}await this.${name}(${noWaitArgs});
|
|
185
|
+
${INDENT}}
|
|
177
186
|
`;
|
|
178
187
|
return content;
|
|
179
188
|
}
|
|
@@ -181,11 +190,21 @@ ${INDENT}}
|
|
|
181
190
|
content = `${INDENT}async ${name}(${paramBlockWithWait}) {
|
|
182
191
|
${INDENT2}await this.clickByTestId(\`${formattedDataTestId}\`, "", wait);
|
|
183
192
|
${INDENT}}
|
|
193
|
+
`;
|
|
194
|
+
content += `
|
|
195
|
+
${INDENT}async ${noWaitName}(${paramBlock}) {
|
|
196
|
+
${INDENT2}await this.${name}(${argsForForward}, false);
|
|
197
|
+
${INDENT}}
|
|
184
198
|
`;
|
|
185
199
|
} else {
|
|
186
200
|
content = `${INDENT}async ${name}(wait: boolean = true) {
|
|
187
201
|
${INDENT2}await this.clickByTestId("${formattedDataTestId}", "", wait);
|
|
188
202
|
${INDENT}}
|
|
203
|
+
`;
|
|
204
|
+
content += `
|
|
205
|
+
${INDENT}async ${noWaitName}() {
|
|
206
|
+
${INDENT2}await this.${name}(false);
|
|
207
|
+
${INDENT}}
|
|
189
208
|
`;
|
|
190
209
|
}
|
|
191
210
|
return content;
|
|
@@ -3445,91 +3464,15 @@ async function generateAggregatedFiles(componentHierarchyMap, vueFilesPathMap, b
|
|
|
3445
3464
|
if (!basePageClassPath) {
|
|
3446
3465
|
throw new Error("basePageClassPath is required for aggregated generation");
|
|
3447
3466
|
}
|
|
3448
|
-
const
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
const inlinePlaywrightTypesModule = () => {
|
|
3458
|
-
const typesPath = node_url.fileURLToPath(new URL("./playwright-types.ts", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href));
|
|
3459
|
-
let typesSource = "";
|
|
3460
|
-
try {
|
|
3461
|
-
typesSource = fs.readFileSync(typesPath, "utf8");
|
|
3462
|
-
} catch {
|
|
3463
|
-
throw new Error(`Failed to read playwright-types.ts at ${typesPath}`);
|
|
3464
|
-
}
|
|
3465
|
-
typesSource = typesSource.replace(
|
|
3466
|
-
/import\s+type\s*\{\s*Locator\s+as\s+PwLocator\s*,\s*Page\s+as\s+PwPage\s*\}\s*from\s*["']@playwright\/test["'];?\s*/,
|
|
3467
|
-
""
|
|
3468
|
-
);
|
|
3469
|
-
return typesSource.trim();
|
|
3470
|
-
};
|
|
3471
|
-
const inlinePointerModule = () => {
|
|
3472
|
-
const pointerPath = node_url.fileURLToPath(new URL("./Pointer.ts", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href));
|
|
3473
|
-
let pointerSource = "";
|
|
3474
|
-
try {
|
|
3475
|
-
pointerSource = fs.readFileSync(pointerPath, "utf8");
|
|
3476
|
-
} catch {
|
|
3477
|
-
throw new Error(`Failed to read Pointer.ts at ${pointerPath}`);
|
|
3478
|
-
}
|
|
3479
|
-
pointerSource = pointerSource.replace(
|
|
3480
|
-
/import\s*\{[\s\S]*?\}\s*from\s*["']\.\.\/click-instrumentation["'];?\s*/,
|
|
3481
|
-
`${clickInstrumentationInline}
|
|
3482
|
-
|
|
3483
|
-
`
|
|
3484
|
-
);
|
|
3485
|
-
pointerSource = pointerSource.replace(
|
|
3486
|
-
/import\s+type\s*\{\s*TestIdClickEventDetail\s*\}\s*from\s*["']\.\.\/click-instrumentation["'];?\s*/g,
|
|
3487
|
-
""
|
|
3488
|
-
);
|
|
3489
|
-
pointerSource = pointerSource.replace(
|
|
3490
|
-
/import\s+type\s*\{\s*Locator\s+as\s+PwLocator\s*,\s*Page\s+as\s+PwPage\s*\}\s*from\s*["']@playwright\/test["'];?\s*/,
|
|
3491
|
-
""
|
|
3492
|
-
);
|
|
3493
|
-
pointerSource = pointerSource.replace(
|
|
3494
|
-
/import\s+type\s*\{\s*PwLocator\s*,\s*PwPage\s*\}\s*from\s*["']\.\/playwright-types["'];?\s*/,
|
|
3495
|
-
""
|
|
3496
|
-
);
|
|
3497
|
-
return pointerSource.trim();
|
|
3498
|
-
};
|
|
3499
|
-
const inlineBasePageModule = () => {
|
|
3500
|
-
let basePageSource = "";
|
|
3501
|
-
try {
|
|
3502
|
-
basePageSource = fs.readFileSync(basePageClassPath, "utf8");
|
|
3503
|
-
} catch {
|
|
3504
|
-
throw new Error(`Failed to read BasePage.ts at ${basePageClassPath}`);
|
|
3505
|
-
}
|
|
3506
|
-
basePageSource = basePageSource.replace(
|
|
3507
|
-
/import\s*\{[\s\S]*?\}\s*from\s*["']\.\.\/click-instrumentation["'];?\s*/,
|
|
3508
|
-
`${clickInstrumentationInline}
|
|
3509
|
-
|
|
3510
|
-
`
|
|
3511
|
-
);
|
|
3512
|
-
basePageSource = basePageSource.replace(
|
|
3513
|
-
/import\s+type\s*\{\s*TestIdClickEventDetail\s*\}\s*from\s*["']\.\.\/click-instrumentation["'];?\s*/g,
|
|
3514
|
-
""
|
|
3515
|
-
);
|
|
3516
|
-
basePageSource = basePageSource.replace(
|
|
3517
|
-
/import\s+type\s*\{\s*Locator\s+as\s+PwLocator\s*,\s*Page\s+as\s+PwPage\s*\}\s*from\s*["']@playwright\/test["'];?\s*/,
|
|
3518
|
-
""
|
|
3519
|
-
);
|
|
3520
|
-
basePageSource = basePageSource.replace(
|
|
3521
|
-
/import\s+type\s*\{\s*PwLocator\s*,\s*PwPage\s*\}\s*from\s*["']\.\/playwright-types["'];?\s*/,
|
|
3522
|
-
""
|
|
3523
|
-
);
|
|
3524
|
-
basePageSource = basePageSource.replace(
|
|
3525
|
-
/import\s+(?:type\s*)?\{[\s\S]*?\}\s*from\s*["']\.\/Pointer["'];?\s*/g,
|
|
3526
|
-
""
|
|
3527
|
-
);
|
|
3528
|
-
return basePageSource.trim();
|
|
3529
|
-
};
|
|
3530
|
-
const playwrightTypesInline = inlinePlaywrightTypesModule();
|
|
3531
|
-
const pointerInline = inlinePointerModule();
|
|
3532
|
-
const basePageInline = inlineBasePageModule();
|
|
3467
|
+
const runtimeDirRel = "./_pom-runtime";
|
|
3468
|
+
const runtimeClassGenRel = `${runtimeDirRel}/class-generation`;
|
|
3469
|
+
imports.push(`import type { PwLocator, PwPage } from "${runtimeClassGenRel}/playwright-types";`);
|
|
3470
|
+
imports.push(`import { BasePage } from "${runtimeClassGenRel}/BasePage";`);
|
|
3471
|
+
imports.push(`import type { Fluent } from "${runtimeClassGenRel}/BasePage";`);
|
|
3472
|
+
imports.push(`export * from "${runtimeDirRel}/click-instrumentation";`);
|
|
3473
|
+
imports.push(`export * from "${runtimeClassGenRel}/playwright-types";`);
|
|
3474
|
+
imports.push(`export * from "${runtimeClassGenRel}/Pointer";`);
|
|
3475
|
+
imports.push(`export * from "${runtimeClassGenRel}/BasePage";`);
|
|
3533
3476
|
const addCustomPomImports = () => {
|
|
3534
3477
|
const importAliases = {
|
|
3535
3478
|
Toggle: "ToggleWidget",
|
|
@@ -3716,13 +3659,6 @@ async function generateAggregatedFiles(componentHierarchyMap, vueFilesPathMap, b
|
|
|
3716
3659
|
const baseContent = [
|
|
3717
3660
|
header2,
|
|
3718
3661
|
...imports,
|
|
3719
|
-
"",
|
|
3720
|
-
playwrightTypesInline,
|
|
3721
|
-
"",
|
|
3722
|
-
pointerInline,
|
|
3723
|
-
"",
|
|
3724
|
-
basePageInline,
|
|
3725
|
-
"",
|
|
3726
3662
|
...classes,
|
|
3727
3663
|
...stubs.length ? ["", ...stubs] : []
|
|
3728
3664
|
].filter(Boolean).join("\n\n");
|
|
@@ -3731,8 +3667,7 @@ async function generateAggregatedFiles(componentHierarchyMap, vueFilesPathMap, b
|
|
|
3731
3667
|
const base = ensureDir(outDir);
|
|
3732
3668
|
const outputFile = path.join(base, "page-object-models.g.ts");
|
|
3733
3669
|
const header = `/// <reference lib="es2015" />
|
|
3734
|
-
${eslintSuppressionHeader}
|
|
3735
|
-
/**
|
|
3670
|
+
${eslintSuppressionHeader}/**
|
|
3736
3671
|
* Aggregated generated POMs
|
|
3737
3672
|
${AUTO_GENERATED_COMMENT}`;
|
|
3738
3673
|
const content = makeAggregatedContent(header, path.dirname(outputFile), [...views, ...components]);
|
|
@@ -3743,9 +3678,40 @@ ${AUTO_GENERATED_COMMENT}
|
|
|
3743
3678
|
|
|
3744
3679
|
export * from "./page-object-models.g";
|
|
3745
3680
|
`;
|
|
3681
|
+
const runtimeDirAbs = path.join(base, "_pom-runtime");
|
|
3682
|
+
const runtimeClassGenAbs = path.join(runtimeDirAbs, "class-generation");
|
|
3683
|
+
const readText = (absPath, description) => {
|
|
3684
|
+
try {
|
|
3685
|
+
return fs.readFileSync(absPath, "utf8");
|
|
3686
|
+
} catch {
|
|
3687
|
+
throw new Error(`Failed to read ${description} at ${absPath}`);
|
|
3688
|
+
}
|
|
3689
|
+
};
|
|
3690
|
+
const clickInstrumentationAbs = node_url.fileURLToPath(new URL("../click-instrumentation.ts", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href));
|
|
3691
|
+
const pointerAbs = node_url.fileURLToPath(new URL("../class-generation/Pointer.ts", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href));
|
|
3692
|
+
const playwrightTypesAbs = node_url.fileURLToPath(new URL("../class-generation/playwright-types.ts", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href));
|
|
3693
|
+
const runtimeFiles = [
|
|
3694
|
+
{
|
|
3695
|
+
filePath: path.join(runtimeDirAbs, "click-instrumentation.ts"),
|
|
3696
|
+
content: readText(clickInstrumentationAbs, "click-instrumentation.ts")
|
|
3697
|
+
},
|
|
3698
|
+
{
|
|
3699
|
+
filePath: path.join(runtimeClassGenAbs, "Pointer.ts"),
|
|
3700
|
+
content: readText(pointerAbs, "Pointer.ts")
|
|
3701
|
+
},
|
|
3702
|
+
{
|
|
3703
|
+
filePath: path.join(runtimeClassGenAbs, "playwright-types.ts"),
|
|
3704
|
+
content: readText(playwrightTypesAbs, "playwright-types.ts")
|
|
3705
|
+
},
|
|
3706
|
+
{
|
|
3707
|
+
filePath: path.join(runtimeClassGenAbs, "BasePage.ts"),
|
|
3708
|
+
content: readText(basePageClassPath, "BasePage.ts")
|
|
3709
|
+
}
|
|
3710
|
+
];
|
|
3746
3711
|
return [
|
|
3747
3712
|
{ filePath: outputFile, content },
|
|
3748
|
-
{ filePath: indexFile, content: indexContent }
|
|
3713
|
+
{ filePath: indexFile, content: indexContent },
|
|
3714
|
+
...runtimeFiles
|
|
3749
3715
|
];
|
|
3750
3716
|
}
|
|
3751
3717
|
function createFile(filePath, content) {
|