react-native-tapline 1.0.0 → 1.2.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/dist/actions.d.ts +43 -0
- package/dist/actions.js +110 -2
- package/dist/actions.js.map +1 -1
- package/dist/agent.d.ts +38 -0
- package/dist/agent.js +98 -1
- package/dist/agent.js.map +1 -1
- package/dist/checks.d.ts +16 -0
- package/dist/checks.js +186 -3
- package/dist/checks.js.map +1 -1
- package/dist/client.js +2 -2
- package/dist/client.js.map +1 -1
- package/dist/index.js +50 -2
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +4 -0
- package/dist/inspect.js +12 -1
- package/dist/inspect.js.map +1 -1
- package/dist/methods.js +2 -0
- package/dist/methods.js.map +1 -1
- package/dist/source.d.ts +60 -0
- package/dist/source.js +214 -0
- package/dist/source.js.map +1 -0
- package/metro.d.ts +17 -1
- package/metro.js +107 -1
- package/package.json +2 -2
package/dist/actions.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type Entry, type Snapshot } from './inspect';
|
|
2
|
+
import { type ElementSource } from './source';
|
|
3
|
+
import { type AuditType } from './checks';
|
|
2
4
|
import { type Fiber } from './fiber';
|
|
3
5
|
import { type LogEntry, type TaplineElement } from './types';
|
|
4
6
|
export { tree } from './inspect';
|
|
@@ -65,6 +67,7 @@ export declare function describeHit(snapshot: Snapshot, hit: Fiber, actor?: Entr
|
|
|
65
67
|
export declare function back(rawParams: unknown): Promise<{
|
|
66
68
|
route: string[];
|
|
67
69
|
closed?: 'sheet';
|
|
70
|
+
dryRun?: true;
|
|
68
71
|
} & SettleOutcome>;
|
|
69
72
|
/**
|
|
70
73
|
* What `press` and `longPress` answer: the element, `closedKeyboard: true` when the keyboard was
|
|
@@ -74,6 +77,11 @@ export type PressResult = {
|
|
|
74
77
|
element: TaplineElement;
|
|
75
78
|
closedKeyboard?: true;
|
|
76
79
|
forced?: string;
|
|
80
|
+
dryRun?: true;
|
|
81
|
+
point?: {
|
|
82
|
+
x: number;
|
|
83
|
+
y: number;
|
|
84
|
+
};
|
|
77
85
|
} & SettleOutcome;
|
|
78
86
|
export declare function press(params: unknown): Promise<PressResult>;
|
|
79
87
|
export declare function longPress(params: unknown): Promise<PressResult>;
|
|
@@ -100,8 +108,10 @@ export declare function setKeyboardControl(next: KeyboardControl | null): void;
|
|
|
100
108
|
export declare function typeText(rawParams: unknown): Promise<{
|
|
101
109
|
element: TaplineElement;
|
|
102
110
|
value: string;
|
|
111
|
+
secure?: true;
|
|
103
112
|
closedKeyboard?: true;
|
|
104
113
|
forced?: string;
|
|
114
|
+
dryRun?: true;
|
|
105
115
|
} & SettleOutcome>;
|
|
106
116
|
/** What `scroll` answers; `target`, `reached` and `unchanged` only with `to: "target"`. */
|
|
107
117
|
export type ScrollResult = {
|
|
@@ -118,6 +128,8 @@ export type ScrollResult = {
|
|
|
118
128
|
reached?: boolean;
|
|
119
129
|
/** The element was clear already: nothing was scrolled. */
|
|
120
130
|
unchanged?: true;
|
|
131
|
+
/** A dry run (AGENT-TOOLS 1.8 §11.4): resolved and checked, nothing scrolled. */
|
|
132
|
+
dryRun?: true;
|
|
121
133
|
} & SettleOutcome;
|
|
122
134
|
export declare function scroll(rawParams: unknown): Promise<ScrollResult>;
|
|
123
135
|
export declare function toggle(rawParams: unknown): Promise<{
|
|
@@ -126,8 +138,38 @@ export declare function toggle(rawParams: unknown): Promise<{
|
|
|
126
138
|
closedKeyboard?: true;
|
|
127
139
|
unchanged?: true;
|
|
128
140
|
forced?: string;
|
|
141
|
+
dryRun?: true;
|
|
142
|
+
point?: {
|
|
143
|
+
x: number;
|
|
144
|
+
y: number;
|
|
145
|
+
};
|
|
129
146
|
} & SettleOutcome>;
|
|
130
147
|
export declare function openURL(rawParams: unknown): Promise<SettleOutcome>;
|
|
148
|
+
/**
|
|
149
|
+
* `inspect {target}` (Tapline 1.1, development builds): the element, the components that draw it, the line
|
|
150
|
+
* of the app's code that wrote it, and that component's props in short. From the screen to the code.
|
|
151
|
+
*/
|
|
152
|
+
export declare function inspect(rawParams: unknown): Promise<{
|
|
153
|
+
element: TaplineElement;
|
|
154
|
+
source: ElementSource;
|
|
155
|
+
}>;
|
|
156
|
+
export interface AuditIssue {
|
|
157
|
+
type: AuditType;
|
|
158
|
+
element: TaplineElement | null;
|
|
159
|
+
problem: string;
|
|
160
|
+
fix: string;
|
|
161
|
+
/** Where the app's code wrote the element (development builds), as Metro names the file. */
|
|
162
|
+
at?: {
|
|
163
|
+
file: string;
|
|
164
|
+
line: number;
|
|
165
|
+
direct: boolean;
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** `audit {types?}` (AGENT-TOOLS 1.8 §10): the screen's accessibility problems, each with its source line in development. */
|
|
169
|
+
export declare function audit(rawParams: unknown): Promise<{
|
|
170
|
+
issues: AuditIssue[];
|
|
171
|
+
checked: AuditType[];
|
|
172
|
+
}>;
|
|
131
173
|
export declare function getLogs(rawParams: unknown): Promise<{
|
|
132
174
|
entries: LogEntry[];
|
|
133
175
|
}>;
|
|
@@ -157,4 +199,5 @@ export declare function swipe(rawParams: unknown): Promise<{
|
|
|
157
199
|
x: number;
|
|
158
200
|
y: number;
|
|
159
201
|
};
|
|
202
|
+
dryRun?: true;
|
|
160
203
|
} & SettleOutcome>;
|
package/dist/actions.js
CHANGED
|
@@ -17,6 +17,8 @@ exports.typeText = typeText;
|
|
|
17
17
|
exports.scroll = scroll;
|
|
18
18
|
exports.toggle = toggle;
|
|
19
19
|
exports.openURL = openURL;
|
|
20
|
+
exports.inspect = inspect;
|
|
21
|
+
exports.audit = audit;
|
|
20
22
|
exports.getLogs = getLogs;
|
|
21
23
|
exports.reload = reload;
|
|
22
24
|
exports.screenshotAvailable = screenshotAvailable;
|
|
@@ -26,6 +28,8 @@ const react_native_1 = require("react-native");
|
|
|
26
28
|
const keyboard_1 = require("./keyboard");
|
|
27
29
|
const native_1 = require("./native");
|
|
28
30
|
const inspect_1 = require("./inspect");
|
|
31
|
+
const source_1 = require("./source");
|
|
32
|
+
const checks_1 = require("./checks");
|
|
29
33
|
const alerts_1 = require("./alerts");
|
|
30
34
|
const fiber_1 = require("./fiber");
|
|
31
35
|
const logs_1 = require("./logs");
|
|
@@ -774,7 +778,7 @@ function requestCloseOf(host) {
|
|
|
774
778
|
}
|
|
775
779
|
/** `back`: what the back button or gesture does, on the focused screen; with a sheet open, closes the sheet. */
|
|
776
780
|
async function back(rawParams) {
|
|
777
|
-
paramsOf(rawParams);
|
|
781
|
+
const dry = paramsOf(rawParams).dryRun === true;
|
|
778
782
|
const snapshot = (0, inspect_1.takeSnapshot)();
|
|
779
783
|
const current = (0, inspect_1.currentRoute)(snapshot.routes);
|
|
780
784
|
// An open sheet is what back closes first (AGENT-TOOLS v1.4): the top Modal's own onRequestClose,
|
|
@@ -786,6 +790,8 @@ async function back(rawParams) {
|
|
|
786
790
|
// a person taps that, so back does.
|
|
787
791
|
const backdrop = onRequestClose ? null : await backdropOf(snapshot);
|
|
788
792
|
if (backdrop) {
|
|
793
|
+
if (dry)
|
|
794
|
+
return { route: current?.chain ?? [], closed: 'sheet', dryRun: true, settledMs: 0 };
|
|
789
795
|
callHandler('onPress', backdrop.onPress, [pressEvent(backdrop.entry.node.fiber, backdrop.frame)]);
|
|
790
796
|
return { route: current?.chain ?? [], closed: 'sheet', ...(await settleOutcome()) };
|
|
791
797
|
}
|
|
@@ -794,6 +800,8 @@ async function back(rawParams) {
|
|
|
794
800
|
const shows = await frontLine(snapshot);
|
|
795
801
|
throw notActionable('no_action', `the open sheet${shows !== undefined ? ` (in front: ${shows})` : ''} has nothing for back to call (its Modal has no onRequestClose); tap its close control.`);
|
|
796
802
|
}
|
|
803
|
+
if (dry)
|
|
804
|
+
return { route: current?.chain ?? [], closed: 'sheet', dryRun: true, settledMs: 0 };
|
|
797
805
|
callHandler('onRequestClose', onRequestClose, []);
|
|
798
806
|
return { route: current?.chain ?? [], closed: 'sheet', ...(await settleOutcome()) };
|
|
799
807
|
}
|
|
@@ -812,6 +820,8 @@ async function back(rawParams) {
|
|
|
812
820
|
if (!canGoBack || typeof nav.goBack !== 'function') {
|
|
813
821
|
throw notActionable('no_action', `Nothing to go back to from ${current.chain.join(' › ')}.`);
|
|
814
822
|
}
|
|
823
|
+
if (dry)
|
|
824
|
+
return { route: current.chain, dryRun: true, settledMs: 0 };
|
|
815
825
|
callHandler('goBack', nav.goBack, []);
|
|
816
826
|
return { route: current.chain, ...(await settleOutcome()) };
|
|
817
827
|
}
|
|
@@ -890,10 +900,14 @@ async function pressLike(kind, rawParams) {
|
|
|
890
900
|
if (entry.tab) {
|
|
891
901
|
if (kind === 'longPress')
|
|
892
902
|
throw notActionable('no_action', `${describe(entry)} has no long-press action`);
|
|
903
|
+
if (params.dryRun === true)
|
|
904
|
+
return { element: (0, inspect_1.toElement)(entry, null), dryRun: true, settledMs: 0 };
|
|
893
905
|
selectTab(snapshot, entry.tab);
|
|
894
906
|
return { element: (0, inspect_1.toElement)(entry, null), ...keyboardNote(closeKeyboardFor(entry)), ...(await settleOutcome()) };
|
|
895
907
|
}
|
|
896
908
|
if (entry.navBack) {
|
|
909
|
+
if (params.dryRun === true)
|
|
910
|
+
return { element: (0, inspect_1.toElement)(entry, null), dryRun: true, settledMs: 0 };
|
|
897
911
|
const { route: _route, ...outcome } = await back({});
|
|
898
912
|
return { element: (0, inspect_1.toElement)(entry, null), ...outcome };
|
|
899
913
|
}
|
|
@@ -916,6 +930,9 @@ async function pressLike(kind, rawParams) {
|
|
|
916
930
|
}
|
|
917
931
|
const { element, frame } = await elementFor(actor);
|
|
918
932
|
const forced = await checkedOrForced(params.force === true, () => assertReachable(snapshot, actor, frame));
|
|
933
|
+
// A dry run (AGENT-TOOLS 1.8 §11.4): every check ran; nothing is touched.
|
|
934
|
+
if (params.dryRun === true)
|
|
935
|
+
return { element, dryRun: true, ...pointOf(frame), ...forced, settledMs: 0 };
|
|
919
936
|
const closedKeyboard = closeKeyboardFor(actor);
|
|
920
937
|
if (handler)
|
|
921
938
|
callHandler(handlerName, handler, [pressEvent(actor.node.fiber, frame)]);
|
|
@@ -929,6 +946,10 @@ async function pressLike(kind, rawParams) {
|
|
|
929
946
|
}
|
|
930
947
|
return { element, ...keyboardNote(closedKeyboard), ...forced, ...(await settleOutcome()) };
|
|
931
948
|
}
|
|
949
|
+
/** Where a finger would land: the middle of the element (points, rounded). */
|
|
950
|
+
function pointOf(frame) {
|
|
951
|
+
return frame ? { point: { x: Math.round(frame.x + frame.width / 2), y: Math.round(frame.y + frame.height / 2) } } : {};
|
|
952
|
+
}
|
|
932
953
|
function press(params) {
|
|
933
954
|
return pressLike('press', params);
|
|
934
955
|
}
|
|
@@ -1112,6 +1133,10 @@ async function typeText(rawParams) {
|
|
|
1112
1133
|
await assertReachable(snapshot, entry, await (0, inspect_1.measureFiber)(entry.node.fiber));
|
|
1113
1134
|
}
|
|
1114
1135
|
});
|
|
1136
|
+
if (params.dryRun === true) {
|
|
1137
|
+
const { element } = await elementFor(entry);
|
|
1138
|
+
return { element, value: (0, inspect_1.displayText)((0, inspect_1.currentInputValue)(entry.node)), dryRun: true, ...forced, settledMs: 0 };
|
|
1139
|
+
}
|
|
1115
1140
|
// Typing into the input that has focus leaves the keyboard alone. Another input above the keyboard
|
|
1116
1141
|
// takes the focus with the keyboard up; one the keyboard covers closes it first.
|
|
1117
1142
|
const closedKeyboard = !(await moveFocusAboveKeyboard(entry)) && closeKeyboardFor(entry);
|
|
@@ -1146,7 +1171,19 @@ async function typeText(rawParams) {
|
|
|
1146
1171
|
}
|
|
1147
1172
|
const outcome = await settleOutcome();
|
|
1148
1173
|
const { element } = await elementFor(entry);
|
|
1149
|
-
|
|
1174
|
+
// A password field's text never leaves the app: one bullet per character, as iOS draws it.
|
|
1175
|
+
const secure = (0, inspect_1.isSecureInput)(entry.node);
|
|
1176
|
+
const shown = secure ? (0, inspect_1.maskSecret)(next) : (0, inspect_1.displayText)(next);
|
|
1177
|
+
// An input that now holds text shows no placeholder (AGENT-TOOLS v1.7).
|
|
1178
|
+
const { placeholder, ...rest } = element;
|
|
1179
|
+
return {
|
|
1180
|
+
element: { ...rest, ...(next === '' && placeholder !== undefined ? { placeholder } : {}), value: shown },
|
|
1181
|
+
value: shown,
|
|
1182
|
+
...(secure ? { secure: true } : {}),
|
|
1183
|
+
...keyboardNote(closedKeyboard),
|
|
1184
|
+
...forced,
|
|
1185
|
+
...outcome,
|
|
1186
|
+
};
|
|
1150
1187
|
}
|
|
1151
1188
|
const rememberedOffsets = new WeakMap();
|
|
1152
1189
|
/** Objects that may expose scroll methods: host public instances first, then list class instances. */
|
|
@@ -1241,6 +1278,13 @@ async function scrollToTarget(params) {
|
|
|
1241
1278
|
break;
|
|
1242
1279
|
p = node.parent;
|
|
1243
1280
|
}
|
|
1281
|
+
if (params.dryRun === true) {
|
|
1282
|
+
const first = scrollers[0];
|
|
1283
|
+
const target = (await elementFor(wanted)).element;
|
|
1284
|
+
if (!first)
|
|
1285
|
+
return { element: target, target, offset: { x: 0, y: 0 }, moved: false, dryRun: true, settledMs: 0 };
|
|
1286
|
+
return { element: (await elementFor(first)).element, target, offset: { x: 0, y: 0 }, moved: false, dryRun: true, settledMs: 0 };
|
|
1287
|
+
}
|
|
1244
1288
|
const { width, height } = snapshot.screen;
|
|
1245
1289
|
const navBar = (0, inspect_1.nativeNavBar)(snapshot);
|
|
1246
1290
|
const tabBar = (0, inspect_1.nativeTabBar)(snapshot);
|
|
@@ -1426,6 +1470,10 @@ async function scroll(rawParams) {
|
|
|
1426
1470
|
entry = best;
|
|
1427
1471
|
frame = bestFrame;
|
|
1428
1472
|
}
|
|
1473
|
+
if (params.dryRun === true) {
|
|
1474
|
+
const { element } = await elementFor(entry, frame);
|
|
1475
|
+
return { element, offset: { x: 0, y: 0 }, moved: false, dryRun: true, settledMs: 0 };
|
|
1476
|
+
}
|
|
1429
1477
|
const candidates = scrollCandidates(entry);
|
|
1430
1478
|
const key = rememberKey(entry, candidates);
|
|
1431
1479
|
const horizontal = (0, inspect_1.findProp)(entry.node, 'horizontal') === true;
|
|
@@ -1579,6 +1627,10 @@ async function toggle(rawParams) {
|
|
|
1579
1627
|
const current = control.info.checked === true;
|
|
1580
1628
|
const next = typeof params.value === 'boolean' ? params.value : !current;
|
|
1581
1629
|
const forced = await checkedOrForced(params.force === true, async () => assertReachable(snapshot, control, await (0, inspect_1.measureFiber)(control.node.fiber)));
|
|
1630
|
+
if (params.dryRun === true) {
|
|
1631
|
+
const { element, frame } = await elementFor(control);
|
|
1632
|
+
return { element, value: next, dryRun: true, ...pointOf(frame), ...forced, settledMs: 0 };
|
|
1633
|
+
}
|
|
1582
1634
|
if (typeof params.value === 'boolean' && control.info.checked === params.value) {
|
|
1583
1635
|
// Already there: a person wouldn't touch it, so the handler isn't called and the keyboard stays.
|
|
1584
1636
|
const { element } = await elementFor(control);
|
|
@@ -1617,6 +1669,56 @@ async function openURL(rawParams) {
|
|
|
1617
1669
|
}
|
|
1618
1670
|
return settleOutcome();
|
|
1619
1671
|
}
|
|
1672
|
+
/**
|
|
1673
|
+
* `inspect {target}` (Tapline 1.1, development builds): the element, the components that draw it, the line
|
|
1674
|
+
* of the app's code that wrote it, and that component's props in short. From the screen to the code.
|
|
1675
|
+
*/
|
|
1676
|
+
async function inspect(rawParams) {
|
|
1677
|
+
if (typeof __DEV__ === 'undefined' || !__DEV__) {
|
|
1678
|
+
throw new types_1.TaplineError('unsupported', 'inspect needs a development build: release builds keep no source locations');
|
|
1679
|
+
}
|
|
1680
|
+
const params = paramsOf(rawParams);
|
|
1681
|
+
const { entry } = await resolveTarget(params.target);
|
|
1682
|
+
const [source] = await (0, source_1.sourcesOf)([entry.node.fiber]);
|
|
1683
|
+
const { element } = await elementFor(entry);
|
|
1684
|
+
return { element, source: source ?? { components: [], location: null, props: {} } };
|
|
1685
|
+
}
|
|
1686
|
+
/** `audit {types?}` (AGENT-TOOLS 1.8 §10): the screen's accessibility problems, each with its source line in development. */
|
|
1687
|
+
async function audit(rawParams) {
|
|
1688
|
+
const params = paramsOf(rawParams);
|
|
1689
|
+
const asked = params.types;
|
|
1690
|
+
if (asked !== undefined && (!Array.isArray(asked) || asked.some((t) => typeof t !== 'string'))) {
|
|
1691
|
+
throw badParams('types must be a list of audit types');
|
|
1692
|
+
}
|
|
1693
|
+
const known = new Set(checks_1.AUDIT_TYPES);
|
|
1694
|
+
const types = new Set(asked === undefined ? checks_1.AUDIT_TYPES : checks_1.AUDIT_TYPES.filter((t) => asked.includes(t) && known.has(t)));
|
|
1695
|
+
const findings = await (0, checks_1.auditScreen)(types);
|
|
1696
|
+
let sources = [];
|
|
1697
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
1698
|
+
const fibers = findings.flatMap((f) => (f.entry !== null ? [f.entry.node.fiber] : []));
|
|
1699
|
+
try {
|
|
1700
|
+
sources = fibers.length > 0 ? await (0, source_1.sourcesOf)(fibers) : [];
|
|
1701
|
+
}
|
|
1702
|
+
catch {
|
|
1703
|
+
sources = []; // Metro unreachable: the report still stands, without lines
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
let next = 0;
|
|
1707
|
+
const issues = [];
|
|
1708
|
+
for (const finding of findings) {
|
|
1709
|
+
const source = finding.entry !== null ? sources[next++] : undefined;
|
|
1710
|
+
const element = finding.entry !== null ? (await elementFor(finding.entry, finding.frame)).element : null;
|
|
1711
|
+
const location = source?.location ?? null;
|
|
1712
|
+
issues.push({
|
|
1713
|
+
type: finding.type,
|
|
1714
|
+
element,
|
|
1715
|
+
problem: finding.problem,
|
|
1716
|
+
fix: finding.fix,
|
|
1717
|
+
...(location !== null ? { at: { file: location.file, line: location.line, direct: location.direct } } : {}),
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
return { issues, checked: [...types] };
|
|
1721
|
+
}
|
|
1620
1722
|
async function getLogs(rawParams) {
|
|
1621
1723
|
const params = paramsOf(rawParams);
|
|
1622
1724
|
if (params.since !== undefined && typeof params.since !== 'number')
|
|
@@ -1802,6 +1904,12 @@ async function swipe(rawParams) {
|
|
|
1802
1904
|
throw notActionable('no_action', `Nothing at (${x0}, ${y0}) to swipe on.`);
|
|
1803
1905
|
const origin = start;
|
|
1804
1906
|
const chain = hostChain(origin);
|
|
1907
|
+
if (params.dryRun === true) {
|
|
1908
|
+
// Who would take the drag is only known by asking the views, which can act on it: a dry run names what lies under the start.
|
|
1909
|
+
const under = snapshot.entries.filter((entry) => chain.some((fiber) => (0, fiber_1.sameFiber)(fiber, entry.node.fiber))).pop();
|
|
1910
|
+
const element = under ? (await elementFor(under)).element : null;
|
|
1911
|
+
return { element, from: { x: x0, y: y0 }, to: { x: x1, y: y1 }, dryRun: true, settledMs: 0 };
|
|
1912
|
+
}
|
|
1805
1913
|
const begin = Date.now();
|
|
1806
1914
|
const record = {
|
|
1807
1915
|
touchActive: true,
|