@reticlehq/browser 2.2.0 → 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.
- package/dist/actions/actions-dom.d.ts +0 -1
- package/dist/actions/actions-dom.js +0 -1
- package/dist/actions/actions.d.ts +3 -1
- package/dist/actions/actions.js +55 -36
- package/dist/actions/scroll.d.ts +0 -1
- package/dist/actions/scroll.js +0 -1
- package/dist/actions/value-input.d.ts +33 -0
- package/dist/actions/value-input.js +63 -0
- package/dist/commands/commands.d.ts +0 -1
- package/dist/commands/commands.js +15 -7
- package/dist/dom/a11y.d.ts +0 -2
- package/dist/dom/a11y.js +54 -24
- package/dist/dom/auto-anchor.d.ts +0 -1
- package/dist/dom/auto-anchor.js +0 -1
- package/dist/dom/blind-spots.d.ts +0 -1
- package/dist/dom/blind-spots.js +1 -2
- package/dist/dom/chart.d.ts +0 -1
- package/dist/dom/chart.js +0 -1
- package/dist/dom/desktop-capture.d.ts +36 -0
- package/dist/dom/desktop-capture.js +113 -0
- package/dist/dom/dom-ignore.d.ts +7 -1
- package/dist/dom/dom-ignore.js +1 -2
- package/dist/dom/occlusion.d.ts +0 -1
- package/dist/dom/occlusion.js +0 -1
- package/dist/dom/query.d.ts +0 -1
- package/dist/dom/query.js +61 -13
- package/dist/dom/realm.d.ts +30 -0
- package/dist/dom/realm.js +58 -0
- package/dist/dom/refs.d.ts +0 -1
- package/dist/dom/refs.js +0 -1
- package/dist/dom/shadow-registry.d.ts +40 -0
- package/dist/dom/shadow-registry.js +165 -0
- package/dist/dom/snapshot.d.ts +2 -2
- package/dist/dom/snapshot.js +21 -4
- package/dist/dom/source.d.ts +0 -1
- package/dist/dom/source.js +0 -1
- package/dist/dom/theme.d.ts +0 -1
- package/dist/dom/theme.js +0 -1
- package/dist/dom/virtualized.d.ts +19 -0
- package/dist/dom/virtualized.js +80 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/observers/animation.d.ts +0 -1
- package/dist/observers/animation.js +0 -1
- package/dist/observers/blind-spots.d.ts +16 -4
- package/dist/observers/blind-spots.js +65 -11
- package/dist/observers/console.d.ts +0 -1
- package/dist/observers/console.js +0 -1
- package/dist/observers/dom.d.ts +0 -2
- package/dist/observers/dom.js +67 -9
- package/dist/observers/download.d.ts +6 -0
- package/dist/observers/download.js +103 -0
- package/dist/observers/focus.d.ts +0 -1
- package/dist/observers/focus.js +0 -1
- package/dist/observers/frames.d.ts +12 -0
- package/dist/observers/frames.js +67 -0
- package/dist/observers/health.d.ts +0 -1
- package/dist/observers/health.js +30 -2
- package/dist/observers/install-all.d.ts +5 -0
- package/dist/observers/install-all.js +63 -0
- package/dist/observers/ipc.d.ts +22 -0
- package/dist/observers/ipc.js +121 -0
- package/dist/observers/network-body.d.ts +17 -1
- package/dist/observers/network-body.js +26 -1
- package/dist/observers/network-redact.d.ts +0 -1
- package/dist/observers/network-redact.js +6 -3
- package/dist/observers/network-stream.d.ts +23 -0
- package/dist/observers/network-stream.js +102 -0
- package/dist/observers/network.d.ts +11 -1
- package/dist/observers/network.js +7 -1
- package/dist/observers/perf.d.ts +0 -1
- package/dist/observers/perf.js +0 -1
- package/dist/observers/route.d.ts +0 -1
- package/dist/observers/route.js +0 -1
- package/dist/observers/scroll.d.ts +0 -1
- package/dist/observers/scroll.js +0 -1
- package/dist/observers/sdk-failure.d.ts +28 -0
- package/dist/observers/sdk-failure.js +60 -0
- package/dist/observers/state.d.ts +0 -1
- package/dist/observers/state.js +0 -1
- package/dist/observers/storage.d.ts +0 -1
- package/dist/observers/storage.js +0 -1
- package/dist/observers/types.d.ts +0 -1
- package/dist/observers/types.js +0 -1
- package/dist/patching/capture-method.d.ts +0 -1
- package/dist/patching/capture-method.js +0 -1
- package/dist/presenter/overlay.d.ts +0 -1
- package/dist/presenter/overlay.js +0 -1
- package/dist/presenter/presenter-config.d.ts +0 -1
- package/dist/presenter/presenter-config.js +0 -1
- package/dist/presenter/presenter-controls.d.ts +0 -1
- package/dist/presenter/presenter-controls.js +0 -1
- package/dist/presenter/presenter-effects.d.ts +0 -1
- package/dist/presenter/presenter-effects.js +0 -1
- package/dist/presenter/presenter-glow.d.ts +0 -1
- package/dist/presenter/presenter-glow.js +0 -1
- package/dist/presenter/presenter-log.d.ts +0 -1
- package/dist/presenter/presenter-log.js +0 -1
- package/dist/presenter/presenter-run-state.d.ts +0 -1
- package/dist/presenter/presenter-run-state.js +0 -1
- package/dist/presenter/presenter-styles.d.ts +0 -1
- package/dist/presenter/presenter-styles.js +0 -1
- package/dist/presenter/presenter-tally.d.ts +0 -1
- package/dist/presenter/presenter-tally.js +0 -1
- package/dist/presenter/presenter-test-helpers.d.ts +0 -1
- package/dist/presenter/presenter-test-helpers.js +0 -1
- package/dist/presenter/presenter-verbs.d.ts +0 -1
- package/dist/presenter/presenter-verbs.js +0 -1
- package/dist/presenter/presenter.d.ts +0 -1
- package/dist/presenter/presenter.js +0 -1
- package/dist/recorder/recorder-styles.d.ts +0 -1
- package/dist/recorder/recorder-styles.js +0 -1
- package/dist/recorder/recorder.d.ts +0 -1
- package/dist/recorder/recorder.js +0 -1
- package/dist/registry/adapters.d.ts +0 -1
- package/dist/registry/adapters.js +0 -1
- package/dist/registry/capabilities.d.ts +0 -1
- package/dist/registry/capabilities.js +0 -1
- package/dist/registry/commit-and-signal.d.ts +0 -1
- package/dist/registry/commit-and-signal.js +0 -1
- package/dist/registry/domains.d.ts +0 -1
- package/dist/registry/domains.js +0 -1
- package/dist/registry/emitter.d.ts +0 -1
- package/dist/registry/emitter.js +0 -1
- package/dist/registry/store-adapters.d.ts +14 -6
- package/dist/registry/store-adapters.js +0 -1
- package/dist/registry/stores.d.ts +0 -1
- package/dist/registry/stores.js +10 -2
- package/dist/reticle-presenter-helpers.d.ts +0 -1
- package/dist/reticle-presenter-helpers.js +0 -1
- package/dist/reticle.d.ts +3 -2
- package/dist/reticle.js +12 -32
- package/dist/review/annotator.d.ts +0 -1
- package/dist/review/annotator.js +0 -1
- package/dist/review/mark-anchor.d.ts +0 -1
- package/dist/review/mark-anchor.js +0 -1
- package/dist/security/serialization.d.ts +0 -1
- package/dist/security/serialization.js +0 -1
- package/dist/timers/clock.d.ts +0 -1
- package/dist/timers/clock.js +0 -1
- package/dist/timers/native-console.d.ts +1 -0
- package/dist/timers/native-console.js +9 -0
- package/dist/timers/native-timers.d.ts +0 -1
- package/dist/timers/native-timers.js +0 -1
- package/dist/transport/transport.d.ts +2 -6
- package/dist/transport/transport.js +95 -18
- package/package.json +8 -3
- package/dist/actions/actions-dom.d.ts.map +0 -1
- package/dist/actions/actions-dom.js.map +0 -1
- package/dist/actions/actions.d.ts.map +0 -1
- package/dist/actions/actions.js.map +0 -1
- package/dist/actions/scroll.d.ts.map +0 -1
- package/dist/actions/scroll.js.map +0 -1
- package/dist/commands/commands.d.ts.map +0 -1
- package/dist/commands/commands.js.map +0 -1
- package/dist/dom/a11y.d.ts.map +0 -1
- package/dist/dom/a11y.js.map +0 -1
- package/dist/dom/auto-anchor.d.ts.map +0 -1
- package/dist/dom/auto-anchor.js.map +0 -1
- package/dist/dom/blind-spots.d.ts.map +0 -1
- package/dist/dom/blind-spots.js.map +0 -1
- package/dist/dom/chart.d.ts.map +0 -1
- package/dist/dom/chart.js.map +0 -1
- package/dist/dom/dom-ignore.d.ts.map +0 -1
- package/dist/dom/dom-ignore.js.map +0 -1
- package/dist/dom/occlusion.d.ts.map +0 -1
- package/dist/dom/occlusion.js.map +0 -1
- package/dist/dom/query.d.ts.map +0 -1
- package/dist/dom/query.js.map +0 -1
- package/dist/dom/refs.d.ts.map +0 -1
- package/dist/dom/refs.js.map +0 -1
- package/dist/dom/snapshot.d.ts.map +0 -1
- package/dist/dom/snapshot.js.map +0 -1
- package/dist/dom/source.d.ts.map +0 -1
- package/dist/dom/source.js.map +0 -1
- package/dist/dom/theme.d.ts.map +0 -1
- package/dist/dom/theme.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/observers/animation.d.ts.map +0 -1
- package/dist/observers/animation.js.map +0 -1
- package/dist/observers/blind-spots.d.ts.map +0 -1
- package/dist/observers/blind-spots.js.map +0 -1
- package/dist/observers/console.d.ts.map +0 -1
- package/dist/observers/console.js.map +0 -1
- package/dist/observers/dom.d.ts.map +0 -1
- package/dist/observers/dom.js.map +0 -1
- package/dist/observers/focus.d.ts.map +0 -1
- package/dist/observers/focus.js.map +0 -1
- package/dist/observers/health.d.ts.map +0 -1
- package/dist/observers/health.js.map +0 -1
- package/dist/observers/network-body.d.ts.map +0 -1
- package/dist/observers/network-body.js.map +0 -1
- package/dist/observers/network-redact.d.ts.map +0 -1
- package/dist/observers/network-redact.js.map +0 -1
- package/dist/observers/network.d.ts.map +0 -1
- package/dist/observers/network.js.map +0 -1
- package/dist/observers/perf.d.ts.map +0 -1
- package/dist/observers/perf.js.map +0 -1
- package/dist/observers/route.d.ts.map +0 -1
- package/dist/observers/route.js.map +0 -1
- package/dist/observers/scroll.d.ts.map +0 -1
- package/dist/observers/scroll.js.map +0 -1
- package/dist/observers/state.d.ts.map +0 -1
- package/dist/observers/state.js.map +0 -1
- package/dist/observers/storage.d.ts.map +0 -1
- package/dist/observers/storage.js.map +0 -1
- package/dist/observers/types.d.ts.map +0 -1
- package/dist/observers/types.js.map +0 -1
- package/dist/patching/capture-method.d.ts.map +0 -1
- package/dist/patching/capture-method.js.map +0 -1
- package/dist/presenter/overlay.d.ts.map +0 -1
- package/dist/presenter/overlay.js.map +0 -1
- package/dist/presenter/presenter-config.d.ts.map +0 -1
- package/dist/presenter/presenter-config.js.map +0 -1
- package/dist/presenter/presenter-controls.d.ts.map +0 -1
- package/dist/presenter/presenter-controls.js.map +0 -1
- package/dist/presenter/presenter-effects.d.ts.map +0 -1
- package/dist/presenter/presenter-effects.js.map +0 -1
- package/dist/presenter/presenter-glow.d.ts.map +0 -1
- package/dist/presenter/presenter-glow.js.map +0 -1
- package/dist/presenter/presenter-log.d.ts.map +0 -1
- package/dist/presenter/presenter-log.js.map +0 -1
- package/dist/presenter/presenter-run-state.d.ts.map +0 -1
- package/dist/presenter/presenter-run-state.js.map +0 -1
- package/dist/presenter/presenter-styles.d.ts.map +0 -1
- package/dist/presenter/presenter-styles.js.map +0 -1
- package/dist/presenter/presenter-tally.d.ts.map +0 -1
- package/dist/presenter/presenter-tally.js.map +0 -1
- package/dist/presenter/presenter-test-helpers.d.ts.map +0 -1
- package/dist/presenter/presenter-test-helpers.js.map +0 -1
- package/dist/presenter/presenter-verbs.d.ts.map +0 -1
- package/dist/presenter/presenter-verbs.js.map +0 -1
- package/dist/presenter/presenter.d.ts.map +0 -1
- package/dist/presenter/presenter.js.map +0 -1
- package/dist/recorder/recorder-styles.d.ts.map +0 -1
- package/dist/recorder/recorder-styles.js.map +0 -1
- package/dist/recorder/recorder.d.ts.map +0 -1
- package/dist/recorder/recorder.js.map +0 -1
- package/dist/registry/adapters.d.ts.map +0 -1
- package/dist/registry/adapters.js.map +0 -1
- package/dist/registry/capabilities.d.ts.map +0 -1
- package/dist/registry/capabilities.js.map +0 -1
- package/dist/registry/commit-and-signal.d.ts.map +0 -1
- package/dist/registry/commit-and-signal.js.map +0 -1
- package/dist/registry/domains.d.ts.map +0 -1
- package/dist/registry/domains.js.map +0 -1
- package/dist/registry/emitter.d.ts.map +0 -1
- package/dist/registry/emitter.js.map +0 -1
- package/dist/registry/store-adapters.d.ts.map +0 -1
- package/dist/registry/store-adapters.js.map +0 -1
- package/dist/registry/stores.d.ts.map +0 -1
- package/dist/registry/stores.js.map +0 -1
- package/dist/reticle-presenter-helpers.d.ts.map +0 -1
- package/dist/reticle-presenter-helpers.js.map +0 -1
- package/dist/reticle.d.ts.map +0 -1
- package/dist/reticle.js.map +0 -1
- package/dist/review/annotator.d.ts.map +0 -1
- package/dist/review/annotator.js.map +0 -1
- package/dist/review/mark-anchor.d.ts.map +0 -1
- package/dist/review/mark-anchor.js.map +0 -1
- package/dist/security/serialization.d.ts.map +0 -1
- package/dist/security/serialization.js.map +0 -1
- package/dist/timers/clock.d.ts.map +0 -1
- package/dist/timers/clock.js.map +0 -1
- package/dist/timers/native-timers.d.ts.map +0 -1
- package/dist/timers/native-timers.js.map +0 -1
- package/dist/transport/transport.d.ts.map +0 -1
- package/dist/transport/transport.js.map +0 -1
|
@@ -51,6 +51,9 @@ interface ActionResult {
|
|
|
51
51
|
* degraded role one — addressing the element across replays with zero hand-added testids.
|
|
52
52
|
*/
|
|
53
53
|
component?: string;
|
|
54
|
+
/** Role + accessible name — the anchor that identifies an INSTANCE, not a JSX site. */
|
|
55
|
+
role?: string;
|
|
56
|
+
name?: string;
|
|
54
57
|
source?: {
|
|
55
58
|
file: string;
|
|
56
59
|
line: number;
|
|
@@ -89,4 +92,3 @@ export declare function executeSequence(steps: ActionStep[]): Promise<{
|
|
|
89
92
|
steps: SequenceStepResult[];
|
|
90
93
|
}>;
|
|
91
94
|
export {};
|
|
92
|
-
//# sourceMappingURL=actions.d.ts.map
|
package/dist/actions/actions.js
CHANGED
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
import { ActionType, ActionWarning, DANGEROUS_ACTION_CONFIRM_ARG, ElementState, isDangerousActionText, SettleReason, } from '@reticlehq/core';
|
|
2
2
|
import { refs } from '../dom/refs.js';
|
|
3
|
-
import {
|
|
3
|
+
import { assertEditable, assertNotRichText, setNativeValue } from './value-input.js';
|
|
4
|
+
import { getAccessibleName, getRole, isVisible, getStates } from '../dom/a11y.js';
|
|
4
5
|
import { elementHasHoverHandlers, identifyComponent } from '../registry/adapters.js';
|
|
5
|
-
import {
|
|
6
|
+
import { isForm, isHtmlElement, isInput, isSelect, isTextArea } from '../dom/realm.js';
|
|
6
7
|
import { nativeSetTimeout, settle } from '../timers/native-timers.js';
|
|
7
|
-
/**
|
|
8
|
-
* Set a value on a controlled input the way React expects (native setter + input event).
|
|
9
|
-
* Returns the `input` event's dispatchEvent result (defaultPrevented of the primary event).
|
|
10
|
-
*/
|
|
11
|
-
function setNativeValue(el, value) {
|
|
12
|
-
const proto = el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
|
|
13
|
-
const setter = captureValueSetter(proto);
|
|
14
|
-
if (setter !== undefined) {
|
|
15
|
-
setter.call(el, value);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
el.value = value;
|
|
19
|
-
}
|
|
20
|
-
const notPrevented = el.dispatchEvent(new Event('input', { bubbles: true, cancelable: true }));
|
|
21
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
22
|
-
return !notPrevented;
|
|
23
|
-
}
|
|
24
8
|
function asString(value, fallback = '') {
|
|
25
9
|
return typeof value === 'string' ? value : fallback;
|
|
26
10
|
}
|
|
@@ -28,7 +12,7 @@ function requireElement(ref) {
|
|
|
28
12
|
const el = refs.resolve(ref);
|
|
29
13
|
if (el === null)
|
|
30
14
|
throw new Error(`ref '${ref}' no longer resolves to an element`);
|
|
31
|
-
if (!(el
|
|
15
|
+
if (!isHtmlElement(el))
|
|
32
16
|
throw new Error(`ref '${ref}' is not an HTMLElement`);
|
|
33
17
|
return el;
|
|
34
18
|
}
|
|
@@ -55,6 +39,12 @@ function anchorOf(el) {
|
|
|
55
39
|
out.component = component;
|
|
56
40
|
if (info?.source !== undefined)
|
|
57
41
|
out.source = info.source;
|
|
42
|
+
const role = getRole(el);
|
|
43
|
+
const name = getAccessibleName(el);
|
|
44
|
+
if (role.length > 0 && name.length > 0) {
|
|
45
|
+
out.role = role;
|
|
46
|
+
out.name = name;
|
|
47
|
+
}
|
|
58
48
|
return out;
|
|
59
49
|
}
|
|
60
50
|
const result = (ref, action, effect, settled, settleReason, anchor, warning) => {
|
|
@@ -77,6 +67,10 @@ const result = (ref, action, effect, settled, settleReason, anchor, warning) =>
|
|
|
77
67
|
base.component = anchor.component;
|
|
78
68
|
if (anchor.source !== undefined)
|
|
79
69
|
base.source = anchor.source;
|
|
70
|
+
if (anchor.role !== undefined)
|
|
71
|
+
base.role = anchor.role;
|
|
72
|
+
if (anchor.name !== undefined)
|
|
73
|
+
base.name = anchor.name;
|
|
80
74
|
if (warning !== undefined)
|
|
81
75
|
base.warning = warning;
|
|
82
76
|
return base;
|
|
@@ -116,7 +110,7 @@ function assertActionAllowed(el, action, args) {
|
|
|
116
110
|
action === ActionType.SUBMIT ||
|
|
117
111
|
(action === ActionType.PRESS && asString(args['key'], 'Enter') === 'Enter');
|
|
118
112
|
const dragTarget = action === ActionType.DRAG ? refs.resolve(asString(args['toRef'])) : null;
|
|
119
|
-
const context = dragTarget
|
|
113
|
+
const context = isHtmlElement(dragTarget)
|
|
120
114
|
? `${dangerousActionContext(el)} ${dangerousActionContext(dragTarget)}`
|
|
121
115
|
: dangerousActionContext(el);
|
|
122
116
|
if (canTrigger &&
|
|
@@ -133,9 +127,7 @@ function enabledOf(el) {
|
|
|
133
127
|
}
|
|
134
128
|
/** Current value for inputs/textareas/selects, else undefined. */
|
|
135
129
|
function valueOf(el) {
|
|
136
|
-
if (el
|
|
137
|
-
el instanceof HTMLTextAreaElement ||
|
|
138
|
-
el instanceof HTMLSelectElement) {
|
|
130
|
+
if (isInput(el) || isTextArea(el) || isSelect(el)) {
|
|
139
131
|
return el.value;
|
|
140
132
|
}
|
|
141
133
|
return undefined;
|
|
@@ -190,41 +182,69 @@ async function dispatchFor(el, action, args) {
|
|
|
190
182
|
el.dispatchEvent(new FocusEvent('focusout', { bubbles: true }));
|
|
191
183
|
return false;
|
|
192
184
|
case ActionType.FILL:
|
|
193
|
-
if (el
|
|
185
|
+
if (isInput(el) || isTextArea(el)) {
|
|
186
|
+
// A MISSING value is a malformed call, not a request to empty the field. `asString` defaults
|
|
187
|
+
// to '', which made such a fill indistinguishable from `clear`: it wiped what was there,
|
|
188
|
+
// dispatched a real input event so React committed the empty string to state, and reported
|
|
189
|
+
// ok:true with nothing flagged. The value is nested (`args:{value}`) at the tool boundary, so
|
|
190
|
+
// passing it at the top level lands here — measured on bench-app's login form, where
|
|
191
|
+
// "admin@reticle.dev" was destroyed by a fill that reported success. An explicit '' still
|
|
192
|
+
// clears, because that is someone asking for it.
|
|
193
|
+
if (typeof args['value'] !== 'string') {
|
|
194
|
+
throw new Error("fill requires a string `value` — pass it nested, as args: { value: '…' }. " +
|
|
195
|
+
'To empty a field on purpose use the `clear` action, or fill with an explicit "".');
|
|
196
|
+
}
|
|
197
|
+
assertEditable(el, 'fill');
|
|
194
198
|
el.focus(); // focus first so a later blur commits (onBlur editors)
|
|
195
|
-
return setNativeValue(el,
|
|
199
|
+
return setNativeValue(el, args['value']);
|
|
196
200
|
}
|
|
201
|
+
assertNotRichText(el, 'fill');
|
|
197
202
|
throw new Error(`cannot fill a <${el.tagName.toLowerCase()}>`);
|
|
198
203
|
case ActionType.TYPE:
|
|
199
|
-
if (el
|
|
204
|
+
if (isInput(el) || isTextArea(el)) {
|
|
205
|
+
// Same family as the valueless FILL: a missing `text` appended '' and reported ok:true, so an
|
|
206
|
+
// agent believed it had typed into a field nothing was written to.
|
|
207
|
+
if (typeof args['text'] !== 'string') {
|
|
208
|
+
throw new Error("type requires a string `text` — pass it nested, as args: { text: '…' }.");
|
|
209
|
+
}
|
|
210
|
+
assertEditable(el, 'type');
|
|
200
211
|
el.focus();
|
|
201
|
-
return setNativeValue(el, el.value +
|
|
212
|
+
return setNativeValue(el, el.value + args['text']);
|
|
202
213
|
}
|
|
214
|
+
assertNotRichText(el, 'type into');
|
|
203
215
|
throw new Error(`cannot type into a <${el.tagName.toLowerCase()}>`);
|
|
204
216
|
case ActionType.CLEAR:
|
|
205
|
-
if (el
|
|
217
|
+
if (isInput(el) || isTextArea(el)) {
|
|
206
218
|
return setNativeValue(el, '');
|
|
207
219
|
}
|
|
208
220
|
// Consistent with TYPE/SELECT/CHECK: clearing a non-field is not a silent success. Throwing
|
|
209
221
|
// surfaces the mismatch instead of reporting ok:true for an action that did nothing.
|
|
210
222
|
throw new Error(`cannot clear a <${el.tagName.toLowerCase()}>`);
|
|
211
223
|
case ActionType.SELECT:
|
|
212
|
-
if (el
|
|
213
|
-
|
|
224
|
+
if (isSelect(el)) {
|
|
225
|
+
// A MISSING value assigned '' to the <select>. No option carries that, so the browser sets
|
|
226
|
+
// selectedIndex to -1 — deselecting everything — from a call that was simply malformed.
|
|
227
|
+
// (Selecting a value that no option carries is a different case and is deliberately NOT
|
|
228
|
+
// refused: `actions.effect.test.ts` pins it as a detectable no-op, where the reported
|
|
229
|
+
// valueChanged delta is what tells the agent the option never took.)
|
|
230
|
+
if (typeof args['value'] !== 'string') {
|
|
231
|
+
throw new Error("select requires a string `value` — pass it nested, as args: { value: '…' }.");
|
|
232
|
+
}
|
|
233
|
+
el.value = args['value'];
|
|
214
234
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
215
235
|
return false; // change is not cancelable.
|
|
216
236
|
}
|
|
217
237
|
throw new Error(`cannot select on a <${el.tagName.toLowerCase()}>`);
|
|
218
238
|
case ActionType.CHECK:
|
|
219
239
|
case ActionType.UNCHECK:
|
|
220
|
-
if (el
|
|
240
|
+
if (isInput(el)) {
|
|
221
241
|
el.checked = action === ActionType.CHECK;
|
|
222
242
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
223
243
|
return false;
|
|
224
244
|
}
|
|
225
245
|
throw new Error(`cannot (un)check a <${el.tagName.toLowerCase()}>`);
|
|
226
246
|
case ActionType.SUBMIT: {
|
|
227
|
-
const form = el
|
|
247
|
+
const form = isForm(el) ? el : el.closest('form');
|
|
228
248
|
if (form === null)
|
|
229
249
|
throw new Error('no form to submit');
|
|
230
250
|
form.requestSubmit();
|
|
@@ -240,7 +260,7 @@ async function dispatchFor(el, action, args) {
|
|
|
240
260
|
el.scrollIntoView();
|
|
241
261
|
return false;
|
|
242
262
|
case ActionType.UPLOAD: {
|
|
243
|
-
if (!(el
|
|
263
|
+
if (!isInput(el) || el.type !== 'file') {
|
|
244
264
|
throw new Error('upload target must be a <input type="file">');
|
|
245
265
|
}
|
|
246
266
|
const file = new File([asString(args['content'], 'reticle test file')], asString(args['name'], 'file.txt'), {
|
|
@@ -258,7 +278,7 @@ async function dispatchFor(el, action, args) {
|
|
|
258
278
|
// asString falls back to '' (never undefined), so the old `!== undefined` was always true and
|
|
259
279
|
// handed '' to refs.resolve. A drag with no target ref is a free drag — resolve nothing.
|
|
260
280
|
const resolved = toRef !== '' ? refs.resolve(toRef) : null;
|
|
261
|
-
return await dragElement(el, resolved
|
|
281
|
+
return await dragElement(el, isHtmlElement(resolved) ? resolved : null, args['data']);
|
|
262
282
|
}
|
|
263
283
|
default:
|
|
264
284
|
throw new Error(`unknown action '${action}'`);
|
|
@@ -363,4 +383,3 @@ export async function executeSequence(steps) {
|
|
|
363
383
|
}
|
|
364
384
|
return { ok: true, count: steps.length, effects, steps: stepResults };
|
|
365
385
|
}
|
|
366
|
-
//# sourceMappingURL=actions.js.map
|
package/dist/actions/scroll.d.ts
CHANGED
package/dist/actions/scroll.js
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing a value into a field, and the two preconditions that make it honest.
|
|
3
|
+
*
|
|
4
|
+
* Lifted out of actions.ts when that file hit the 600-line cap. It is a cohesive unit: every export
|
|
5
|
+
* here exists so that a value write either lands as a real user's edit would, or is refused with a
|
|
6
|
+
* reason — never forced through while reporting success.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Set a value on a controlled input the way React expects (native setter + input event).
|
|
10
|
+
* Returns the `input` event's dispatchEvent result (defaultPrevented of the primary event).
|
|
11
|
+
*/
|
|
12
|
+
export declare function setNativeValue(el: HTMLInputElement | HTMLTextAreaElement, value: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Refuse to write to a field a person could not edit.
|
|
15
|
+
*
|
|
16
|
+
* `readonly` and `disabled` block USER input, not scripted assignment, so the prototype value setter
|
|
17
|
+
* writes straight through both. Measured: filling a readonly input reported `ok:true` and
|
|
18
|
+
* `valueChanged:true` with nothing in the effect block marking it read-only. That leaves the app in a
|
|
19
|
+
* state no user could reach, and every conclusion drawn from what follows is about software nobody
|
|
20
|
+
* can operate — the same reason the click path reports `occluded`, and the same choice Playwright
|
|
21
|
+
* makes when it refuses to fill a non-editable element.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* A rich-text target is UNSUPPORTED, not a mistake by the caller.
|
|
25
|
+
*
|
|
26
|
+
* Every rich-text editor (TipTap, Quill, ProseMirror, Slate, Lexical) is `[contenteditable]`, and
|
|
27
|
+
* these actions handle only input/textarea. `cannot fill a <div>` reads as "you picked the wrong
|
|
28
|
+
* element" and sends the reader hunting for a better selector that does not exist. Support is not
|
|
29
|
+
* faked: assigning textContent would update the DOM while the editor's own model kept the old value,
|
|
30
|
+
* so the tool would report success for content the app will never submit.
|
|
31
|
+
*/
|
|
32
|
+
export declare function assertNotRichText(el: HTMLElement, action: string): void;
|
|
33
|
+
export declare function assertEditable(el: HTMLInputElement | HTMLTextAreaElement, action: string): void;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { captureValueSetter } from '../patching/capture-method.js';
|
|
2
|
+
import { valuePrototypeOf } from '../dom/realm.js';
|
|
3
|
+
/**
|
|
4
|
+
* Writing a value into a field, and the two preconditions that make it honest.
|
|
5
|
+
*
|
|
6
|
+
* Lifted out of actions.ts when that file hit the 600-line cap. It is a cohesive unit: every export
|
|
7
|
+
* here exists so that a value write either lands as a real user's edit would, or is refused with a
|
|
8
|
+
* reason — never forced through while reporting success.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Set a value on a controlled input the way React expects (native setter + input event).
|
|
12
|
+
* Returns the `input` event's dispatchEvent result (defaultPrevented of the primary event).
|
|
13
|
+
*/
|
|
14
|
+
export function setNativeValue(el, value) {
|
|
15
|
+
// Realm-aware: a controlled input inside a same-origin frame has its own prototype.
|
|
16
|
+
const proto = valuePrototypeOf(el);
|
|
17
|
+
const setter = proto === undefined ? undefined : captureValueSetter(proto);
|
|
18
|
+
if (setter !== undefined) {
|
|
19
|
+
setter.call(el, value);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
el.value = value;
|
|
23
|
+
}
|
|
24
|
+
const notPrevented = el.dispatchEvent(new Event('input', { bubbles: true, cancelable: true }));
|
|
25
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
26
|
+
return !notPrevented;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Refuse to write to a field a person could not edit.
|
|
30
|
+
*
|
|
31
|
+
* `readonly` and `disabled` block USER input, not scripted assignment, so the prototype value setter
|
|
32
|
+
* writes straight through both. Measured: filling a readonly input reported `ok:true` and
|
|
33
|
+
* `valueChanged:true` with nothing in the effect block marking it read-only. That leaves the app in a
|
|
34
|
+
* state no user could reach, and every conclusion drawn from what follows is about software nobody
|
|
35
|
+
* can operate — the same reason the click path reports `occluded`, and the same choice Playwright
|
|
36
|
+
* makes when it refuses to fill a non-editable element.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* A rich-text target is UNSUPPORTED, not a mistake by the caller.
|
|
40
|
+
*
|
|
41
|
+
* Every rich-text editor (TipTap, Quill, ProseMirror, Slate, Lexical) is `[contenteditable]`, and
|
|
42
|
+
* these actions handle only input/textarea. `cannot fill a <div>` reads as "you picked the wrong
|
|
43
|
+
* element" and sends the reader hunting for a better selector that does not exist. Support is not
|
|
44
|
+
* faked: assigning textContent would update the DOM while the editor's own model kept the old value,
|
|
45
|
+
* so the tool would report success for content the app will never submit.
|
|
46
|
+
*/
|
|
47
|
+
export function assertNotRichText(el, action) {
|
|
48
|
+
// The ATTRIBUTE, not `isContentEditable`: jsdom does not implement the property, so a test would
|
|
49
|
+
// pass against a guard that never fires in the environment the unit suite runs in.
|
|
50
|
+
const flag = el.getAttribute('contenteditable');
|
|
51
|
+
if (el.isContentEditable || (flag !== null && flag !== 'false')) {
|
|
52
|
+
throw new Error(`cannot ${action} a contenteditable element — rich-text editors keep their own document model, ` +
|
|
53
|
+
'so writing to the DOM here would look right and submit the old content. This surface is not supported yet.');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function assertEditable(el, action) {
|
|
57
|
+
if (el.disabled) {
|
|
58
|
+
throw new Error(`cannot ${action} a disabled <${el.tagName.toLowerCase()}> — a user could not edit it, so forcing the value would put the app in a state nobody can reach`);
|
|
59
|
+
}
|
|
60
|
+
if (el.readOnly) {
|
|
61
|
+
throw new Error(`cannot ${action} a readonly <${el.tagName.toLowerCase()}> — a user could not edit it, so forcing the value would put the app in a state nobody can reach`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -4,4 +4,3 @@ export declare const RELOAD_CACHE_BUST_PARAM = "_reticle_reload";
|
|
|
4
4
|
export declare function resolveNavigationUrl(rawUrl: string, baseUrl: string): string | null;
|
|
5
5
|
/** Map browser command names to handlers. Used by the transport on each COMMAND. */
|
|
6
6
|
export declare function createCommandRegistry(): Map<string, CommandHandler>;
|
|
7
|
-
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -5,8 +5,10 @@ import { executeAction, executeSequence, dispatchWebMcp, } from '../actions/acti
|
|
|
5
5
|
import { describe } from '../dom/a11y.js';
|
|
6
6
|
import { themeReport } from '../dom/theme.js';
|
|
7
7
|
import { refs } from '../dom/refs.js';
|
|
8
|
+
import { isButton, isInput } from '../dom/realm.js';
|
|
8
9
|
import { hitTestOccluder } from '../dom/occlusion.js';
|
|
9
10
|
import { readStorage } from '../observers/storage.js';
|
|
11
|
+
import { captureDesktopWindow } from '../dom/desktop-capture.js';
|
|
10
12
|
import { identifyComponent, readComponentState } from '../registry/adapters.js';
|
|
11
13
|
import { readStoresWithTruncation, readStoresRaw, storeNames } from '../registry/stores.js';
|
|
12
14
|
import { sanitizeWithReport } from '../security/serialization.js';
|
|
@@ -24,6 +26,14 @@ function num(value) {
|
|
|
24
26
|
function record(value) {
|
|
25
27
|
return typeof value === 'object' && value !== null ? value : {};
|
|
26
28
|
}
|
|
29
|
+
function sourceLocation(value) {
|
|
30
|
+
if (typeof value !== 'object' || value === null)
|
|
31
|
+
return undefined;
|
|
32
|
+
const obj = value;
|
|
33
|
+
if (typeof obj['file'] !== 'string' || typeof obj['line'] !== 'number')
|
|
34
|
+
return undefined;
|
|
35
|
+
return { file: obj['file'], line: obj['line'], column: num(obj['column']) };
|
|
36
|
+
}
|
|
27
37
|
function queryFromArgs(args) {
|
|
28
38
|
return {
|
|
29
39
|
by: str(args['by']),
|
|
@@ -35,10 +45,12 @@ function queryFromArgs(args) {
|
|
|
35
45
|
placeholder: str(args['placeholder']),
|
|
36
46
|
testid: str(args['testid']),
|
|
37
47
|
alt: str(args['alt']),
|
|
48
|
+
component: str(args['component']),
|
|
38
49
|
scope: str(args['scope']),
|
|
39
50
|
attrs: Array.isArray(args['attrs'])
|
|
40
51
|
? args['attrs'].filter((a) => typeof a === 'string')
|
|
41
52
|
: undefined,
|
|
53
|
+
source: sourceLocation(args['source']),
|
|
42
54
|
};
|
|
43
55
|
}
|
|
44
56
|
function inspect(ref) {
|
|
@@ -66,12 +78,8 @@ function inspect(ref) {
|
|
|
66
78
|
...describe(el),
|
|
67
79
|
tag: el.tagName.toLowerCase(),
|
|
68
80
|
href: el.getAttribute('href') ?? undefined,
|
|
69
|
-
formAction: el
|
|
70
|
-
|
|
71
|
-
: undefined,
|
|
72
|
-
formText: el instanceof HTMLButtonElement || el instanceof HTMLInputElement
|
|
73
|
-
? (el.form?.textContent ?? undefined)
|
|
74
|
-
: undefined,
|
|
81
|
+
formAction: isButton(el) || isInput(el) ? (el.form?.getAttribute('action') ?? undefined) : undefined,
|
|
82
|
+
formText: isButton(el) || isInput(el) ? (el.form?.textContent ?? undefined) : undefined,
|
|
75
83
|
box: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
76
84
|
// True when another element sits over this one's center point — the click would hit the overlay,
|
|
77
85
|
// not this control (a z-index/overlay bug the DOM tree cannot show).
|
|
@@ -213,6 +221,7 @@ export function createCommandRegistry() {
|
|
|
213
221
|
reg.set(ReticleCommand.STATE_READ, (args) => readState(str(args['ref']), str(args['store']), str(args['path']), num(args['depth'])));
|
|
214
222
|
reg.set(ReticleCommand.STORAGE_READ, (args) => readStorage(str(args['area'])));
|
|
215
223
|
reg.set(ReticleCommand.CAPABILITIES, () => getCapabilities());
|
|
224
|
+
reg.set(ReticleCommand.CAPTURE, (args) => captureDesktopWindow(args['fullPage'] === true));
|
|
216
225
|
reg.set(ReticleCommand.SCROLL, (args) => {
|
|
217
226
|
const dy = args['dy'];
|
|
218
227
|
const fraction = args['fraction'];
|
|
@@ -242,4 +251,3 @@ export function createCommandRegistry() {
|
|
|
242
251
|
});
|
|
243
252
|
return reg;
|
|
244
253
|
}
|
|
245
|
-
//# sourceMappingURL=commands.js.map
|
package/dist/dom/a11y.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ElementState, type ElementDescriptor } from '@reticlehq/core';
|
|
2
2
|
/** Compute the ARIA role (explicit wins, else implicit from the tag). */
|
|
3
3
|
export declare function getRole(el: Element): string;
|
|
4
|
-
/** Accessible name via a practical subset of the accname algorithm. */
|
|
5
4
|
export declare function getAccessibleName(el: Element): string;
|
|
6
5
|
/**
|
|
7
6
|
* The set of states relevant to assertions. `visible` is an O(depth) forced-style walk; callers that
|
|
@@ -28,4 +27,3 @@ export declare function isVisible(el: Element, memo?: Map<Element, boolean>): bo
|
|
|
28
27
|
/** Build the compact descriptor surfaced to the agent. `memo` (optional) shares the per-call
|
|
29
28
|
* visibility cache with the query's state filter so ancestors aren't re-walked per element. */
|
|
30
29
|
export declare function describe(el: Element, memo?: Map<Element, boolean>): ElementDescriptor;
|
|
31
|
-
//# sourceMappingURL=a11y.d.ts.map
|
package/dist/dom/a11y.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { ElementState, REDACTED_VALUE } from '@reticlehq/core';
|
|
2
|
+
import { isButton, isHtmlElement, isImage, isInput, isSelect, isTextArea } from './realm.js';
|
|
2
3
|
import { refs } from './refs.js';
|
|
3
4
|
import { inspectChart } from './chart.js';
|
|
4
5
|
import { isSensitiveKey } from '../security/serialization.js';
|
|
5
6
|
import { formatSource, sourceFromDom } from './source.js';
|
|
6
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Roles whose accessible name comes from their text content (ARIA's `nameFrom: author content`).
|
|
9
|
+
*
|
|
10
|
+
* `radio`, `checkbox`, `row` and `tooltip` were missing, and the gap is not cosmetic: a segmented
|
|
11
|
+
* filter written as `<button role="radio">held</button>` — an extremely ordinary design-system
|
|
12
|
+
* control — reported as a nameless `radio`, so `by: role` + name could not address it at all and an
|
|
13
|
+
* agent had to fall back to a testid the app has no reason to carry. Measured on a shipments console:
|
|
14
|
+
* six filters, six nameless radios, none reachable by name.
|
|
15
|
+
*
|
|
16
|
+
* `listitem`, `status` and `alert` are NOT in the spec's list. They are kept as a deliberate
|
|
17
|
+
* over-approximation — a name computed from content is more useful than no name — and removing them
|
|
18
|
+
* has no measured symptom to justify the risk.
|
|
19
|
+
*/
|
|
7
20
|
const NAME_FROM_CONTENT = new Set([
|
|
8
21
|
'button',
|
|
9
22
|
'link',
|
|
@@ -11,9 +24,13 @@ const NAME_FROM_CONTENT = new Set([
|
|
|
11
24
|
'option',
|
|
12
25
|
'listitem',
|
|
13
26
|
'cell',
|
|
27
|
+
'checkbox',
|
|
14
28
|
'columnheader',
|
|
29
|
+
'radio',
|
|
30
|
+
'row',
|
|
15
31
|
'rowheader',
|
|
16
32
|
'tab',
|
|
33
|
+
'tooltip',
|
|
17
34
|
'menuitem',
|
|
18
35
|
'menuitemcheckbox',
|
|
19
36
|
'menuitemradio',
|
|
@@ -104,12 +121,36 @@ function labelledByText(el) {
|
|
|
104
121
|
for (const id of ids.split(/\s+/)) {
|
|
105
122
|
const ref = el.ownerDocument.getElementById(id);
|
|
106
123
|
if (ref !== null)
|
|
107
|
-
parts.push(collapse(ref
|
|
124
|
+
parts.push(collapse(textWithoutHidden(ref)));
|
|
108
125
|
}
|
|
109
126
|
const joined = parts.join(' ').trim();
|
|
110
127
|
return joined.length > 0 ? joined : null;
|
|
111
128
|
}
|
|
112
129
|
/** Accessible name via a practical subset of the accname algorithm. */
|
|
130
|
+
/** Accessible name via a practical subset of the accname algorithm. */
|
|
131
|
+
/**
|
|
132
|
+
* Text content with `aria-hidden` subtrees removed.
|
|
133
|
+
*
|
|
134
|
+
* The accessible-name spec excludes them, and — decisively — so does dom-accessibility-api, which is
|
|
135
|
+
* what `by: role` + name MATCHES against via testing-library. Reading raw `textContent` here made the
|
|
136
|
+
* name we REPORT differ from the name that can be SELECTED: Material UI renders its required-field
|
|
137
|
+
* marker as `<span aria-hidden="true"> *</span>`, so a login field was reported as `"Username *"` and
|
|
138
|
+
* addressable only as `"Username"`. Reporting a name the agent cannot use defeats the purpose of
|
|
139
|
+
* reporting it at all, so both must come from the same rule.
|
|
140
|
+
*/
|
|
141
|
+
function textWithoutHidden(node) {
|
|
142
|
+
if (node.nodeType === Node.TEXT_NODE)
|
|
143
|
+
return node.textContent ?? '';
|
|
144
|
+
if (node.nodeType !== Node.ELEMENT_NODE)
|
|
145
|
+
return '';
|
|
146
|
+
const el = node;
|
|
147
|
+
if (el.getAttribute('aria-hidden') === 'true')
|
|
148
|
+
return '';
|
|
149
|
+
let out = '';
|
|
150
|
+
for (const child of el.childNodes)
|
|
151
|
+
out += textWithoutHidden(child);
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
113
154
|
export function getAccessibleName(el) {
|
|
114
155
|
const labelled = labelledByText(el);
|
|
115
156
|
if (labelled !== null)
|
|
@@ -117,31 +158,29 @@ export function getAccessibleName(el) {
|
|
|
117
158
|
const ariaLabel = el.getAttribute('aria-label');
|
|
118
159
|
if (ariaLabel !== null && ariaLabel.trim().length > 0)
|
|
119
160
|
return ariaLabel.trim();
|
|
120
|
-
if (el
|
|
161
|
+
if (isImage(el)) {
|
|
121
162
|
const alt = el.getAttribute('alt');
|
|
122
163
|
if (alt !== null)
|
|
123
164
|
return alt.trim();
|
|
124
165
|
}
|
|
125
|
-
if (el
|
|
126
|
-
el instanceof HTMLTextAreaElement ||
|
|
127
|
-
el instanceof HTMLSelectElement) {
|
|
166
|
+
if (isInput(el) || isTextArea(el) || isSelect(el)) {
|
|
128
167
|
const labels = el.labels;
|
|
129
168
|
if (labels !== null && labels.length > 0) {
|
|
130
169
|
const text = [...labels]
|
|
131
|
-
.map((l) => collapse(l
|
|
170
|
+
.map((l) => collapse(textWithoutHidden(l)))
|
|
132
171
|
.join(' ')
|
|
133
172
|
.trim();
|
|
134
173
|
if (text.length > 0)
|
|
135
174
|
return text;
|
|
136
175
|
}
|
|
137
|
-
if (el
|
|
176
|
+
if (isInput(el) || isTextArea(el)) {
|
|
138
177
|
const placeholder = el.getAttribute('placeholder');
|
|
139
178
|
if (placeholder !== null && placeholder.trim().length > 0)
|
|
140
179
|
return placeholder.trim();
|
|
141
180
|
}
|
|
142
181
|
}
|
|
143
182
|
if (NAME_FROM_CONTENT.has(getRole(el))) {
|
|
144
|
-
const text = collapse(el
|
|
183
|
+
const text = collapse(textWithoutHidden(el));
|
|
145
184
|
if (text.length > 0)
|
|
146
185
|
return text;
|
|
147
186
|
}
|
|
@@ -163,14 +202,10 @@ function ariaBool(el, attr) {
|
|
|
163
202
|
export function getStates(el, visible = isVisible(el)) {
|
|
164
203
|
const states = [ElementState.PRESENT];
|
|
165
204
|
states.push(visible ? ElementState.VISIBLE : ElementState.HIDDEN);
|
|
166
|
-
const disabledProp = (el
|
|
167
|
-
el instanceof HTMLInputElement ||
|
|
168
|
-
el instanceof HTMLSelectElement ||
|
|
169
|
-
el instanceof HTMLTextAreaElement) &&
|
|
170
|
-
el.disabled;
|
|
205
|
+
const disabledProp = (isButton(el) || isInput(el) || isSelect(el) || isTextArea(el)) && el.disabled;
|
|
171
206
|
const disabled = disabledProp || ariaBool(el, 'aria-disabled') === true;
|
|
172
207
|
states.push(disabled ? ElementState.DISABLED : ElementState.ENABLED);
|
|
173
|
-
const checkedProp = el
|
|
208
|
+
const checkedProp = isInput(el) && (el.type === 'checkbox' || el.type === 'radio') && el.checked;
|
|
174
209
|
if (checkedProp || ariaBool(el, 'aria-checked') === true)
|
|
175
210
|
states.push(ElementState.CHECKED);
|
|
176
211
|
if (ariaBool(el, 'aria-expanded') === true)
|
|
@@ -186,9 +221,7 @@ export function getStates(el, visible = isVisible(el)) {
|
|
|
186
221
|
* flow recorder's fill-value redaction, so recorded flows never persist typed passwords/OTPs/keys.
|
|
187
222
|
*/
|
|
188
223
|
export function isSensitiveField(el) {
|
|
189
|
-
if (!(el
|
|
190
|
-
!(el instanceof HTMLTextAreaElement) &&
|
|
191
|
-
!(el instanceof HTMLSelectElement)) {
|
|
224
|
+
if (!isInput(el) && !isTextArea(el) && !isSelect(el)) {
|
|
192
225
|
return false;
|
|
193
226
|
}
|
|
194
227
|
const autocomplete = el.getAttribute('autocomplete') ?? '';
|
|
@@ -199,14 +232,12 @@ export function isSensitiveField(el) {
|
|
|
199
232
|
el.getAttribute('aria-label') ?? '',
|
|
200
233
|
];
|
|
201
234
|
const sensitiveAutocomplete = /current-password|new-password|cc-number|cc-csc|one-time-code/i.test(autocomplete);
|
|
202
|
-
return ((el
|
|
235
|
+
return ((isInput(el) && el.type.toLowerCase() === 'password') ||
|
|
203
236
|
sensitiveAutocomplete ||
|
|
204
237
|
identifiers.some(isSensitiveKey));
|
|
205
238
|
}
|
|
206
239
|
export function getValue(el) {
|
|
207
|
-
if (el
|
|
208
|
-
el instanceof HTMLTextAreaElement ||
|
|
209
|
-
el instanceof HTMLSelectElement) {
|
|
240
|
+
if (isInput(el) || isTextArea(el) || isSelect(el)) {
|
|
210
241
|
if (isSensitiveField(el))
|
|
211
242
|
return REDACTED_VALUE;
|
|
212
243
|
return el.value;
|
|
@@ -218,7 +249,7 @@ export function getValue(el) {
|
|
|
218
249
|
function selfHidden(el) {
|
|
219
250
|
if (el.getAttribute('aria-hidden') === 'true')
|
|
220
251
|
return true;
|
|
221
|
-
if (el
|
|
252
|
+
if (isHtmlElement(el) && el.hidden)
|
|
222
253
|
return true;
|
|
223
254
|
const view = el.ownerDocument.defaultView;
|
|
224
255
|
if (view !== null) {
|
|
@@ -296,4 +327,3 @@ export function describe(el, memo) {
|
|
|
296
327
|
}
|
|
297
328
|
return base;
|
|
298
329
|
}
|
|
299
|
-
//# sourceMappingURL=a11y.js.map
|
package/dist/dom/auto-anchor.js
CHANGED
|
@@ -11,4 +11,3 @@ export declare function classifyCrossOriginFrames(frames: ReadonlyArray<{
|
|
|
11
11
|
}>): number;
|
|
12
12
|
/** Probe the live document: count iframes whose document is unreachable (cross-origin). */
|
|
13
13
|
export declare function countCrossOriginFrames(): number;
|
|
14
|
-
//# sourceMappingURL=blind-spots.d.ts.map
|
package/dist/dom/blind-spots.js
CHANGED
|
@@ -15,7 +15,7 @@ export function countCrossOriginFrames() {
|
|
|
15
15
|
const frames = [];
|
|
16
16
|
for (const frame of document.querySelectorAll('iframe')) {
|
|
17
17
|
const hasSrc = frame.getAttribute('src') !== null && frame.getAttribute('src') !== '';
|
|
18
|
-
let sameOrigin
|
|
18
|
+
let sameOrigin;
|
|
19
19
|
try {
|
|
20
20
|
// Reaching contentDocument throws (or yields null) for a cross-origin frame.
|
|
21
21
|
sameOrigin = frame.contentDocument !== null;
|
|
@@ -27,4 +27,3 @@ export function countCrossOriginFrames() {
|
|
|
27
27
|
}
|
|
28
28
|
return classifyCrossOriginFrames(frames);
|
|
29
29
|
}
|
|
30
|
-
//# sourceMappingURL=blind-spots.js.map
|
package/dist/dom/chart.d.ts
CHANGED
package/dist/dom/chart.js
CHANGED