@letsrunit/playwright 0.3.8 → 0.3.9
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/dist/index.d.ts +2 -2
- package/dist/index.js +19 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/{locator.ts → fuzzy-locator.ts} +12 -8
- package/src/index.ts +1 -1
- package/src/selector/field-selector.ts +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@letsrunit/playwright",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Playwright extensions and utilities for letsrunit",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"testing",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"packageManager": "yarn@4.10.3",
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@letsrunit/utils": "0.3.
|
|
47
|
+
"@letsrunit/utils": "0.3.9",
|
|
48
48
|
"@playwright/test": "^1.57.0",
|
|
49
49
|
"case": "^1.6.3",
|
|
50
50
|
"diff": "^8.0.3",
|
|
@@ -5,16 +5,18 @@ type LocatorOptions = Parameters<Page['locator']>[1];
|
|
|
5
5
|
/**
|
|
6
6
|
* Locates an element using Playwright selectors, with fallbacks.
|
|
7
7
|
*/
|
|
8
|
-
export async function
|
|
8
|
+
export async function fuzzyLocator(page: Page, selector: string): Promise<Locator> {
|
|
9
9
|
const primary = page.locator(selector).first();
|
|
10
10
|
if (await primary.count()) return primary;
|
|
11
11
|
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
return (
|
|
13
|
+
(await tryRelaxNameToHasText(page, selector)) ||
|
|
14
|
+
(await tryTagInsteadOfRole(page, selector)) ||
|
|
15
|
+
(await tryRoleNameProximity(page, selector)) ||
|
|
16
|
+
(await tryFieldAlternative(page, selector)) ||
|
|
17
|
+
(await tryAsField(page, selector)) ||
|
|
18
|
+
primary
|
|
19
|
+
); // Nothing found, return the original locator (so caller can still wait/assert)
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
async function firstMatch(page: Page, sel: string | string[], opts: LocatorOptions = {}): Promise<Locator | null> {
|
|
@@ -65,11 +67,13 @@ async function tryRoleNameProximity(page: Page, selector: string): Promise<Locat
|
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
// Try alternatives if field is not found
|
|
68
|
-
// field="foo" → #foo > input
|
|
70
|
+
// field="foo" → #foo > input (only when name is a valid CSS identifier)
|
|
69
71
|
async function tryFieldAlternative(page: Page, selector: string): Promise<Locator | null> {
|
|
70
72
|
const matchField = selector.match(/^field="([^"]+)"i?$/i);
|
|
71
73
|
if (!matchField) return null;
|
|
72
74
|
const [, field] = matchField;
|
|
75
|
+
// Skip if the name contains characters invalid in a CSS ID selector
|
|
76
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(field)) return null;
|
|
73
77
|
return firstMatch(page, `#${field} > input`);
|
|
74
78
|
}
|
|
75
79
|
|
package/src/index.ts
CHANGED
|
@@ -109,6 +109,14 @@ export const createFieldEngine = () => ({
|
|
|
109
109
|
const ph = (el as HTMLInputElement).placeholder ?? el.getAttribute('placeholder');
|
|
110
110
|
if (ph) texts.push(ph);
|
|
111
111
|
|
|
112
|
+
// 7) Adjacent sibling <label> (handles <input/><label>Text</label> pattern without for/id)
|
|
113
|
+
for (let sib = el.nextElementSibling; sib; sib = sib.nextElementSibling) {
|
|
114
|
+
if (sib.tagName === 'LABEL') { texts.push(sib.textContent ?? ''); break; }
|
|
115
|
+
}
|
|
116
|
+
for (let sib = el.previousElementSibling; sib; sib = sib.previousElementSibling) {
|
|
117
|
+
if (sib.tagName === 'LABEL') { texts.push(sib.textContent ?? ''); break; }
|
|
118
|
+
}
|
|
119
|
+
|
|
112
120
|
return texts;
|
|
113
121
|
}
|
|
114
122
|
|