@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 +16 -21
- package/class-generation/Pointer.ts +43 -2
- package/class-generation/index.ts +27 -4
- package/dist/class-generation/Pointer.d.ts +2 -0
- package/dist/class-generation/Pointer.d.ts.map +1 -1
- package/dist/index.cjs +311 -148
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +312 -149
- package/dist/index.mjs.map +1 -1
- package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
- package/dist/plugin/support/build-plugin.d.ts +12 -2
- package/dist/plugin/support/build-plugin.d.ts.map +1 -1
- package/dist/plugin/support/dev-plugin.d.ts +2 -0
- package/dist/plugin/support/dev-plugin.d.ts.map +1 -1
- package/dist/plugin/support-plugins.d.ts +2 -0
- package/dist/plugin/support-plugins.d.ts.map +1 -1
- package/dist/plugin/types.d.ts +1 -1
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/tests/build-serve-parity.test.d.ts +2 -0
- package/dist/tests/build-serve-parity.test.d.ts.map +1 -0
- package/dist/tests/pointer.test.d.ts +2 -0
- package/dist/tests/pointer.test.d.ts.map +1 -0
- package/package.json +1 -1
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
●
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
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
|
-
**
|
|
16
|
-
-
|
|
17
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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
|
|
353
|
+
await editableLocator.clear();
|
|
313
354
|
await this.page.keyboard.type(text, { delay: typeDelayMs });
|
|
314
355
|
}
|
|
315
356
|
else {
|
|
316
|
-
await
|
|
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}
|
|
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
|
|
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" ||
|
|
1234
|
-
: (scope === "components" ||
|
|
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;
|
|
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"}
|