nativeproof 0.7.0 → 0.8.0
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/CHANGELOG.md +13 -0
- package/README.md +7 -0
- package/dist/locator.d.ts +20 -2
- package/dist/locator.js +36 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ All notable changes to NativeProof are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/) and the project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 0.8.0
|
|
8
|
+
|
|
9
|
+
Relative locators — `Locator.near` scopes to the match nearest an anchor.
|
|
10
|
+
|
|
11
|
+
**Added**
|
|
12
|
+
|
|
13
|
+
- **`Locator.near(anchor, { maxDistance? })`** — orders this locator's matches by bounds-centre
|
|
14
|
+
distance to the `anchor` locator's match, nearest first, so a control is addressed by the element
|
|
15
|
+
beside it: `getByRole("checkbox").near(getByText("Wi-Fi"))` is the checkbox in the Wi-Fi row.
|
|
16
|
+
`maxDistance` (px) drops farther matches, so an absent control resolves to nothing. Composes with
|
|
17
|
+
`.nth()` / `.check()` / `expect(locator).toBeChecked()`. Together with role selectors this retires
|
|
18
|
+
source-bounds geometry for "the control next to this label".
|
|
19
|
+
|
|
7
20
|
## 0.7.0
|
|
8
21
|
|
|
9
22
|
Role selectors — `getByRole` matches by element role, not just name.
|
package/README.md
CHANGED
|
@@ -449,8 +449,15 @@ await p.getByText("Item").count(); // how many elements match
|
|
|
449
449
|
await p.getByText("Item").nth(1).tap(); // the 2nd match (.first() / .last(); negative counts from the end)
|
|
450
450
|
await member.terms.check(); // checkbox/switch → tap to checked (no-op if already there); also uncheck()
|
|
451
451
|
await member.terms.isChecked(); // boolean
|
|
452
|
+
await p.getByRole("checkbox").near(p.getByText("Wi-Fi")).check(); // the checkbox in the Wi-Fi row
|
|
452
453
|
```
|
|
453
454
|
|
|
455
|
+
**Relative locators.** `getByRole(role)` matches by element class/type (`checkbox`, `switch`, `button`,
|
|
456
|
+
`textfield`, `image`), and `Locator.near(anchor, { maxDistance? })` scopes to the match nearest an
|
|
457
|
+
anchor's element — so a control beside a label is addressed by the label, not by coordinates:
|
|
458
|
+
`page(driver).getByRole("checkbox").near(page(driver).getByText("Wi-Fi"))`. Compose with `.check()` /
|
|
459
|
+
`expect(locator).toBeChecked()`.
|
|
460
|
+
|
|
454
461
|
`tap()` resolves the element's bounds from the page source and taps the centre — a coordinate
|
|
455
462
|
tap that works even on Compose / SwiftUI nodes Appium reports as non-clickable.
|
|
456
463
|
|
package/dist/locator.d.ts
CHANGED
|
@@ -74,18 +74,36 @@ export declare class Locator {
|
|
|
74
74
|
private readonly options;
|
|
75
75
|
/** When set, the locator resolves to the nth match (negative counts from the end). */
|
|
76
76
|
private readonly index?;
|
|
77
|
+
/** When set, matches are ordered by proximity to the anchor (and filtered by maxDistance). */
|
|
78
|
+
private readonly proximity?;
|
|
77
79
|
constructor(driver: Driver, selector: Selector, options?: WaitOptions,
|
|
78
80
|
/** When set, the locator resolves to the nth match (negative counts from the end). */
|
|
79
|
-
index?: number | undefined
|
|
81
|
+
index?: number | undefined,
|
|
82
|
+
/** When set, matches are ordered by proximity to the anchor (and filtered by maxDistance). */
|
|
83
|
+
proximity?: {
|
|
84
|
+
anchor: Locator;
|
|
85
|
+
maxDistance?: number;
|
|
86
|
+
} | undefined);
|
|
80
87
|
private attribute;
|
|
81
88
|
/** Node tags this selector matches in `source`, in document order (role- or attribute-based). */
|
|
82
89
|
private nodesIn;
|
|
83
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* All node tags this selector matches in the current source — document order, or, when a
|
|
92
|
+
* `near` anchor is set, ordered nearest-first by bounds-centre distance (filtered by maxDistance).
|
|
93
|
+
*/
|
|
84
94
|
private matchedNodes;
|
|
85
95
|
/** The single node this locator resolves to (the nth match, or the first when unindexed). */
|
|
86
96
|
private pick;
|
|
87
97
|
/** A locator scoped to the nth match (0-based; negative counts from the end). */
|
|
88
98
|
nth(index: number): Locator;
|
|
99
|
+
/**
|
|
100
|
+
* Scope to the match nearest `anchor` (by bounds-centre distance) — the relative locator for
|
|
101
|
+
* native: `getByRole("checkbox").near(getByText("Wi-Fi"))` is the checkbox in the Wi-Fi row.
|
|
102
|
+
* `maxDistance` (px) drops matches farther than that, so an absent control resolves to nothing.
|
|
103
|
+
*/
|
|
104
|
+
near(anchor: Locator, options?: {
|
|
105
|
+
maxDistance?: number;
|
|
106
|
+
}): Locator;
|
|
89
107
|
/** A locator scoped to the first match. */
|
|
90
108
|
first(): Locator;
|
|
91
109
|
/** A locator scoped to the last match. */
|
package/dist/locator.js
CHANGED
|
@@ -66,13 +66,17 @@ export class Locator {
|
|
|
66
66
|
selector;
|
|
67
67
|
options;
|
|
68
68
|
index;
|
|
69
|
+
proximity;
|
|
69
70
|
constructor(driver, selector, options = {},
|
|
70
71
|
/** When set, the locator resolves to the nth match (negative counts from the end). */
|
|
71
|
-
index
|
|
72
|
+
index,
|
|
73
|
+
/** When set, matches are ordered by proximity to the anchor (and filtered by maxDistance). */
|
|
74
|
+
proximity) {
|
|
72
75
|
this.driver = driver;
|
|
73
76
|
this.selector = selector;
|
|
74
77
|
this.options = options;
|
|
75
78
|
this.index = index;
|
|
79
|
+
this.proximity = proximity;
|
|
76
80
|
}
|
|
77
81
|
attribute() {
|
|
78
82
|
return attributeFor(this.selector, this.driver.platform);
|
|
@@ -83,9 +87,28 @@ export class Locator {
|
|
|
83
87
|
? nodesForRole(source, this.selector.value, this.driver.platform)
|
|
84
88
|
: nodesForAttribute(source, this.attribute(), this.selector.value);
|
|
85
89
|
}
|
|
86
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* All node tags this selector matches in the current source — document order, or, when a
|
|
92
|
+
* `near` anchor is set, ordered nearest-first by bounds-centre distance (filtered by maxDistance).
|
|
93
|
+
*/
|
|
87
94
|
async matchedNodes() {
|
|
88
|
-
|
|
95
|
+
const nodes = this.nodesIn(await this.driver.source());
|
|
96
|
+
if (!this.proximity)
|
|
97
|
+
return nodes;
|
|
98
|
+
const anchor = await this.proximity.anchor.bounds();
|
|
99
|
+
if (!anchor)
|
|
100
|
+
return [];
|
|
101
|
+
const { maxDistance } = this.proximity;
|
|
102
|
+
return nodes
|
|
103
|
+
.map((node) => ({ node, bounds: parseBounds(/bounds="([^"]+)"/.exec(node)?.[1]) }))
|
|
104
|
+
.filter((entry) => entry.bounds !== null)
|
|
105
|
+
.map((entry) => ({
|
|
106
|
+
node: entry.node,
|
|
107
|
+
distance: Math.hypot(entry.bounds.centerX - anchor.centerX, entry.bounds.centerY - anchor.centerY),
|
|
108
|
+
}))
|
|
109
|
+
.filter((entry) => maxDistance === undefined || entry.distance <= maxDistance)
|
|
110
|
+
.sort((a, b) => a.distance - b.distance)
|
|
111
|
+
.map((entry) => entry.node);
|
|
89
112
|
}
|
|
90
113
|
/** The single node this locator resolves to (the nth match, or the first when unindexed). */
|
|
91
114
|
pick(nodes) {
|
|
@@ -96,7 +119,16 @@ export class Locator {
|
|
|
96
119
|
}
|
|
97
120
|
/** A locator scoped to the nth match (0-based; negative counts from the end). */
|
|
98
121
|
nth(index) {
|
|
99
|
-
return new Locator(this.driver, this.selector, this.options, index);
|
|
122
|
+
return new Locator(this.driver, this.selector, this.options, index, this.proximity);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Scope to the match nearest `anchor` (by bounds-centre distance) — the relative locator for
|
|
126
|
+
* native: `getByRole("checkbox").near(getByText("Wi-Fi"))` is the checkbox in the Wi-Fi row.
|
|
127
|
+
* `maxDistance` (px) drops matches farther than that, so an absent control resolves to nothing.
|
|
128
|
+
*/
|
|
129
|
+
near(anchor, options = {}) {
|
|
130
|
+
const proximity = options.maxDistance === undefined ? { anchor } : { anchor, maxDistance: options.maxDistance };
|
|
131
|
+
return new Locator(this.driver, this.selector, this.options, this.index, proximity);
|
|
100
132
|
}
|
|
101
133
|
/** A locator scoped to the first match. */
|
|
102
134
|
first() {
|
package/package.json
CHANGED