@immense/vue-pom-generator 1.0.36 → 1.0.38

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 CHANGED
@@ -45,6 +45,10 @@ If `generation.playwright.fixtures` is enabled, it also emits:
45
45
 
46
46
  - `tests/playwright/__generated__/fixtures.g.ts` (generated; do not edit)
47
47
 
48
+ Generated fixtures automatically prefer matching handwritten override classes from
49
+ `tests/playwright/pom/overrides/<ClassName>.ts` (or the sibling `overrides/` directory next to
50
+ your configured `generation.playwright.customPoms.dir`).
51
+
48
52
  ### Vite config example
49
53
 
50
54
  ```ts
@@ -147,13 +151,35 @@ export default defineConfig(() => {
147
151
 
148
152
  Notes:
149
153
 
150
- - `vuePomGenerator(...)` already wires `@vitejs/plugin-vue` internally for non-Nuxt apps.
151
- - Do not pass `vue()` into `createVuePomGeneratorPlugins(...)`; pass Vue options via `vueOptions`.
154
+ - `vuePomGenerator(...)` wires `@vitejs/plugin-vue` internally by default for standard Vue apps.
155
+ - Do not pass `vue()` into `createVuePomGeneratorPlugins(...)`; pass Vue plugin options via `vueOptions`.
156
+ - When the app should own `vue()` explicitly, set `vuePluginOwnership: "external"` and add `vue()` separately in your Vite config.
152
157
 
153
158
  - **Injection is enabled by plugin inclusion** (there is no longer an `injection.enabled` flag).
154
159
  - **Generation is enabled by default** and can be disabled via `generation: false`.
155
160
  - **Router-aware POM helpers are enabled** when `generation.router.entry` is provided (the generator will introspect your router).
156
161
 
162
+ ### External Vue plugin ownership
163
+
164
+ If your app should own the core Vue Vite plugin explicitly, add `vue()` yourself and let this package patch the resolved plugin:
165
+
166
+ ```ts
167
+ import vue from "@vitejs/plugin-vue";
168
+ import { defineConfig } from "vite";
169
+ import { defineVuePomGeneratorConfig, vuePomGenerator } from "@immense/vue-pom-generator";
170
+
171
+ const pomConfig = defineVuePomGeneratorConfig({
172
+ vuePluginOwnership: "external",
173
+ });
174
+
175
+ export default defineConfig({
176
+ plugins: [
177
+ vue(),
178
+ ...vuePomGenerator(pomConfig),
179
+ ],
180
+ });
181
+ ```
182
+
157
183
  ### `generation.router`
158
184
 
159
185
  Controls router introspection for `:to` analysis and navigation helper generation.
@@ -180,6 +206,13 @@ Defaults:
180
206
 
181
207
  - when `true`: writes `fixtures.g.ts` alongside generated POMs (under `generation.outDir`)
182
208
 
209
+ Convention:
210
+
211
+ - if `tests/playwright/pom/overrides/<ClassName>.ts` exists, the generated fixture for that page/component
212
+ instantiates the override class instead of the raw generated `Pom.<ClassName>`
213
+ - the override directory is inferred as the sibling `overrides/` directory next to
214
+ `generation.playwright.customPoms.dir`
215
+
183
216
  ### Vite config example
184
217
 
185
218
  - `nativeWrappers` describes common wrapper components (e.g. design-system buttons/inputs)
@@ -204,7 +237,9 @@ When you want CI/builds to fail on explicit test ids, pair `existingIdBehavior:
204
237
 
205
238
  ### ESLint cleanup rule: remove existing test-id attributes
206
239
 
207
- Use the `remove-existing-test-id-attributes` rule to strip explicit test-id attributes from `.vue` files before or while enforcing `existingIdBehavior: "error"`.
240
+ Use the `remove-existing-test-id-attributes` rule to strip explicit test-id usage from `.vue` files before or while enforcing `existingIdBehavior: "error"`.
241
+
242
+ The fixer handles both template attributes like `data-testid="save-button"` and object-literal keys such as `inputAttr: { 'data-testid': 'save-button' }` inside Vue SFC expressions/scripts.
208
243
 
209
244
  Add this to your ESLint flat-config file, typically `eslint.config.ts` (or `eslint.config.js` / `eslint.config.mjs` at the project root):
210
245
 
