execbro 2.1.2 → 2.3.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.
Files changed (138) hide show
  1. package/README.md +4 -3
  2. package/build/__tests__/integration/text-input.test.d.ts +2 -0
  3. package/build/__tests__/integration/text-input.test.d.ts.map +1 -0
  4. package/build/__tests__/integration/text-input.test.js +181 -0
  5. package/build/__tests__/integration/text-input.test.js.map +1 -0
  6. package/build/__tests__/unit/focusedInputTools.test.js +19 -6
  7. package/build/__tests__/unit/focusedInputTools.test.js.map +1 -1
  8. package/build/__tests__/unit/inputTarget.test.d.ts +2 -0
  9. package/build/__tests__/unit/inputTarget.test.d.ts.map +1 -0
  10. package/build/__tests__/unit/inputTarget.test.js +144 -0
  11. package/build/__tests__/unit/inputTarget.test.js.map +1 -0
  12. package/build/__tests__/unit/inputTargetTools.test.d.ts +2 -0
  13. package/build/__tests__/unit/inputTargetTools.test.d.ts.map +1 -0
  14. package/build/__tests__/unit/inputTargetTools.test.js +46 -0
  15. package/build/__tests__/unit/inputTargetTools.test.js.map +1 -0
  16. package/build/__tests__/unit/inputTextTool.test.d.ts +2 -0
  17. package/build/__tests__/unit/inputTextTool.test.d.ts.map +1 -0
  18. package/build/__tests__/unit/inputTextTool.test.js +120 -0
  19. package/build/__tests__/unit/inputTextTool.test.js.map +1 -0
  20. package/build/__tests__/unit/keyboardMetrics.test.d.ts +2 -0
  21. package/build/__tests__/unit/keyboardMetrics.test.d.ts.map +1 -0
  22. package/build/__tests__/unit/keyboardMetrics.test.js +58 -0
  23. package/build/__tests__/unit/keyboardMetrics.test.js.map +1 -0
  24. package/build/__tests__/unit/keyboardRaise.test.d.ts +2 -0
  25. package/build/__tests__/unit/keyboardRaise.test.d.ts.map +1 -0
  26. package/build/__tests__/unit/keyboardRaise.test.js +74 -0
  27. package/build/__tests__/unit/keyboardRaise.test.js.map +1 -0
  28. package/build/__tests__/unit/nativeInputValue.test.d.ts +2 -0
  29. package/build/__tests__/unit/nativeInputValue.test.d.ts.map +1 -0
  30. package/build/__tests__/unit/nativeInputValue.test.js +67 -0
  31. package/build/__tests__/unit/nativeInputValue.test.js.map +1 -0
  32. package/build/__tests__/unit/screenSpace.test.d.ts +2 -0
  33. package/build/__tests__/unit/screenSpace.test.d.ts.map +1 -0
  34. package/build/__tests__/unit/screenSpace.test.js +58 -0
  35. package/build/__tests__/unit/screenSpace.test.js.map +1 -0
  36. package/build/__tests__/unit/screenStateInputValue.test.d.ts +2 -0
  37. package/build/__tests__/unit/screenStateInputValue.test.d.ts.map +1 -0
  38. package/build/__tests__/unit/screenStateInputValue.test.js +49 -0
  39. package/build/__tests__/unit/screenStateInputValue.test.js.map +1 -0
  40. package/build/__tests__/unit/screenStateKeyboard.test.d.ts +2 -0
  41. package/build/__tests__/unit/screenStateKeyboard.test.d.ts.map +1 -0
  42. package/build/__tests__/unit/screenStateKeyboard.test.js +99 -0
  43. package/build/__tests__/unit/screenStateKeyboard.test.js.map +1 -0
  44. package/build/__tests__/unit/textEntry.test.d.ts +2 -0
  45. package/build/__tests__/unit/textEntry.test.d.ts.map +1 -0
  46. package/build/__tests__/unit/textEntry.test.js +291 -0
  47. package/build/__tests__/unit/textEntry.test.js.map +1 -0
  48. package/build/__tests__/unit/visibility.test.js +21 -0
  49. package/build/__tests__/unit/visibility.test.js.map +1 -1
  50. package/build/core/focusedInputTools.d.ts.map +1 -1
  51. package/build/core/focusedInputTools.js +13 -35
  52. package/build/core/focusedInputTools.js.map +1 -1
  53. package/build/core/guides.d.ts.map +1 -1
  54. package/build/core/guides.js +18 -4
  55. package/build/core/guides.js.map +1 -1
  56. package/build/core/index.d.ts +1 -1
  57. package/build/core/index.d.ts.map +1 -1
  58. package/build/core/index.js +1 -1
  59. package/build/core/index.js.map +1 -1
  60. package/build/core/injected/visibility.d.ts +14 -1
  61. package/build/core/injected/visibility.d.ts.map +1 -1
  62. package/build/core/injected/visibility.js +28 -4
  63. package/build/core/injected/visibility.js.map +1 -1
  64. package/build/core/injectedFilters.d.ts +31 -0
  65. package/build/core/injectedFilters.d.ts.map +1 -0
  66. package/build/core/injectedFilters.js +31 -0
  67. package/build/core/injectedFilters.js.map +1 -0
  68. package/build/core/inputTarget.d.ts +85 -0
  69. package/build/core/inputTarget.d.ts.map +1 -0
  70. package/build/core/inputTarget.js +364 -0
  71. package/build/core/inputTarget.js.map +1 -0
  72. package/build/core/inputTargetTools.d.ts +12 -0
  73. package/build/core/inputTargetTools.d.ts.map +1 -0
  74. package/build/core/inputTargetTools.js +26 -0
  75. package/build/core/inputTargetTools.js.map +1 -0
  76. package/build/core/inspector.d.ts +6 -0
  77. package/build/core/inspector.d.ts.map +1 -1
  78. package/build/core/inspector.js +200 -36
  79. package/build/core/inspector.js.map +1 -1
  80. package/build/core/ios.d.ts.map +1 -1
  81. package/build/core/ios.js +7 -1
  82. package/build/core/ios.js.map +1 -1
  83. package/build/core/keyboardMetrics.d.ts +11 -0
  84. package/build/core/keyboardMetrics.d.ts.map +1 -0
  85. package/build/core/keyboardMetrics.js +40 -0
  86. package/build/core/keyboardMetrics.js.map +1 -0
  87. package/build/core/keyboardRaise.d.ts +22 -0
  88. package/build/core/keyboardRaise.d.ts.map +1 -0
  89. package/build/core/keyboardRaise.js +74 -0
  90. package/build/core/keyboardRaise.js.map +1 -0
  91. package/build/core/nativeInputValue.d.ts +48 -0
  92. package/build/core/nativeInputValue.d.ts.map +1 -0
  93. package/build/core/nativeInputValue.js +113 -0
  94. package/build/core/nativeInputValue.js.map +1 -0
  95. package/build/core/pressables.d.ts +0 -32
  96. package/build/core/pressables.d.ts.map +1 -1
  97. package/build/core/pressables.js +5 -1061
  98. package/build/core/pressables.js.map +1 -1
  99. package/build/core/screenLayout.d.ts +3 -0
  100. package/build/core/screenLayout.d.ts.map +1 -1
  101. package/build/core/screenLayout.js +32 -0
  102. package/build/core/screenLayout.js.map +1 -1
  103. package/build/core/screenSpace.d.ts +75 -0
  104. package/build/core/screenSpace.d.ts.map +1 -0
  105. package/build/core/screenSpace.js +84 -0
  106. package/build/core/screenSpace.js.map +1 -0
  107. package/build/core/screenSpaceDevice.d.ts +15 -0
  108. package/build/core/screenSpaceDevice.d.ts.map +1 -0
  109. package/build/core/screenSpaceDevice.js +24 -0
  110. package/build/core/screenSpaceDevice.js.map +1 -0
  111. package/build/core/screenState.d.ts +39 -0
  112. package/build/core/screenState.d.ts.map +1 -1
  113. package/build/core/screenState.js +200 -16
  114. package/build/core/screenState.js.map +1 -1
  115. package/build/core/textEntry.d.ts +75 -0
  116. package/build/core/textEntry.d.ts.map +1 -0
  117. package/build/core/textEntry.js +252 -0
  118. package/build/core/textEntry.js.map +1 -0
  119. package/build/tools/componentTools.d.ts.map +1 -1
  120. package/build/tools/componentTools.js +63 -188
  121. package/build/tools/componentTools.js.map +1 -1
  122. package/build/tools/interactionTools.d.ts +13 -0
  123. package/build/tools/interactionTools.d.ts.map +1 -1
  124. package/build/tools/interactionTools.js +112 -2
  125. package/build/tools/interactionTools.js.map +1 -1
  126. package/build/tools/screenshotTools.d.ts.map +1 -1
  127. package/build/tools/screenshotTools.js +37 -72
  128. package/build/tools/screenshotTools.js.map +1 -1
  129. package/package.json +1 -1
  130. package/tools.json +10 -4
  131. package/build/__tests__/unit/focusedInput.test.d.ts +0 -2
  132. package/build/__tests__/unit/focusedInput.test.d.ts.map +0 -1
  133. package/build/__tests__/unit/focusedInput.test.js +0 -58
  134. package/build/__tests__/unit/focusedInput.test.js.map +0 -1
  135. package/build/core/focusedInput.d.ts +0 -27
  136. package/build/core/focusedInput.d.ts.map +0 -1
  137. package/build/core/focusedInput.js +0 -110
  138. package/build/core/focusedInput.js.map +0 -1
