@immense/vue-pom-generator 1.0.42 → 1.0.44

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 CHANGED
@@ -1,25 +1,22 @@
1
- Based on the PR information and the specified release window (2026-04-06T12:50:48-05:00 to
2
- 2026-04-06T17:25:31-05:00), only PR #6 falls within this window. PRs #1 and #4 were merged
3
- earlier (February and April 1st respectively) and should not be included in v1.0.42.
1
+ ## Highlights
4
2
 
5
- ```markdown
6
- ## Highlights
7
-
8
- - Dev mode now fails fast on snapshot generation errors instead of silently continuing
9
- - Snapshot maps are rebuilt atomically to prevent partial state after compilation failures
10
- - Dev server behavior now matches build-mode semantics for strict snapshot errors
11
- - Added regression test coverage for strict collision failures during dev startup
3
+ - Fixed handling of wrapped editable targets in class generation
4
+ - Enhanced Pointer module to correctly resolve targets through wrapper elements
5
+ - Added comprehensive test suite for pointer functionality (152 new test lines)
6
+ - Improved test coverage for class generation edge cases
12
7
 
13
8
  ## Changes
14
9
 
15
- **Error Handling**
16
- - Dev startup now awaits snapshot generation so strict transform failures reject instead of
17
- silently continuing
18
- - Snapshot maps are rebuilt atomically during startup and batched regeneration to avoid partial
19
- state on failed recompiles
10
+ **Bug Fixes**
11
+ - Handle wrapped editable targets correctly in Pointer resolution logic
12
+ - Updated class generation index to support wrapped target elements
20
13
 
21
14
  **Testing**
22
- - Added regression coverage for strict collision failures during dev startup
15
+ - Added new `tests/pointer.test.ts` with comprehensive pointer test coverage
16
+ - Expanded `tests/class-generation-coverage.test.ts` with 43 additional test lines
17
+
18
+ **Type Updates**
19
+ - Minor update to `plugin/types.ts`
23
20
 
24
21
  ## Breaking Changes
25
22
 
@@ -27,12 +24,10 @@
27
24
 
28
25
  ## Pull Requests Included
29
26
 
30
- - #6 fix: fail fast on dev snapshot generation errors
31
- (https://github.com/immense/vue-pom-generator/pull/6)
27
+ None this release contains direct commits only.
32
28
 
33
29
  ## Testing
34
30
 
35
- Added regression test coverage for strict collision failures during dev startup. All tests
36
- passing with validation via eslint, typecheck, build, and test suite.
37
- ```
31
+ Significant test coverage added: new dedicated pointer test suite (152 lines) and expanded class
32
+ generation coverage tests (43 lines).
38
33
 
@@ -5,6 +5,8 @@ import type { PwLocator, PwPage } from "./playwright-types";
5
5
  // ---------------------------------------------------------------------------
6
6
 
7
7
  const __PW_CURSOR_ID__ = "__pw_cursor__";
8
+ const __PW_EDITABLE_DESCENDANT_SELECTOR__
9
+ = "input, textarea, select, [contenteditable=''], [contenteditable='true'], [contenteditable]:not([contenteditable='false'])";
8
10
 
9
11
  // A minimal 16×24 arrow cursor encoded as a base64 PNG.
10
12
  const __PW_CURSOR_PNG__ =
@@ -165,6 +167,44 @@ export class Pointer {
165
167
  return trimmed || undefined;
166
168
  }
167
169
 