package/RELEASE_NOTES.md CHANGED
@@ -1,33 +1,51 @@
1
- ## v1.0.36
1
+ Now I have enough information to generate the release notes. The main feature is auto-wiring
2
+ fixture overrides in Playwright test fixtures.
2
3
 
3
- ### Highlights
4
+ ```markdown
5
+ # Release v1.0.38
4
6
 
5
- - Fixed handler collision suffixes to prevent naming conflicts in strict error mode
6
- - Improved handler suffix derivation by skipping unstable leading arguments
7
- - Enhanced detection of constant-like member expressions for more stable naming
8
- - Added comprehensive test coverage for collision avoidance scenarios
7
+ ## Highlights
9
8
 
10
- ### Changes
9
+ - Playwright fixtures now automatically detect and use handwritten POM override classes
10
+ - Override classes in `overrides/<ClassName>.ts` are auto-wired when generating fixtures
11
+ - Enhanced test coverage for class generation and TypeScript compilation
12
+ - Updated documentation with fixture override conventions
11
13
 
12
- **Bug Fixes**
13
- - Fixed handler collision suffixes when generating test IDs and POM methods (#27dc566)
14
- - Skip unstable leading arguments when deriving handler suffixes from function arguments
15
- - Check up to 4 arguments (previously 2) to find stable literal/constant values
16
- - Validate constant-like root identifiers (e.g., `RebootPreference.Suppress`) before using
17
- member expression names
14
+ ## Changes
18
15
 
19
- **Testing**
20
- - Added test for strict-mode collision avoidance with later stable args
21
- - Added utility test coverage for `getRootIdentifierFromMemberChain` function
22
- - New test validates `clickRunDeploymentActionAssign` method generation without conflicts
16
+ ### Playwright Fixture Generation
23
17
 
24
- ### Pull Requests Included
18
+ - Fixtures now automatically prefer matching handwritten override classes from the `overrides/`
19
+ directory
20
+ - Override directory location is inferred as sibling to `generation.playwright.customPoms.dir`
21
+ - Generated fixtures import and instantiate override classes when available (e.g.,
22
+ `tests/playwright/pom/overrides/<ClassName>.ts`)
23
+ - Added runtime detection of override files via filesystem checks during generation
25
24
 
26
- - [#1](https://github.com/immense/vue-pom-generator/pull/1) - Add PR release-notes preview
27
- comments (merged 2026-02-02, not in v1.0.36 window)
25
+ ### Documentation
26
+
27
+ - Added fixture override convention documentation to README
28
+ - Updated plugin type definitions with fixture override behavior
29
+ - Documented override directory structure and lookup rules
28
30
 
29
31
  ### Testing
30
32
 
31
- Added 44 lines of test coverage across `transform.test.ts` and `utils-coverage.test.ts` to
32
- validate collision handling improvements.
33
+ - Expanded `tests/class-generation-coverage.test.ts` with fixture override scenarios
34
+ - Enhanced `tests/generated-tsc.test.ts` with 75+ additional lines of TypeScript compilation
35
+ tests
36
+
37
+ ## Breaking Changes
38
+
39
+ None.
40
+
41
+ ## Pull Requests Included
42
+
43
+ - #1 Add PR release-notes preview comments https://github.com/immense/vue-pom-generator/pull/1
44
+ (by @dkattan)
45
+
46
+ ## Testing
47
+
48
+ Test coverage expanded significantly with new test cases for fixture override auto-wiring and
49
+ TypeScript compilation validation.
50
+ ```
33
51
 
@@ -233,7 +233,6 @@ function generateExtraClickMethodContent(spec: PomExtraClickMethodSpec): string
233
233
  if (labelNeedsTemplate) {
234
234
  lines.push(` const label = ${labelExpr};`);
235
235
  }
236
-
237
236
  const rootArg = rootNeedsTemplate ? "rootTestId" : rootExpr;
238
237
  const labelArg = labelNeedsTemplate ? "label" : labelExpr;
239
238
  lines.push(` await this.clickWithinTestIdByLabel(${rootArg}, ${labelArg}, ${annotationArg}, ${waitArg});`);