@@ -0,0 +1,120 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { formatTextEntryResponse } from "../../tools/interactionTools.js";
3
+ describe("formatTextEntryResponse", () => {
4
+ it("reports the landed value and keyboard state on success", () => {
5
+ const r = formatTextEntryResponse({
6
+ success: true,
7
+ value: "a@b",
8
+ path: "react",
9
+ verified: true,
10
+ keyboard: { raised: true, changed: true }
11
+ });
12
+ expect(r.isError).toBeFalsy();
13
+ expect(r.content[0].text).toContain('"a@b"');
14
+ expect(r.content[0].text).toContain("keyboard: visible");
15
+ });
16
+ it("says when a retry was needed", () => {
17
+ const r = formatTextEntryResponse({
18
+ success: true,
19
+ value: "CASEB",
20
+ path: "react",
21
+ verified: true,
22
+ retried: true
23
+ });
24
+ expect(r.content[0].text).toContain("retried once");
25
+ });
26
+ it("marks a verified mismatch as an error naming sent and landed", () => {
27
+ const r = formatTextEntryResponse({
28
+ success: false,
29
+ sent: "CASEB",
30
+ landed: "CSEBA",
31
+ retried: true,
32
+ error: "text landed differently than it was sent"
33
+ });
34
+ expect(r.isError).toBe(true);
35
+ expect(r.content[0].text).toContain("CASEB");
36
+ expect(r.content[0].text).toContain("CSEBA");
37
+ });
38
+ it("lists the matching inputs when the target was ambiguous", () => {
39
+ const r = formatTextEntryResponse({
40
+ success: false,
41
+ ambiguous: true,
42
+ error: "7 inputs match this target",
43
+ candidates: [
44
+ { index: 0, component: "FormInput", label: "Title *", placeholder: "Type here", value: "", testID: "new-topic-title" },
45
+ { index: 1, component: "FormInput", label: "Goal", placeholder: "Type here", value: "", testID: "new-topic-goal" }
46
+ ]
47
+ });
48
+ expect(r.isError).toBe(true);
49
+ const text = r.content[0].text;
50
+ expect(text).toContain("Title *");
51
+ expect(text).toContain("Goal");
52
+ expect(text).toContain("0:");
53
+ expect(text).toContain("1:");
54
+ expect(text).toContain("index");
55
+ });
56
+ it("does not set isError when only the keyboard raise failed", () => {
57
+ const r = formatTextEntryResponse({
58
+ success: true,
59
+ value: "hi",
60
+ path: "react",
61
+ verified: true,
62
+ keyboard: { raised: false, changed: false, reason: "osascript error 1002" }
63
+ });
64
+ expect(r.isError).toBeFalsy();
65
+ expect(r.content[0].text).toContain("1002");
66
+ });
67
+ it("flags an unverified success loudly without failing it", () => {
68
+ const r = formatTextEntryResponse({
69
+ success: true,
70
+ path: "hid",
71
+ verified: false,
72
+ error: "the field exposes no readable value, so the text could not be confirmed"
73
+ });
74
+ expect(r.isError).toBeFalsy();
75
+ // A caller skimming for success must not mistake this for a confirmed write.
76
+ expect(r.content[0].text).toContain("UNVERIFIED");
77
+ });
78
+ it("says how many inputs were left out of a capped list", () => {
79
+ // A cap that is not reported reads as "this is everything", which is how
80
+ // a caller concludes its field is absent when it is simply past the cut.
81
+ const r = formatTextEntryResponse({
82
+ success: false,
83
+ error: "no focused TextInput",
84
+ totalInputs: 19,
85
+ candidates: Array.from({ length: 12 }, (_, i) => ({
86
+ index: i,
87
+ component: "FormInput",
88
+ label: `field ${i}`,
89
+ placeholder: null,
90
+ value: null,
91
+ testID: null
92
+ }))
93
+ });
94
+ expect(r.content[0].text).toContain("showing 12 of 19");
95
+ });
96
+ it("does not claim truncation when the whole list is shown", () => {
97
+ const r = formatTextEntryResponse({
98
+ success: false,
99
+ error: "no focused TextInput",
100
+ totalInputs: 2,
101
+ candidates: [
102
+ { index: 0, component: "FormInput", label: "a", placeholder: null, value: null, testID: null },
103
+ { index: 1, component: "FormInput", label: "b", placeholder: null, value: null, testID: null }
104
+ ]
105
+ });
106
+ expect(r.content[0].text).not.toContain("showing");
107
+ });
108
+ it("lists inputs on screen when nothing matched at all", () => {
109
+ const r = formatTextEntryResponse({
110
+ success: false,
111
+ error: "no TextInput matched that target",
112
+ candidates: [
113
+ { index: 0, component: "FormInput", label: "Title *", placeholder: "Type here", value: null, testID: null }
114
+ ]
115
+ });
116
+ expect(r.content[0].text).toContain("inputs on screen");
117
+ expect(r.content[0].text).not.toContain("Re-run with index");
118
+ });
119
+ });
120
+ //# sourceMappingURL=inputTextTool.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputTextTool.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/inputTextTool.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=keyboardMetrics.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboardMetrics.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/keyboardMetrics.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,58 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { readKeyboardState } from "../../core/keyboardMetrics.js";
3
+ const exec = (payload) => async () => ({
4
+ success: true,
5
+ result: JSON.stringify(payload)
6
+ });
7
+ describe("readKeyboardState", () => {
8
+ it("reports a visible keyboard with its metrics", async () => {
9
+ const s = await readKeyboardState(undefined, exec({ visible: true, metrics: { height: 345, screenY: 567, width: 420 } }));
10
+ expect(s).toMatchObject({ visible: true, height: 345, screenY: 567, width: 420 });
11
+ expect(s.error).toBeUndefined();
12
+ });
13
+ it("handles metrics() returning undefined while hidden", async () => {
14
+ // Verified on device 2026-08-01: RN's Keyboard.metrics() is undefined when
15
+ // the keyboard is down, so the shape is checked rather than assumed.
16
+ const s = await readKeyboardState(undefined, exec({ visible: false }));
17
+ expect(s).toMatchObject({ visible: false, height: null, screenY: null, width: null });
18
+ expect(s.error).toBeUndefined();
19
+ });
20
+ it("degrades to not-visible with a reason when the module is unreachable", async () => {
21
+ const s = await readKeyboardState(undefined, exec({ error: "no Keyboard module" }));
22
+ expect(s.visible).toBe(false);
23
+ expect(s.error).toBe("no Keyboard module");
24
+ });
25
+ it("degrades when the executor itself fails", async () => {
26
+ const s = await readKeyboardState(undefined, async () => ({
27
+ success: false,
28
+ error: "No apps connected"
29
+ }));
30
+ expect(s.visible).toBe(false);
31
+ expect(s.error).toBe("No apps connected");
32
+ });
33
+ it("degrades when the payload is not parseable", async () => {
34
+ const s = await readKeyboardState(undefined, async () => ({ success: true, result: "not json" }));
35
+ expect(s.visible).toBe(false);
36
+ expect(s.error).toContain("parse");
37
+ });
38
+ it("reaches the Keyboard module through the injected require", async () => {
39
+ let seen = "";
40
+ const spy = async (expression) => {
41
+ seen = expression;
42
+ return { success: true, result: JSON.stringify({ visible: false }) };
43
+ };
44
+ await readKeyboardState(undefined, spy);
45
+ expect(seen).toContain("require('react-native')");
46
+ expect(seen).toContain("Keyboard");
47
+ });
48
+ it("threads the device argument through", async () => {
49
+ let seen;
50
+ const spy = async (_e, device) => {
51
+ seen = device;
52
+ return { success: true, result: JSON.stringify({ visible: false }) };
53
+ };
54
+ await readKeyboardState("iPhone Air", spy);
55
+ expect(seen).toBe("iPhone Air");
56
+ });
57
+ });
58
+ //# sourceMappingURL=keyboardMetrics.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboardMetrics.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/keyboardMetrics.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=keyboardRaise.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboardRaise.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/keyboardRaise.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import { describe, expect, it, jest } from "@jest/globals";
2
+ import { raiseKeyboard } from "../../core/keyboardRaise.js";
3
+ const visible = { visible: true, height: 345, screenY: 567, width: 420 };
4
+ const hidden = { visible: false, height: null, screenY: null, width: null };
5
+ function deps(states, over = {}) {
6
+ const queue = [...states];
7
+ const d = {
8
+ readState: jest.fn(async () => queue.shift() ?? states[states.length - 1]),
9
+ runOsascript: jest.fn(async () => ""),
10
+ runAdb: jest.fn(async () => ""),
11
+ delay: jest.fn(async () => { }),
12
+ ...over
13
+ };
14
+ return d;
15
+ }
16
+ describe("raiseKeyboard", () => {
17
+ it("does nothing when the keyboard is already up", async () => {
18
+ const d = deps([visible]);
19
+ const r = await raiseKeyboard("ios", "UDID", d);
20
+ expect(r).toEqual({ raised: true, changed: false });
21
+ // Firing the toggle here would HIDE it — the menu item toggles, not shows.
22
+ expect(d.runOsascript).not.toHaveBeenCalled();
23
+ });
24
+ it("iOS: activates Simulator first, and never touches ConnectHardwareKeyboard", async () => {
25
+ const d = deps([hidden, visible]);
26
+ const r = await raiseKeyboard("ios", "UDID", d);
27
+ expect(r).toEqual({ raised: true, changed: true });
28
+ const script = d.runOsascript.mock.calls[0][0];
29
+ expect(script).toContain('tell application "Simulator" to activate');
30
+ expect(script).toContain("Toggle Software Keyboard");
31
+ // Toggling the hardware keyboard would rewrite a persistent user setting.
32
+ expect(script).not.toContain("Connect Hardware Keyboard");
33
+ });
34
+ it("android: flips show_ime_with_hard_keyboard on the target device", async () => {
35
+ const d = deps([hidden, visible]);
36
+ await raiseKeyboard("android", "emulator-5554", d);
37
+ const args = d.runAdb.mock.calls[0][0];
38
+ expect(args.join(" ")).toContain("-s emulator-5554");
39
+ expect(args.join(" ")).toContain("show_ime_with_hard_keyboard 1");
40
+ });
41
+ it("reports a reason instead of throwing when osascript fails", async () => {
42
+ const d = deps([hidden], {
43
+ runOsascript: jest.fn(async () => {
44
+ throw new Error("osascript error 1002: not authorised to send Apple events");
45
+ })
46
+ });
47
+ const r = await raiseKeyboard("ios", "UDID", d);
48
+ expect(r.raised).toBe(false);
49
+ expect(r.reason).toContain("1002");
50
+ });
51
+ it("reports a reason when the CDP read fails across activate", async () => {
52
+ const d = deps([hidden, { ...hidden, error: "No apps connected" }]);
53
+ const r = await raiseKeyboard("ios", "UDID", d);
54
+ expect(r).toMatchObject({ raised: false, changed: true, reason: "No apps connected" });
55
+ });
56
+ it("does not claim success when the toggle ran but nothing appeared", async () => {
57
+ const d = deps([hidden, hidden]);
58
+ const r = await raiseKeyboard("ios", "UDID", d);
59
+ expect(r.raised).toBe(false);
60
+ expect(r.reason).toContain("did not appear");
61
+ });
62
+ it("bails out with a reason when the state cannot be read at all", async () => {
63
+ const d = deps([{ ...hidden, error: "Keyboard module unavailable" }]);
64
+ const r = await raiseKeyboard("android", "emulator-5554", d);
65
+ expect(r).toEqual({ raised: false, changed: false, reason: "Keyboard module unavailable" });
66
+ expect(d.runAdb).not.toHaveBeenCalled();
67
+ });
68
+ it("waits for the animation before re-reading visibility", async () => {
69
+ const d = deps([hidden, visible]);
70
+ await raiseKeyboard("ios", "UDID", d);
71
+ expect(d.delay).toHaveBeenCalled();
72
+ });
73
+ });
74
+ //# sourceMappingURL=keyboardRaise.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboardRaise.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/keyboardRaise.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=nativeInputValue.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nativeInputValue.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/nativeInputValue.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { parseIosFields, parseAndroidFields, resolveWrittenField } from "../../core/nativeInputValue.js";
3
+ describe("parseIosFields", () => {
4
+ it("reads AXUniqueId as the testID and AXValue as the text", () => {
5
+ const json = JSON.stringify({
6
+ children: [
7
+ { type: "TextField", AXUniqueId: "name-input", AXValue: "Alice" },
8
+ { type: "Other", children: [{ type: "TextField", AXUniqueId: null, AXValue: "Email" }] }
9
+ ]
10
+ });
11
+ expect(parseIosFields(json)).toEqual([
12
+ { id: "name-input", text: "Alice", focused: false },
13
+ { id: null, text: "Email", focused: false }
14
+ ]);
15
+ });
16
+ it("returns nothing rather than throwing on unparseable output", () => {
17
+ expect(parseIosFields("not json")).toEqual([]);
18
+ });
19
+ });
20
+ describe("parseAndroidFields", () => {
21
+ const node = (attrs) => `<node class="android.widget.EditText" ${attrs}/>`;
22
+ it("reads resource-id, text and focused", () => {
23
+ const xml = node('resource-id="name-input" text="Alice" hint="Enter name" focused="true"');
24
+ expect(parseAndroidFields(xml)).toEqual([{ id: "name-input", text: "Alice", focused: true }]);
25
+ });
26
+ it("treats text equal to the hint as empty", () => {
27
+ // An empty Android EditText reports its hint in `text`; taking that at
28
+ // face value would verify a write that never happened.
29
+ const xml = node('resource-id="email-input" text="Email" hint="Email" focused="false"');
30
+ expect(parseAndroidFields(xml)).toEqual([{ id: "email-input", text: "", focused: false }]);
31
+ });
32
+ it("ignores non-EditText nodes", () => {
33
+ expect(parseAndroidFields('<node class="android.widget.TextView" text="hi"/>')).toEqual([]);
34
+ });
35
+ });
36
+ describe("resolveWrittenField", () => {
37
+ const f = (id, text, focused = false) => ({ id, text, focused });
38
+ it("prefers the testID when there is one", () => {
39
+ const after = [f("a", "one"), f("b", "two")];
40
+ expect(resolveWrittenField(after, after, "b")).toEqual({ text: "two", via: "testID" });
41
+ });
42
+ it("falls back to the focused field when there is no testID", () => {
43
+ const after = [f(null, "one"), f(null, "two", true)];
44
+ expect(resolveWrittenField(after, after, null)).toEqual({ text: "two", via: "focused" });
45
+ });
46
+ it("falls back to the single changed field when nothing identifies it", () => {
47
+ // iOS exposes no focus flag, so a field with no testID is only findable
48
+ // by what changed.
49
+ const before = [f(null, "one"), f(null, "")];
50
+ const after = [f(null, "one"), f(null, "Alice")];
51
+ expect(resolveWrittenField(before, after, null)).toEqual({ text: "Alice", via: "changed" });
52
+ });
53
+ it("returns null when several fields changed", () => {
54
+ const before = [f(null, ""), f(null, "")];
55
+ const after = [f(null, "x"), f(null, "y")];
56
+ expect(resolveWrittenField(before, after, null)).toBeNull();
57
+ });
58
+ it("returns null when nothing changed and nothing identifies the field", () => {
59
+ const same = [f(null, "one"), f(null, "two")];
60
+ expect(resolveWrittenField(same, same, null)).toBeNull();
61
+ });
62
+ it("does not silently match a different testID", () => {
63
+ const after = [f("a", "one")];
64
+ expect(resolveWrittenField(after, after, "missing")).toBeNull();
65
+ });
66
+ });
67
+ //# sourceMappingURL=nativeInputValue.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nativeInputValue.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/nativeInputValue.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=screenSpace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenSpace.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/screenSpace.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { toScreenSpaceY, fromScreenSpaceY, SCREEN_SPACE_HELPER_JS } from "../../core/screenSpace.js";
3
+ const IOS = { platform: "ios", topInset: 59 };
4
+ const ANDROID = { platform: "android", topInset: 54 };
5
+ describe("toScreenSpaceY", () => {
6
+ // Android measureInWindow is relative to the app window, which starts below the
7
+ // status bar. Measured on device: a header button at 7dp renders at 61dp, and the
8
+ // screenshot formatter has always added the status bar to match.
9
+ it("always adds the status bar on Android", () => {
10
+ expect(toScreenSpaceY(7, ANDROID)).toBe(61);
11
+ expect(toScreenSpaceY(569, ANDROID)).toBe(623);
12
+ });
13
+ // iOS already includes the status bar for a normally-presented screen. Measured on
14
+ // device: get_screen_state and raw measureInWindow agree exactly (RoleButton "Yes"
15
+ // at (104,569) in both), so shifting here would break correct coordinates.
16
+ it("leaves a normally-presented iOS screen untouched", () => {
17
+ expect(toScreenSpaceY(75, IOS)).toBe(75);
18
+ expect(toScreenSpaceY(569, IOS)).toBe(569);
19
+ expect(toScreenSpaceY(59, IOS)).toBe(59);
20
+ });
21
+ // A react-native-screens modal measures from its own container. Measured on device:
22
+ // the Register button reports y=29 but renders at y=88 — one inset lower.
23
+ it("shifts an iOS element measured inside the safe-area band", () => {
24
+ expect(toScreenSpaceY(29, IOS)).toBe(88);
25
+ expect(toScreenSpaceY(0, IOS)).toBe(59);
26
+ });
27
+ it("is a no-op when the inset is unknown", () => {
28
+ expect(toScreenSpaceY(29, { platform: "ios", topInset: 0 })).toBe(29);
29
+ expect(toScreenSpaceY(7, { platform: "android", topInset: 0 })).toBe(7);
30
+ });
31
+ });
32
+ describe("fromScreenSpaceY", () => {
33
+ it("round-trips Android", () => {
34
+ for (const y of [0, 7, 569, 1200]) {
35
+ expect(fromScreenSpaceY(toScreenSpaceY(y, ANDROID), ANDROID)).toBe(y);
36
+ }
37
+ });
38
+ // Only the band that the forward map can produce is unshifted. Values at or above
39
+ // 2*inset were never shifted, so unshifting them would corrupt correct input.
40
+ it("round-trips the iOS modal band and leaves ordinary values alone", () => {
41
+ for (const y of [0, 29, 58]) {
42
+ expect(fromScreenSpaceY(toScreenSpaceY(y, IOS), IOS)).toBe(y);
43
+ }
44
+ expect(fromScreenSpaceY(400, IOS)).toBe(400);
45
+ expect(fromScreenSpaceY(118, IOS)).toBe(118);
46
+ });
47
+ });
48
+ describe("SCREEN_SPACE_HELPER_JS", () => {
49
+ // The injected walkers must apply the identical rule; a divergent second copy is
50
+ // what produced the mismatch this module exists to remove.
51
+ it("evaluates to a function matching the Node implementation", () => {
52
+ const fn = new Function(`${SCREEN_SPACE_HELPER_JS} return toScreenSpaceY;`)();
53
+ expect(fn(29, IOS)).toBe(88);
54
+ expect(fn(75, IOS)).toBe(75);
55
+ expect(fn(7, ANDROID)).toBe(61);
56
+ });
57
+ });
58
+ //# sourceMappingURL=screenSpace.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenSpace.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/screenSpace.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=screenStateInputValue.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenStateInputValue.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/screenStateInputValue.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { formatScreenStateSummary } from "../../core/screenState.js";
3
+ // A controlled TextInput keeps its text in props.value, not in a child text
4
+ // node. Before this change the label chain checked accessibilityLabel, then
5
+ // child text, then placeholder — so every field reported its placeholder as
6
+ // content, and the "type, then verify with get_screen_state" workflow we
7
+ // recommend read a stale placeholder and concluded the write had failed.
8
+ function input(over = {}) {
9
+ return {
10
+ label: "Search gifts, brands",
11
+ component: "TextInput",
12
+ center: { x: 183, y: 139 },
13
+ bounds: { x: 60, y: 128, width: 246, height: 21 },
14
+ testID: null,
15
+ isInput: true,
16
+ ...over
17
+ };
18
+ }
19
+ function screen(pressables) {
20
+ return { route: null, overlays: [], pressables, texts: [], images: [] };
21
+ }
22
+ describe("get_screen_state input reporting", () => {
23
+ it("renders the field's value, not its placeholder", () => {
24
+ const out = formatScreenStateSummary(screen([input({ inputValue: "HELLO", inputPlaceholder: "Search gifts, brands" })]), undefined, { pressablesOnly: true });
25
+ expect(out).toContain('value:"HELLO"');
26
+ });
27
+ it("marks an empty field as empty and keeps the placeholder identifiable", () => {
28
+ const out = formatScreenStateSummary(screen([input({ inputValue: null, inputPlaceholder: "Search gifts, brands" })]), undefined, { pressablesOnly: true });
29
+ expect(out).toContain("[input] empty");
30
+ expect(out).toContain('placeholder:"Search gifts, brands"');
31
+ expect(out).not.toContain('value:"Search gifts, brands"');
32
+ });
33
+ it("never lets a placeholder pose as a value", () => {
34
+ const out = formatScreenStateSummary(screen([input({ inputValue: null, inputPlaceholder: "Enter email" })]), undefined, { pressablesOnly: true });
35
+ // The whole bug: an agent reading this line must not conclude the field
36
+ // contains "Enter email".
37
+ expect(out).not.toMatch(/value:"Enter email"/);
38
+ });
39
+ it("still marks inputs that report neither value nor placeholder", () => {
40
+ const out = formatScreenStateSummary(screen([input()]), undefined, { pressablesOnly: true });
41
+ expect(out).toContain("[input]");
42
+ });
43
+ it("leaves non-input pressables untouched", () => {
44
+ const out = formatScreenStateSummary(screen([{ label: "Submit", center: { x: 1, y: 2 }, bounds: { x: 0, y: 0, width: 4, height: 4 }, testID: null }]), undefined, { pressablesOnly: true });
45
+ expect(out).toContain('"Submit"');
46
+ expect(out).not.toContain("[input]");
47
+ });
48
+ });
49
+ //# sourceMappingURL=screenStateInputValue.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenStateInputValue.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/screenStateInputValue.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=screenStateKeyboard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenStateKeyboard.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/screenStateKeyboard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,99 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { formatKeyboardLine, partitionByKeyboard, formatScreenStateSummary } from "../../core/screenState.js";
3
+ const up = { visible: true, height: 345, screenY: 567, width: 420 };
4
+ const down = { visible: false, height: null, screenY: null, width: null };
5
+ describe("formatKeyboardLine", () => {
6
+ it("reports height and the remaining content area when visible", () => {
7
+ const line = formatKeyboardLine(up);
8
+ expect(line).toContain("345");
9
+ expect(line).toContain("567");
10
+ });
11
+ it("renders nothing when the keyboard is down", () => {
12
+ expect(formatKeyboardLine(down)).toBe("");
13
+ });
14
+ it("says unknown rather than nothing when the state could not be read", () => {
15
+ // Silence would read as "keyboard is down", which is a different claim.
16
+ const line = formatKeyboardLine({ ...down, error: "no Keyboard module" });
17
+ expect(line).toContain("unknown");
18
+ expect(line).toContain("no Keyboard module");
19
+ });
20
+ it("renders nothing when visible is true but metrics are missing", () => {
21
+ expect(formatKeyboardLine({ visible: true, height: null, screenY: null, width: null })).toBe("");
22
+ });
23
+ });
24
+ describe("partitionByKeyboard", () => {
25
+ const item = (y, label) => ({
26
+ label,
27
+ center: { x: 10, y },
28
+ bounds: { x: 0, y: y - 5, width: 20, height: 10 }
29
+ });
30
+ it("splits elements the keyboard covers from the reachable ones", () => {
31
+ const { reachable, blocked } = partitionByKeyboard([item(100, "top"), item(800, "under")], up);
32
+ expect(reachable.map((p) => p.label)).toEqual(["top"]);
33
+ expect(blocked.map((p) => p.label)).toEqual(["under"]);
34
+ });
35
+ it("treats an element exactly on the keyboard edge as blocked", () => {
36
+ const { blocked } = partitionByKeyboard([item(567, "edge")], up);
37
+ expect(blocked.map((p) => p.label)).toEqual(["edge"]);
38
+ });
39
+ it("blocks nothing when the keyboard is down", () => {
40
+ const { reachable, blocked } = partitionByKeyboard([item(100, "a"), item(800, "b")], down);
41
+ expect(reachable).toHaveLength(2);
42
+ expect(blocked).toHaveLength(0);
43
+ });
44
+ });
45
+ describe("get_screen_state keyboard reporting", () => {
46
+ const pressable = (y, label) => ({
47
+ label,
48
+ center: { x: 210, y },
49
+ bounds: { x: 20, y: y - 20, width: 380, height: 40 },
50
+ testID: null
51
+ });
52
+ const screen = (pressables) => ({
53
+ route: null,
54
+ overlays: [],
55
+ pressables,
56
+ texts: [],
57
+ images: []
58
+ });
59
+ it("groups keyboard-covered elements separately", () => {
60
+ const out = formatScreenStateSummary(screen([pressable(100, "Header"), pressable(841, "Home tab")]), undefined, {
61
+ pressablesOnly: true,
62
+ keyboard: up
63
+ });
64
+ expect(out).toContain("⌨️ Keyboard: visible");
65
+ expect(out).toContain("Blocked by keyboard");
66
+ const blockedIdx = out.indexOf("Blocked by keyboard");
67
+ expect(out.indexOf("Home tab")).toBeGreaterThan(blockedIdx);
68
+ expect(out.indexOf("Header")).toBeLessThan(blockedIdx);
69
+ });
70
+ it("adds nothing when no keyboard state was supplied", () => {
71
+ const out = formatScreenStateSummary(screen([pressable(841, "Home tab")]), undefined, {
72
+ pressablesOnly: true
73
+ });
74
+ expect(out).not.toContain("Keyboard");
75
+ expect(out).not.toContain("Blocked by keyboard");
76
+ });
77
+ it("adds no blocked group when the keyboard is down", () => {
78
+ const out = formatScreenStateSummary(screen([pressable(841, "Home tab")]), undefined, {
79
+ pressablesOnly: true,
80
+ keyboard: down
81
+ });
82
+ expect(out).not.toContain("Blocked by keyboard");
83
+ expect(out).toContain("Home tab");
84
+ });
85
+ });
86
+ describe("formatKeyboardLine rounding", () => {
87
+ it("rounds Android's fractional dp to whole points", () => {
88
+ const line = formatKeyboardLine({
89
+ visible: true,
90
+ height: 288.3809509277344,
91
+ screenY: 587.047607421875,
92
+ width: 411.4285583496094
93
+ });
94
+ expect(line).toContain("288pt");
95
+ expect(line).toContain("411x587pt");
96
+ expect(line).not.toContain(".");
97
+ });
98
+ });
99
+ //# sourceMappingURL=screenStateKeyboard.test.js.map