170
+ private async isEditableElement(locator: PwLocator): Promise<boolean> {
171
+ try {
172
+ return await locator.first().evaluate((element) => {
173
+ if (!(element instanceof HTMLElement)) {
174
+ return false;
175
+ }
176
+
177
+ const tagName = element.tagName.toLowerCase();
178
+ return tagName === "input"
179
+ || tagName === "textarea"
180
+ || tagName === "select"
181
+ || element.isContentEditable;
182
+ });
183
+ }
184
+ catch {
185
+ return false;
186
+ }
187
+ }
188
+
189
+ private async resolveEditableLocator(locator: PwLocator): Promise<PwLocator> {
190
+ const first = locator.first();
191
+ if (await this.isEditableElement(first)) {
192
+ return first;
193
+ }
194
+
195
+ const descendant = first.locator(__PW_EDITABLE_DESCENDANT_SELECTOR__).first();
196
+ try {
197
+ if (await descendant.count() > 0) {
198
+ return descendant;
199
+ }
200
+ }
201
+ catch {
202
+ // Fall back to the original target if descendant lookup fails.
203
+ }
204
+
205
+ return first;
206
+ }
207
+
168
208
  public async animateCursorToElement(
169
209
  target: ElementTarget,
170
210
  executeClick: boolean = true,
@@ -305,15 +345,16 @@ export class Pointer {
305
345
  await this.animateCursorToElement(target, executeClick, delayMs, annotationText, options);
306
346
 
307
347
  const locator = this.toLocator(target);
348
+ const editableLocator = await this.resolveEditableLocator(locator);
308
349
  const typeDelayMs = animationOptions.keyboard?.typeDelayMilliseconds ?? 100;
309
350
 
310
351
  if (animationOptions.enabled !== false && typeDelayMs > 0) {
311
352
  // Clear existing content, then type character-by-character so keystrokes are visible.
312
- await locator.first().clear();
353
+ await editableLocator.clear();
313
354
  await this.page.keyboard.type(text, { delay: typeDelayMs });
314
355
  }
315
356
  else {
316
- await locator.first().fill(text);
357
+ await editableLocator.fill(text);
317
358
  }
318
359
  }
319
360
  }