@@ -322,6 +321,10 @@ export interface GenerateFilesOptions {
322
321
  * Default output (when `true`):
323
322
  * - `<projectRoot>/<outDir>/fixtures.g.ts`
324
323
  *
324
+ * Convention:
325
+ * - fixtures automatically prefer matching handwritten override classes from
326
+ * `<dirname(customPomDir)>/overrides/<ClassName>.ts` when present
327
+ *
325
328
  * Accepted values:
326
329
  * - `true`: enable with defaults
327
330
  * - `"path"`: enable and write the fixture file under this directory (resolved relative to projectRoot),
@@ -506,6 +509,7 @@ export async function generateFiles(
506
509
  generateFixtures,
507
510
  pomOutDir: outDir,
508
511
  projectRoot,
512
+ customPomDir,
509
513
  });
510
514
  if (fixtureRegistryFile) {
511
515
  writeGeneratedFile(fixtureRegistryFile);
@@ -1005,6 +1009,7 @@ function maybeGenerateFixtureRegistry(
1005
1009
  generateFixtures: GenerateFilesOptions["generateFixtures"];
1006
1010
  pomOutDir: string;
1007
1011
  projectRoot?: string;
1012
+ customPomDir?: string;
1008
1013
  },
1009
1014
  ): GeneratedFileOutput | null {
1010
1015
  const { generateFixtures, pomOutDir } = options;
@@ -1031,6 +1036,12 @@ function maybeGenerateFixtureRegistry(
1031
1036
  ? fixtureOutDirRel
1032
1037
  : path.resolve(root, fixtureOutDirRel);
1033
1038
 
1039
+ const customPomDirRel = options.customPomDir ?? "tests/playwright/pom/custom";
1040
+ const customPomDirAbs = path.isAbsolute(customPomDirRel)
1041
+ ? customPomDirRel
1042
+ : path.resolve(root, customPomDirRel);
1043
+ const overridePomDirAbs = path.resolve(path.dirname(customPomDirAbs), "overrides");
1044
+
1034
1045
  // Resolve the directory that contains the POM barrel export (e.g. <root>/pom).
1035
1046
  const pomDirAbs = path.isAbsolute(pomOutDir) ? pomOutDir : path.resolve(root, pomOutDir);
1036
1047
 
@@ -1067,6 +1078,29 @@ function maybeGenerateFixtureRegistry(
1067
1078
  })
1068
1079
  .sort((a, b) => a.localeCompare(b));
1069
1080
 
1081
+ const fixtureClassNames = [...viewClassNames, ...componentClassNames];
1082
+ const overrideCtorEntries = fixtureClassNames
1083
+ .map((name) => {
1084
+ const overrideFilePath = path.join(overridePomDirAbs, `${name}.ts`);
1085
+ if (!fs.existsSync(overrideFilePath))
1086
+ return null;
1087
+
1088
+ return {
1089
+ className: name,
1090
+ localIdentifier: `${name}Override`,
1091
+ importSpecifier: stripExtension(toPosixRelativePath(fixtureOutDirAbs, overrideFilePath)),
1092
+ };
1093
+ })
1094
+ .filter((entry): entry is { className: string; localIdentifier: string; importSpecifier: string } => !!entry);
1095
+ const overrideCtorByClassName = new Map(overrideCtorEntries.map(entry => [entry.className, entry.localIdentifier]));
1096
+ const overrideImports = overrideCtorEntries.length
1097
+ ? `${overrideCtorEntries
1098
+ .map(entry => `import { ${entry.className} as ${entry.localIdentifier} } from "${entry.importSpecifier}";`)
1099
+ .join("\n")}\n\n`
1100
+ : "";
1101
+
1102
+ const fixtureCtorExpression = (name: string) => overrideCtorByClassName.get(name) ?? `Pom.${name}`;
1103
+
1070
1104
  const header = `${eslintSuppressionHeader}/**\n`
1071
1105
  + ` * DO NOT MODIFY BY HAND\n`
1072
1106
  + ` *\n`
@@ -1080,11 +1114,11 @@ function maybeGenerateFixtureRegistry(
1080
1114
  // View POMs implement goTo() directly, so fixtures can be strongly typed without
1081
1115
  // casting/augmenting at runtime.
1082
1116
  const fixturesTypeEntries = viewClassNames
1083
- .map(name => ` ${lowerFirst(name)}: Pom.${name},`)
1117
+ .map(name => ` ${lowerFirst(name)}: ${fixtureCtorExpression(name)},`)
1084
1118
  .join("\n");
1085
1119
 
1086
1120
  const componentFixturesTypeEntries = componentClassNames
1087
- .map(name => ` ${lowerFirst(name)}: Pom.${name},`)
1121
+ .map(name => ` ${lowerFirst(name)}: ${fixtureCtorExpression(name)},`)
1088
1122
  .join("\n");
1089
1123
 
1090
1124
  const pomFactoryType = `export type PomConstructor<T> = new (page: PwPage) => T;\n\n`
@@ -1101,7 +1135,8 @@ function maybeGenerateFixtureRegistry(
1101
1135
  }/** Generated Playwright fixtures (typed page objects). */\n\n`
1102
1136
  + `import { expect, test as base } from "@playwright/test";\n`
1103
1137
  + `import type { Page as PwPage } from "@playwright/test";\n`
1104
- + `import * as Pom from "${pomImport}";\n\n`
1138
+ + `import * as Pom from "${pomImport}";\n`
1139
+ + `${overrideImports}`
1105
1140
  + `export interface PlaywrightOptions {\n`
1106
1141
  + ` animation: Pom.PlaywrightAnimationOptions;\n`
1107
1142
  + `}\n\n`
@@ -1896,8 +1931,9 @@ function getWidgetInstancesForView(
1896
1931
  continue;
1897
1932
  }
1898
1933
 
1899
- if (!availableClassIdentifiers.has(className))
1934
+ if (!availableClassIdentifiers.has(className)) {
1900
1935
  continue;
1936
+ }
1901
1937
 
1902
1938
  // Prefer stripping the view prefix (e.g. PreferencesPage-) for cleaner member names.
1903
1939
  const viewPrefix = `${componentName}-`;
@@ -17,6 +17,10 @@ export interface GenerateFilesOptions {
17
17
  * Default output (when `true`):
18
18
  * - `<projectRoot>/<outDir>/fixtures.g.ts`
19
19
  *
20
+ * Convention:
21
+ * - fixtures automatically prefer matching handwritten override classes from
22
+ * `<dirname(customPomDir)>/overrides/<ClassName>.ts` when present
23
+ *
20
24
  * Accepted values:
21
25
  * - `true`: enable with defaults
22
26
  * - `"path"`: enable and write the fixture file under this directory (resolved relative to projectRoot),
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../class-generation/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oCAAoC,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,EAAE,sBAAsB,EAAoE,MAAM,UAAU,CAAC;AAQpH,OAAO,EAAE,oCAAoC,EAAE,CAAC;AA8ChD,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAwPD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE1D;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhD;;;;;OAKG;IACH,oCAAoC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAEzD;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,KAAK,CAAC;QAC3B,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,wBAAwB,EAAE,MAAM,EAAE,CAAC;QAEnC;;;WAGG;QACH,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,GAAG,MAAM,CAAC;KAC5C,CAAC,CAAC;IAEH,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,uDAAuD;IACvD,aAAa,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;IAEvC,6BAA6B;IAC7B,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,6EAA6E;IAC7E,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,mDAAmD;IACnD,UAAU,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAEnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAClD;AAwCD,wBAAsB,aAAa,CACjC,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAC1D,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,iBAAiB,EAAE,MAAM,EACzB,OAAO,GAAE,oBAAyB,iBAkFnC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../class-generation/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oCAAoC,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,EAAE,sBAAsB,EAAoE,MAAM,UAAU,CAAC;AAQpH,OAAO,EAAE,oCAAoC,EAAE,CAAC;AA8ChD,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuPD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE1D;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhD;;;;;OAKG;IACH,oCAAoC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAEzD;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,KAAK,CAAC;QAC3B,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,wBAAwB,EAAE,MAAM,EAAE,CAAC;QAEnC;;;WAGG;QACH,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,GAAG,MAAM,CAAC;KAC5C,CAAC,CAAC;IAEH,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,uDAAuD;IACvD,aAAa,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;IAEvC,6BAA6B;IAC7B,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,6EAA6E;IAC7E,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,mDAAmD;IACnD,UAAU,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAEnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAClD;AAwCD,wBAAsB,aAAa,CACjC,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAC1D,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,iBAAiB,EAAE,MAAM,EACzB,OAAO,GAAE,oBAAyB,iBAmFnC"}