@immense/vue-pom-generator 1.0.7 → 1.0.8
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/README.md +85 -71
- package/RELEASE_NOTES.md +30 -13
- package/class-generation/BasePage.ts +17 -2
- package/class-generation/Pointer.ts +1 -1
- package/class-generation/index.ts +77 -24
- package/class-generation/playwright-types.ts +23 -0
- package/dist/index.cjs +583 -208
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +584 -209
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -47,69 +47,87 @@ import vue from "@vitejs/plugin-vue";
|
|
|
47
47
|
import { createVuePomGeneratorPlugins } from "@immense/vue-pom-generator";
|
|
48
48
|
|
|
49
49
|
export default defineConfig(() => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
50
|
+
const vueOptions = {
|
|
51
|
+
script: { defineModel: true, propsDestructure: true },
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
plugins: [
|
|
56
|
+
...createVuePomGeneratorPlugins({
|
|
57
|
+
vueOptions,
|
|
58
|
+
logging: { verbosity: "info" },
|
|
59
|
+
|
|
60
|
+
injection: {
|
|
61
|
+
// Attribute to inject/read as the test id (default: data-testid)
|
|
62
|
+
attribute: "data-testid",
|
|
63
|
+
|
|
64
|
+
// Used to classify Vue files as "views" vs components (default: src/views)
|
|
65
|
+
viewsDir: "src/views",
|
|
66
|
+
|
|
67
|
+
// Directories to scan for .vue files when building the POM library (default: ["src"])
|
|
68
|
+
// For Nuxt, you might want ["app", "components", "pages", "layouts"]
|
|
69
|
+
scanDirs: ["src"],
|
|
70
|
+
|
|
71
|
+
// Optional: wrapper semantics for design-system components
|
|
72
|
+
nativeWrappers: {
|
|
73
|
+
MyButton: { role: "button" },
|
|
74
|
+
MyInput: { role: "input" },
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// Optional: opt specific components out of injection
|
|
78
|
+
excludeComponents: ["MyButton"],
|
|
79
|
+
|
|
80
|
+
// Optional: preserve/overwrite/error when an author already set the attribute
|
|
81
|
+
existingIdBehavior: "preserve",
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
generation: {
|
|
85
|
+
// Default: ["ts"]
|
|
86
|
+
emit: ["ts", "csharp"],
|
|
87
|
+
|
|
88
|
+
// C# specific configuration
|
|
89
|
+
csharp: {
|
|
90
|
+
// The namespace for generated C# classes (default: Playwright.Generated)
|
|
91
|
+
namespace: "MyProject.Tests.Generated",
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// Default: tests/playwright/generated
|
|
95
|
+
outDir: "tests/playwright/generated",
|
|
96
|
+
|
|
97
|
+
// Controls how to handle duplicate generated member names within a single POM class.
|
|
98
|
+
// - "error": fail compilation
|
|
99
|
+
// - "warn": warn and suffix
|
|
100
|
+
// - "suffix": suffix silently (default)
|
|
101
|
+
nameCollisionBehavior: "suffix",
|
|
102
|
+
|
|
103
|
+
// Enable router introspection. When provided, router-aware POM helpers are generated.
|
|
104
|
+
router: {
|
|
105
|
+
// For standard Vue apps:
|
|
106
|
+
entry: "src/router.ts",
|
|
107
|
+
// For Nuxt apps (file-based routing):
|
|
108
|
+
// type: "nuxt"
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
playwright: {
|
|
112
|
+
fixtures: true,
|
|
113
|
+
customPoms: {
|
|
114
|
+
// Default: tests/playwright/pom/custom
|
|
115
|
+
dir: "tests/playwright/pom/custom",
|
|
116
|
+
importAliases: { MyCheckBox: "CheckboxWidget" },
|
|
117
|
+
attachments: [
|
|
118
|
+
{
|
|
119
|
+
className: "ConfirmationModal",
|
|
120
|
+
propertyName: "confirmationModal",
|
|
121
|
+
attachWhenUsesComponents: ["Page"],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
vue(vueOptions),
|
|
129
|
+
],
|
|
130
|
+
};
|
|
113
131
|
});
|
|
114
132
|
```
|
|
115
133
|
|
|
@@ -119,16 +137,12 @@ Notes:
|
|
|
119
137
|
- **Generation is enabled by default** and can be disabled via `generation: false`.
|
|
120
138
|
- **Router-aware POM helpers are enabled** when `generation.router.entry` is provided (the generator will introspect your router).
|
|
121
139
|
|
|
122
|
-
### `generation.router
|
|
140
|
+
### `generation.router`
|
|
123
141
|
|
|
124
|
-
Controls
|
|
142
|
+
Controls router introspection for `:to` analysis and navigation helper generation.
|
|
125
143
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- relative paths are resolved relative to Vite's resolved `config.root`
|
|
129
|
-
- absolute paths are used as-is
|
|
130
|
-
|
|
131
|
-
This file must export a **default router factory function** (e.g. `export default makeRouter`).
|
|
144
|
+
- `entry: string`: For standard Vue apps, where router introspection loads your Vue Router definition from. This file must export a **default router factory function** (e.g. `export default makeRouter`).
|
|
145
|
+
- `type: "vue-router" | "nuxt"`: The introspection provider. Defaults to `"vue-router"`. Use `"nuxt"` for file-based routing discovery (e.g. `app/pages` or `pages`).
|
|
132
146
|
|
|
133
147
|
### `generation.playwright.fixtures: boolean | string | { outDir?: string }`
|
|
134
148
|
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
● ## Highlights
|
|
2
|
-
-
|
|
3
|
-
-
|
|
4
|
-
-
|
|
2
|
+
- Added Nuxt pages introspection to router introspection.
|
|
3
|
+
- Major overhaul of Vue plugin with expanded support/dev/build plugins and types.
|
|
4
|
+
- Introduced Playwright types and updated class generation pipeline.
|
|
5
|
+
- Significant updates to transform and utility modules to support new features.
|
|
6
|
+
- CI improvements: release on main without gate and a pre-push up-to-date guard.
|
|
5
7
|
|
|
6
8
|
## Changes
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
- Router Introspection
|
|
10
|
+
- Enhanced router-introspection.ts to add Nuxt pages introspection functionality.
|
|
11
|
+
- Plugin System
|
|
12
|
+
- Overhauled plugin/vue-plugin.ts (+286/- changes) with expanded capabilities.
|
|
13
|
+
- Updated support plugins: dev-plugin, build-plugin, support-plugins, path-utils, logger, and
|
|
14
|
+
types.
|
|
15
|
+
- Improved create-vue-pom-generator-plugins.ts for plugin creation.
|
|
16
|
+
- Class Generation
|
|
17
|
+
- Added class-generation/playwright-types.ts.
|
|
18
|
+
- Updated BasePage, Pointer, index.ts, and method-generation.ts for generation flow.
|
|
19
|
+
- Transform & Utilities
|
|
20
|
+
- Refactored transform.ts and utils.ts with substantial changes.
|
|
21
|
+
- Minor eslint.config.ts adjustments.
|
|
22
|
+
- CI & Tooling
|
|
23
|
+
- Updated .github/workflows/release.yml and agentic-release-notes.lock.yml.
|
|
24
|
+
- Added scripts/git-hooks/pre-push.sh pre-push guard.
|
|
25
|
+
- Cleaned up .gitignore; updated package.json and package-lock.json for git hooks.
|
|
26
|
+
- Documentation & Logs
|
|
27
|
+
- Linted README.md.
|
|
28
|
+
- Added build_v5.log and build_web_v5.log.
|
|
14
29
|
|
|
15
30
|
## Pull Requests Included
|
|
16
|
-
- #1 Add PR release-notes preview comments
|
|
17
|
-
|
|
31
|
+
- #1 Add PR release-notes preview comments (https://github.com/immense/vue-pom-generator/pull/1)
|
|
32
|
+
by @dkattan
|
|
18
33
|
|
|
19
34
|
## Testing
|
|
20
|
-
-
|
|
35
|
+
- Updated tests in tests/options.test.ts and tests/utils-coverage.test.ts; adjusted fixture
|
|
36
|
+
MyComp_CancelButtons_InnerText.vue.
|
|
37
|
+
- No new test suites added.
|
|
21
38
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PwLocator, PwPage } from "./playwright-types";
|
|
2
2
|
import { TESTID_CLICK_EVENT_NAME, TESTID_CLICK_EVENT_STRICT_FLAG } from "../click-instrumentation";
|
|
3
3
|
import type { TestIdClickEventDetail } from "../click-instrumentation";
|
|
4
4
|
import { Pointer } from "./Pointer";
|
|
5
|
-
import type { AfterPointerClickInfo } from "./Pointer";
|
|
5
|
+
import type { AfterPointerClick, AfterPointerClickInfo } from "./Pointer";
|
|
6
6
|
|
|
7
7
|
// Click instrumentation is a core contract for generated POMs.
|
|
8
8
|
const REQUIRE_CLICK_EVENT = true;
|
|
@@ -218,6 +218,21 @@ export class BasePage {
|
|
|
218
218
|
return this.page.locator(this.selectorForTestId(testId));
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Animates the cursor to an element.
|
|
223
|
+
*/
|
|
224
|
+
protected async animateCursorToElement(
|
|
225
|
+
target: string | PwLocator,
|
|
226
|
+
executeClick: boolean = true,
|
|
227
|
+
delayMs: number = 100,
|
|
228
|
+
annotationText: string = "",
|
|
229
|
+
options?: {
|
|
230
|
+
afterClick?: AfterPointerClick;
|
|
231
|
+
},
|
|
232
|
+
): Promise<void> {
|
|
233
|
+
await this.pointer.animateCursorToElement(target, executeClick, delayMs, annotationText, options);
|
|
234
|
+
}
|
|
235
|
+
|
|
221
236
|
/**
|
|
222
237
|
* Creates an indexable proxy for keyed elements so generated POMs can expose
|
|
223
238
|
* ergonomic accessors like:
|
|
@@ -3,9 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { generateViewObjectModelMethodContent } from "../method-generation";
|
|
6
|
-
import { parseRouterFileFromCwd } from "../router-introspection";
|
|
7
|
-
// NOTE: This module intentionally does not depend on Babel parsing.
|
|
8
|
-
|
|
6
|
+
import { introspectNuxtPages, parseRouterFileFromCwd } from "../router-introspection";
|
|
9
7
|
import { IComponentDependencies, IDataTestId, PomExtraClickMethodSpec, PomPrimarySpec, upperFirst } from "../utils";
|
|
10
8
|
|
|
11
9
|
// Intentionally imported so tooling understands this exported helper is part of the
|
|
@@ -62,9 +60,16 @@ interface RouteMeta {
|
|
|
62
60
|
template: string;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
async function getRouteMetaByComponent(
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
async function getRouteMetaByComponent(
|
|
64
|
+
projectRoot?: string,
|
|
65
|
+
routerEntry?: string,
|
|
66
|
+
routerType?: "vue-router" | "nuxt",
|
|
67
|
+
): Promise<Record<string, RouteMeta>> {
|
|
68
|
+
const root = projectRoot ?? process.cwd();
|
|
69
|
+
|
|
70
|
+
const { routeMetaEntries } = routerType === "nuxt"
|
|
71
|
+
? await introspectNuxtPages(root)
|
|
72
|
+
: await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry));
|
|
68
73
|
|
|
69
74
|
const map = new Map<string, RouteMeta[]>();
|
|
70
75
|
for (const entry of routeMetaEntries) {
|
|
@@ -327,12 +332,20 @@ export interface GenerateFilesOptions {
|
|
|
327
332
|
/** Which POM languages to emit. Defaults to ["ts"]. */
|
|
328
333
|
emitLanguages?: Array<"ts" | "csharp">;
|
|
329
334
|
|
|
335
|
+
/** C# generation options. */
|
|
336
|
+
csharp?: {
|
|
337
|
+
namespace?: string;
|
|
338
|
+
};
|
|
339
|
+
|
|
330
340
|
/** When true, generate router-aware helpers like goToSelf() on view POMs. */
|
|
331
341
|
vueRouterFluentChaining?: boolean;
|
|
332
342
|
|
|
333
343
|
/** Router entry path used for vue-router introspection when fluent chaining is enabled. */
|
|
334
344
|
routerEntry?: string;
|
|
335
345
|
|
|
346
|
+
/** The type of router introspection to perform. */
|
|
347
|
+
routerType?: "vue-router" | "nuxt";
|
|
348
|
+
|
|
336
349
|
routeMetaByComponent?: Record<string, RouteMeta>;
|
|
337
350
|
}
|
|
338
351
|
|
|
@@ -382,8 +395,10 @@ export async function generateFiles(
|
|
|
382
395
|
customPomImportAliases,
|
|
383
396
|
testIdAttribute,
|
|
384
397
|
emitLanguages: emitLanguagesOverride,
|
|
398
|
+
csharp,
|
|
385
399
|
vueRouterFluentChaining,
|
|
386
400
|
routerEntry,
|
|
401
|
+
routerType,
|
|
387
402
|
} = options;
|
|
388
403
|
|
|
389
404
|
const emitLanguages: Array<"ts" | "csharp"> = emitLanguagesOverride?.length
|
|
@@ -393,7 +408,7 @@ export async function generateFiles(
|
|
|
393
408
|
const outDir = outDirOverride ?? "./pom";
|
|
394
409
|
|
|
395
410
|
const routeMetaByComponent = vueRouterFluentChaining
|
|
396
|
-
? await getRouteMetaByComponent(projectRoot, routerEntry)
|
|
411
|
+
? await getRouteMetaByComponent(projectRoot, routerEntry, routerType)
|
|
397
412
|
: undefined;
|
|
398
413
|
|
|
399
414
|
if (emitLanguages.includes("ts")) {
|
|
@@ -421,6 +436,8 @@ export async function generateFiles(
|
|
|
421
436
|
if (emitLanguages.includes("csharp")) {
|
|
422
437
|
const csFiles = generateAggregatedCSharpFiles(componentHierarchyMap, outDir, {
|
|
423
438
|
projectRoot,
|
|
439
|
+
testIdAttribute,
|
|
440
|
+
csharp,
|
|
424
441
|
});
|
|
425
442
|
for (const file of csFiles) {
|
|
426
443
|
createFile(file.filePath, file.content);
|
|
@@ -454,11 +471,11 @@ function toCSharpParam(paramTypeExpr: string): { type: string; defaultExpr?: str
|
|
|
454
471
|
const typePart = left.includes("|") ? "string" : left;
|
|
455
472
|
|
|
456
473
|
let type = "string";
|
|
457
|
-
if (/(
|
|
474
|
+
if (/(?:^|\s)boolean(?:\s|$)/.test(typePart))
|
|
458
475
|
type = "bool";
|
|
459
|
-
else if (/(
|
|
476
|
+
else if (/(?:^|\s)string(?:\s|$)/.test(typePart))
|
|
460
477
|
type = "string";
|
|
461
|
-
else if (/(
|
|
478
|
+
else if (/(?:^|\s)number(?:\s|$)/.test(typePart))
|
|
462
479
|
type = "int";
|
|
463
480
|
else if (/\d+/.test(typePart) && typePart === "")
|
|
464
481
|
type = "int";
|
|
@@ -508,10 +525,17 @@ function formatCSharpParams(params: Record<string, string> | undefined): { signa
|
|
|
508
525
|
function generateAggregatedCSharpFiles(
|
|
509
526
|
componentHierarchyMap: Map<string, IComponentDependencies>,
|
|
510
527
|
outDir: string,
|
|
511
|
-
options: {
|
|
528
|
+
options: {
|
|
529
|
+
projectRoot?: string;
|
|
530
|
+
testIdAttribute?: string;
|
|
531
|
+
csharp?: {
|
|
532
|
+
namespace?: string;
|
|
533
|
+
};
|
|
534
|
+
} = {},
|
|
512
535
|
): Array<{ filePath: string; content: string }> {
|
|
513
|
-
const
|
|
514
|
-
const
|
|
536
|
+
const outAbs = ensureDir(outDir);
|
|
537
|
+
const namespace = options.csharp?.namespace ?? "Playwright.Generated";
|
|
538
|
+
const testIdAttribute = (options.testIdAttribute || "data-testid").trim() || "data-testid";
|
|
515
539
|
|
|
516
540
|
const entries = Array.from(componentHierarchyMap.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
517
541
|
|
|
@@ -527,13 +551,13 @@ function generateAggregatedCSharpFiles(
|
|
|
527
551
|
"using System.Threading.Tasks;",
|
|
528
552
|
"using Microsoft.Playwright;",
|
|
529
553
|
"",
|
|
530
|
-
|
|
554
|
+
`namespace ${namespace};`,
|
|
531
555
|
"",
|
|
532
|
-
"public abstract class BasePage",
|
|
556
|
+
"public abstract partial class BasePage",
|
|
533
557
|
"{",
|
|
534
558
|
" protected BasePage(IPage page) => Page = page;",
|
|
535
559
|
" protected IPage Page { get; }",
|
|
536
|
-
|
|
560
|
+
` protected ILocator LocatorByTestId(string testId) => Page.Locator($"[${testIdAttribute}=\\"{testId}\\"]");`,
|
|
537
561
|
"",
|
|
538
562
|
" // Minimal vue-select helper mirroring the TS BasePage.selectVSelectByTestId behavior.",
|
|
539
563
|
" // Note: annotationText is currently a no-op in C# output (we don't render a cursor overlay).",
|
|
@@ -559,8 +583,9 @@ function generateAggregatedCSharpFiles(
|
|
|
559
583
|
const chunks: string[] = [header];
|
|
560
584
|
|
|
561
585
|
for (const [componentName, deps] of entries) {
|
|
586
|
+
const className = toPascalCaseLocal(componentName);
|
|
562
587
|
chunks.push(
|
|
563
|
-
`public
|
|
588
|
+
`public partial class ${className} : BasePage\n{\n public ${className}(IPage page) : base(page) { }\n`,
|
|
564
589
|
);
|
|
565
590
|
|
|
566
591
|
// Primary specs
|
|
@@ -748,7 +773,7 @@ function maybeGenerateFixtureRegistry(
|
|
|
748
773
|
projectRoot?: string;
|
|
749
774
|
},
|
|
750
775
|
) {
|
|
751
|
-
const { generateFixtures, pomOutDir
|
|
776
|
+
const { generateFixtures, pomOutDir } = options;
|
|
752
777
|
if (!generateFixtures)
|
|
753
778
|
return;
|
|
754
779
|
|
|
@@ -767,7 +792,7 @@ function maybeGenerateFixtureRegistry(
|
|
|
767
792
|
const fixtureOutDirRel = looksLikeFilePath ? path.dirname(fixtureOutRel) : fixtureOutRel;
|
|
768
793
|
const fixtureFileName = looksLikeFilePath ? path.basename(fixtureOutRel) : "fixtures.g.ts";
|
|
769
794
|
|
|
770
|
-
const root = projectRoot ?? process.cwd();
|
|
795
|
+
const root = options.projectRoot ?? process.cwd();
|
|
771
796
|
const fixtureOutDirAbs = path.isAbsolute(fixtureOutDirRel)
|
|
772
797
|
? fixtureOutDirRel
|
|
773
798
|
: path.resolve(root, fixtureOutDirRel);
|
|
@@ -1132,8 +1157,7 @@ async function generateAggregatedFiles(
|
|
|
1132
1157
|
outputDir: string,
|
|
1133
1158
|
items: Array<[string, IComponentDependencies]>,
|
|
1134
1159
|
) => {
|
|
1135
|
-
|
|
1136
|
-
const imports: string[] = ["import type { Locator as PwLocator, Page as PwPage } from \"@playwright/test\";"];
|
|
1160
|
+
const imports: string[] = [];
|
|
1137
1161
|
|
|
1138
1162
|
if (!basePageClassPath) {
|
|
1139
1163
|
throw new Error("basePageClassPath is required for aggregated generation");
|
|
@@ -1158,6 +1182,20 @@ async function generateAggregatedFiles(
|
|
|
1158
1182
|
"}",
|
|
1159
1183
|
].join("\n");
|
|
1160
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
|
+
return typesSource.trim();
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1161
1199
|
const inlinePointerModule = () => {
|
|
1162
1200
|
// Inline Pointer.ts from this package so generated POMs are self-contained and do not
|
|
1163
1201
|
// rely on runtime TS module resolution within workspace packages.
|
|
@@ -1190,6 +1228,12 @@ async function generateAggregatedFiles(
|
|
|
1190
1228
|
"",
|
|
1191
1229
|
);
|
|
1192
1230
|
|
|
1231
|
+
// The aggregated file inlines these structural types once at the top.
|
|
1232
|
+
pointerSource = pointerSource.replace(
|
|
1233
|
+
/import\s+type\s*\{\s*PwLocator\s*,\s*PwPage\s*\}\s*from\s*["']\.\/playwright-types["'];?\s*/,
|
|
1234
|
+
"",
|
|
1235
|
+
);
|
|
1236
|
+
|
|
1193
1237
|
return pointerSource.trim();
|
|
1194
1238
|
};
|
|
1195
1239
|
|
|
@@ -1222,15 +1266,22 @@ async function generateAggregatedFiles(
|
|
|
1222
1266
|
"",
|
|
1223
1267
|
);
|
|
1224
1268
|
|
|
1269
|
+
// The aggregated file inlines these structural types once at the top.
|
|
1270
|
+
basePageSource = basePageSource.replace(
|
|
1271
|
+
/import\s+type\s*\{\s*PwLocator\s*,\s*PwPage\s*\}\s*from\s*["']\.\/playwright-types["'];?\s*/,
|
|
1272
|
+
"",
|
|
1273
|
+
);
|
|
1274
|
+
|
|
1225
1275
|
// BasePage references Pointer, but in aggregated output we inline Pointer above.
|
|
1226
1276
|
basePageSource = basePageSource.replace(
|
|
1227
|
-
/import\s
|
|
1277
|
+
/import\s+(?:type\s*)?\{[\s\S]*?\}\s*from\s*["']\.\/Pointer["'];?\s*/g,
|
|
1228
1278
|
"",
|
|
1229
1279
|
);
|
|
1230
1280
|
|
|
1231
1281
|
return basePageSource.trim();
|
|
1232
1282
|
};
|
|
1233
1283
|
|
|
1284
|
+
const playwrightTypesInline = inlinePlaywrightTypesModule();
|
|
1234
1285
|
const pointerInline = inlinePointerModule();
|
|
1235
1286
|
const basePageInline = inlineBasePageModule();
|
|
1236
1287
|
|
|
@@ -1244,7 +1295,7 @@ async function generateAggregatedFiles(
|
|
|
1244
1295
|
const importAliases: Record<string, string> = {
|
|
1245
1296
|
Toggle: "ToggleWidget",
|
|
1246
1297
|
Checkbox: "CheckboxWidget",
|
|
1247
|
-
...(options.customPomImportAliases
|
|
1298
|
+
...(options.customPomImportAliases),
|
|
1248
1299
|
};
|
|
1249
1300
|
|
|
1250
1301
|
const customDirRelOrAbs = options.customPomDir ?? "tests/playwright/pom/custom";
|
|
@@ -1489,6 +1540,8 @@ async function generateAggregatedFiles(
|
|
|
1489
1540
|
header,
|
|
1490
1541
|
...imports,
|
|
1491
1542
|
"",
|
|
1543
|
+
playwrightTypesInline,
|
|
1544
|
+
"",
|
|
1492
1545
|
pointerInline,
|
|
1493
1546
|
"",
|
|
1494
1547
|
basePageInline,
|
|
@@ -1502,7 +1555,7 @@ async function generateAggregatedFiles(
|
|
|
1502
1555
|
|
|
1503
1556
|
const base = ensureDir(outDir);
|
|
1504
1557
|
const outputFile = path.join(base, "page-object-models.g.ts");
|
|
1505
|
-
const header =
|
|
1558
|
+
const header = `/// <reference lib="es2015" />\n${eslintSuppressionHeader}/**\n * Aggregated generated POMs\n${AUTO_GENERATED_COMMENT}`;
|
|
1506
1559
|
const content = makeAggregatedContent(header, path.dirname(outputFile), [...views, ...components]);
|
|
1507
1560
|
|
|
1508
1561
|
const indexFile = path.join(base, "index.ts");
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface PwLocator {
|
|
2
|
+
locator: (selector: string) => PwLocator;
|
|
3
|
+
first: () => PwLocator;
|
|
4
|
+
count: () => Promise<number>;
|
|
5
|
+
click: (options?: { force?: boolean }) => Promise<void>;
|
|
6
|
+
fill: (value: string, options?: { force?: boolean; timeout?: number }) => Promise<void>;
|
|
7
|
+
getAttribute: (name: string) => Promise<string | null>;
|
|
8
|
+
scrollIntoViewIfNeeded: (options?: { timeout?: number }) => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type PwSelectOption = string | { value?: string; label?: string; index?: number };
|
|
12
|
+
|
|
13
|
+
export interface PwPage {
|
|
14
|
+
locator: (selector: string) => PwLocator;
|
|
15
|
+
url: () => string;
|
|
16
|
+
waitForTimeout: (timeout: number) => Promise<void>;
|
|
17
|
+
evaluate: <R, Arg>(pageFunction: (arg: Arg) => R | Promise<R>, arg: Arg) => Promise<R>;
|
|
18
|
+
isVisible: (selector: string, options?: { timeout?: number }) => Promise<boolean>;
|
|
19
|
+
textContent: (selector: string, options?: { timeout?: number }) => Promise<string | null>;
|
|
20
|
+
waitForSelector: (selector: string, options?: { timeout?: number }) => Promise<object | null>;
|
|
21
|
+
hover: (selector: string, options?: { timeout?: number }) => Promise<void>;
|
|
22
|
+
selectOption: (selector: string, values: PwSelectOption | PwSelectOption[], options?: { timeout?: number }) => Promise<string[]>;
|
|
23
|
+
}
|