@@ -758,6 +758,26 @@ function generateAggregatedCSharpFiles(
758
758
  " protected IPage Page { get; }",
759
759
  ` protected ILocator LocatorByTestId(string testId) => Page.Locator($"[${testIdAttribute}=\\"{testId}\\"]");`,
760
760
  " protected ILocator LocatorWithinTestIdByLabel(string rootTestId, string label, bool exact = true) => LocatorByTestId(rootTestId).GetByLabel(label, new() { Exact = exact });",
761
+ " protected async Task<ILocator> ResolveEditableLocatorAsync(ILocator locator)",
762
+ " {",
763
+ " var isEditable = await locator.EvaluateAsync<bool>(@\"el => {",
764
+ " if (!el || !(el instanceof HTMLElement)) return false;",
765
+ " const tagName = el.tagName.toLowerCase();",
766
+ " return tagName === 'input' || tagName === 'textarea' || tagName === 'select' || el.isContentEditable;",
767
+ " }\");",
768
+ " if (isEditable)",
769
+ " {",
770
+ " return locator;",
771
+ " }",
772
+ "",
773
+ " var descendant = locator.Locator(\"input, textarea, select, [contenteditable=''], [contenteditable='true'], [contenteditable]:not([contenteditable='false'])\").First;",
774
+ " if (await descendant.CountAsync() > 0)",
775
+ " {",
776
+ " return descendant;",
777
+ " }",
778
+ "",
779
+ " return locator;",
780
+ " }",
761
781
  " protected async Task ClickWithinTestIdByLabelAsync(string rootTestId, string label, bool exact = true)",
762
782
  " {",
763
783
  " await LocatorWithinTestIdByLabel(rootTestId, label, exact).ClickAsync();",
@@ -889,7 +909,8 @@ function generateAggregatedCSharpFiles(
889
909
 
890
910
  const emitActionCall = (locatorAccess: string) => {
891
911
  if (pom.nativeRole === "input") {
892
- chunks.push(` await ${locatorAccess}.FillAsync(text);`);
912
+ chunks.push(` var editableLocator = await ResolveEditableLocatorAsync(${locatorAccess});`);
913
+ chunks.push(" await editableLocator.FillAsync(text);");
893
914
  }
894
915
  else if (pom.nativeRole === "select") {
895
916
  chunks.push(` await ${locatorAccess}.SelectOptionAsync(value);`);
@@ -923,7 +944,8 @@ function generateAggregatedCSharpFiles(
923
944
  chunks.push(" if (await locator.CountAsync() > 0)");
924
945
  chunks.push(" {");
925
946
  if (pom.nativeRole === "input") {
926
- chunks.push(" await locator.FillAsync(text);");
947
+ chunks.push(" var editableLocator = await ResolveEditableLocatorAsync(locator);");
948
+ chunks.push(" await editableLocator.FillAsync(text);");
927
949
  }
928
950
  else if (pom.nativeRole === "select") {
929
951
  chunks.push(" await locator.SelectOptionAsync(value);");
@@ -1229,9 +1251,10 @@ function generateViewObjectModelContent(
1229
1251
  return false;
1230
1252
 
1231
1253
  const scope = a.attachTo ?? "views";
1254
+ const scopeMatchesBoth = scope === "both" || scope === "pagesAndComponents";
1232
1255
  const scopeOk = isView
1233
- ? (scope === "views" || scope === "both")
1234
- : (scope === "components" || scope === "both");
1256
+ ? (scope === "views" || scopeMatchesBoth)
1257
+ : (scope === "components" || scopeMatchesBoth);
1235
1258
  if (!scopeOk)
1236
1259
  return false;
1237
1260
  return a.attachWhenUsesComponents.some(c => hasChildComponent(c));
@@ -56,6 +56,8 @@ export declare class Pointer {
56
56
  constructor(page: PwPage, testIdAttribute: string);
57
57
  private toLocator;
58
58
  private getTestId;
59
+ private isEditableElement;
60
+ private resolveEditableLocator;
59
61
  animateCursorToElement(target: ElementTarget, executeClick?: boolean, delayMs?: number, _annotationText?: string, options?: {
60
62
  afterClick?: AfterPointerClick;
61
63
  }): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"Pointer.d.ts","sourceRoot":"","sources":["../../class-generation/Pointer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AA8D5D,MAAM,WAAW,0BAA0B;IAC1C;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,sDAAsD;IACtD,OAAO,CAAC,EAAE;QACT;;;;WAIG;QACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAE9B;;;WAGG;QACH,eAAe,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,CAAC;QAE7E;;;WAGG;QACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;IAEF,uCAAuC;IACvC,QAAQ,CAAC,EAAE;QACV;;;WAGG;QACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAC/B,CAAC;CACF;AASD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,0BAA0B,GAAG,IAAI,CAavF;AAED,MAAM,WAAW,qBAAqB;IACrC,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtF,KAAK,aAAa,GAAG,MAAM,GAAG,SAAS,CAAC;AAMxC,qBAAa,OAAO;IACnB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAEtB,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM;IAKxD,OAAO,CAAC,SAAS;YAIH,SAAS;IAMV,sBAAsB,CAClC,MAAM,EAAE,aAAa,EACrB,YAAY,GAAE,OAAc,EAC5B,OAAO,GAAE,MAAY,EACrB,eAAe,GAAE,MAAW,EAC5B,OAAO,CAAC,EAAE;QACT,UAAU,CAAC,EAAE,iBAAiB,CAAC;KAC/B,GACC,OAAO,CAAC,IAAI,CAAC;IAsHH,qCAAqC,CACjD,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,YAAY,GAAE,OAAc,EAC5B,OAAO,GAAE,MAAY,EACrB,cAAc,GAAE,MAAW,EAC3B,OAAO,CAAC,EAAE;QACT,UAAU,CAAC,EAAE,iBAAiB,CAAC;KAC/B,GACC,OAAO,CAAC,IAAI,CAAC;CAgBhB"}
1
+ {"version":3,"file":"Pointer.d.ts","sourceRoot":"","sources":["../../class-generation/Pointer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAgE5D,MAAM,WAAW,0BAA0B;IAC1C;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,sDAAsD;IACtD,OAAO,CAAC,EAAE;QACT;;;;WAIG;QACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAE9B;;;WAGG;QACH,eAAe,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,CAAC;QAE7E;;;WAGG;QACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;IAEF,uCAAuC;IACvC,QAAQ,CAAC,EAAE;QACV;;;WAGG;QACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAC/B,CAAC;CACF;AASD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,0BAA0B,GAAG,IAAI,CAavF;AAED,MAAM,WAAW,qBAAqB;IACrC,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtF,KAAK,aAAa,GAAG,MAAM,GAAG,SAAS,CAAC;AAMxC,qBAAa,OAAO;IACnB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAEtB,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM;IAKxD,OAAO,CAAC,SAAS;YAIH,SAAS;YAMT,iBAAiB;YAmBjB,sBAAsB;IAmBvB,sBAAsB,CAClC,MAAM,EAAE,aAAa,EACrB,YAAY,GAAE,OAAc,EAC5B,OAAO,GAAE,MAAY,EACrB,eAAe,GAAE,MAAW,EAC5B,OAAO,CAAC,EAAE;QACT,UAAU,CAAC,EAAE,iBAAiB,CAAC;KAC/B,GACC,OAAO,CAAC,IAAI,CAAC;IAsHH,qCAAqC,CACjD,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,YAAY,GAAE,OAAc,EAC5B,OAAO,GAAE,MAAY,EACrB,cAAc,GAAE,MAAW,EAC3B,OAAO,CAAC,EAAE;QACT,UAAU,CAAC,EAAE,iBAAiB,CAAC;KAC/B,GACC,OAAO,CAAC,IAAI,CAAC;CAiBhB"}