browser-debugger-cli 0.9.0 → 0.11.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/.claude/skills/bdg/SKILL.md +268 -0
- package/README.md +15 -1
- package/dist/commands/dom/a11y.js +2 -1
- package/dist/commands/dom/formInteraction.js +56 -25
- package/dist/commands/dom/helpers/keyAttributes.d.ts +20 -0
- package/dist/commands/dom/helpers/keyAttributes.js +54 -0
- package/dist/commands/dom/helpers/query.d.ts +1 -1
- package/dist/commands/dom/helpers/query.js +66 -19
- package/dist/commands/dom/helpers/runElementCommand.js +4 -3
- package/dist/commands/dom/helpers/screenshot.js +85 -12
- package/dist/commands/dom/index.d.ts +1 -0
- package/dist/commands/dom/index.js +8 -3
- package/dist/commands/dom/inspect.d.ts +15 -0
- package/dist/commands/dom/inspect.js +82 -0
- package/dist/commands/dom/layout.js +2 -2
- package/dist/commands/dom/listeners.js +2 -2
- package/dist/commands/dom/semanticUtils.d.ts +14 -1
- package/dist/commands/dom/semanticUtils.js +44 -3
- package/dist/commands/installSkill.d.ts +20 -0
- package/dist/commands/installSkill.js +87 -0
- package/dist/commands/network/list.js +13 -2
- package/dist/commands/optionBehaviors.js +48 -6
- package/dist/commands/page.d.ts +1 -1
- package/dist/commands/page.js +62 -3
- package/dist/commands/shared/commonOptions.d.ts +4 -0
- package/dist/commands/shared/commonOptions.js +9 -0
- package/dist/commands/shared/optionTypes.d.ts +21 -0
- package/dist/commands/shared/startHelpers.d.ts +66 -0
- package/dist/commands/shared/startHelpers.js +91 -10
- package/dist/commands/shared/validation.d.ts +11 -0
- package/dist/commands/shared/validation.js +16 -0
- package/dist/commands.js +3 -0
- package/dist/daemon/launcher.d.ts +8 -1
- package/dist/daemon/launcher.js +3 -1
- package/dist/daemon/session/Session.d.ts +7 -0
- package/dist/daemon/session/Session.js +23 -1
- package/dist/daemon/session/commandRegistry.d.ts +14 -1
- package/dist/daemon/session/commandRegistry.js +65 -9
- package/dist/daemon/session/interactions.d.ts +18 -5
- package/dist/daemon/session/interactions.js +22 -12
- package/dist/daemon.js +3565 -329
- package/dist/errors/messages.d.ts +85 -0
- package/dist/errors/messages.js +128 -1
- package/dist/index.js +2151 -960
- package/dist/ipc/client.d.ts +9 -0
- package/dist/ipc/client.js +13 -0
- package/dist/ipc/protocol/commands.d.ts +56 -1
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +35 -2
- package/dist/ipc/protocol/inspectTypes.d.ts +388 -0
- package/dist/ipc/protocol/inspectTypes.js +10 -0
- package/dist/runtime/dom/actionEffects.d.ts +94 -15
- package/dist/runtime/dom/actionEffects.js +173 -27
- package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -14
- package/dist/runtime/dom/actionEffectsScripts.js +224 -32
- package/dist/runtime/dom/elementInfo.d.ts +26 -0
- package/dist/runtime/dom/elementInfo.js +65 -0
- package/dist/runtime/dom/eventListeners.js +14 -4
- package/dist/runtime/dom/formFillHelpers/fill.d.ts +3 -4
- package/dist/runtime/dom/formFillHelpers/fill.js +77 -28
- package/dist/runtime/dom/frameSelection.d.ts +11 -0
- package/dist/runtime/dom/frameSelection.js +20 -1
- package/dist/runtime/dom/frames.d.ts +38 -5
- package/dist/runtime/dom/frames.js +136 -21
- package/dist/runtime/dom/inspect.d.ts +28 -0
- package/dist/runtime/dom/inspect.js +557 -0
- package/dist/runtime/dom/inspectAllStyles.d.ts +62 -0
- package/dist/runtime/dom/inspectAllStyles.js +385 -0
- package/dist/runtime/dom/inspectCascade.d.ts +94 -0
- package/dist/runtime/dom/inspectCascade.js +371 -0
- package/dist/runtime/dom/inspectCascadeModel.d.ts +39 -0
- package/dist/runtime/dom/inspectCascadeModel.js +232 -0
- package/dist/runtime/dom/inspectHints.d.ts +62 -0
- package/dist/runtime/dom/inspectHints.js +305 -0
- package/dist/runtime/dom/inspectLayoutModel.d.ts +87 -0
- package/dist/runtime/dom/inspectLayoutModel.js +346 -0
- package/dist/runtime/dom/inspectModel.d.ts +74 -0
- package/dist/runtime/dom/inspectModel.js +184 -0
- package/dist/runtime/dom/inspectPaintModel.d.ts +157 -0
- package/dist/runtime/dom/inspectPaintModel.js +461 -0
- package/dist/runtime/dom/inspectRules.d.ts +37 -0
- package/dist/runtime/dom/inspectRules.js +101 -0
- package/dist/runtime/dom/inspectScripts.d.ts +132 -0
- package/dist/runtime/dom/inspectScripts.js +263 -0
- package/dist/runtime/dom/inspectTree.d.ts +40 -0
- package/dist/runtime/dom/inspectTree.js +134 -0
- package/dist/runtime/dom/inspectVariables.d.ts +33 -0
- package/dist/runtime/dom/inspectVariables.js +94 -0
- package/dist/runtime/dom/inspectWhyModel.d.ts +20 -0
- package/dist/runtime/dom/inspectWhyModel.js +134 -0
- package/dist/runtime/dom/layout.d.ts +5 -1
- package/dist/runtime/dom/layout.js +10 -3
- package/dist/runtime/dom/listenerPageScripts.d.ts +11 -5
- package/dist/runtime/dom/listenerPageScripts.js +95 -9
- package/dist/runtime/dom/listenerSummary.d.ts +4 -0
- package/dist/runtime/dom/listenerSummary.js +26 -9
- package/dist/runtime/dom/reactEventHelpers.d.ts +5 -0
- package/dist/runtime/dom/reactEventHelpers.js +12 -4
- package/dist/runtime/page/emulation.d.ts +20 -0
- package/dist/runtime/page/emulation.js +37 -0
- package/dist/telemetry/a11y.d.ts +10 -0
- package/dist/telemetry/a11y.js +78 -1
- package/dist/telemetry/console.d.ts +1 -0
- package/dist/telemetry/console.js +100 -5
- package/dist/telemetry/network.js +3 -1
- package/dist/types.d.ts +40 -0
- package/dist/ui/formatters/details.d.ts +8 -0
- package/dist/ui/formatters/details.js +59 -3
- package/dist/ui/formatters/dom.d.ts +2 -1
- package/dist/ui/formatters/dom.js +25 -9
- package/dist/ui/formatters/inspect.d.ts +39 -0
- package/dist/ui/formatters/inspect.js +596 -0
- package/dist/ui/formatters/installSkill.d.ts +11 -0
- package/dist/ui/formatters/installSkill.js +31 -0
- package/dist/ui/formatters/keyAttributes.d.ts +19 -0
- package/dist/ui/formatters/keyAttributes.js +84 -0
- package/dist/ui/formatters/layout.js +2 -2
- package/dist/ui/formatters/networkHeaders.d.ts +13 -0
- package/dist/ui/formatters/networkHeaders.js +23 -3
- package/dist/ui/formatters/networkList.d.ts +29 -1
- package/dist/ui/formatters/networkList.js +86 -20
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/formatting.d.ts +9 -0
- package/dist/ui/formatting.js +6 -3
- package/dist/ui/messages/commands.d.ts +123 -7
- package/dist/ui/messages/commands.js +181 -10
- package/dist/ui/messages/networkMessages.d.ts +14 -0
- package/dist/ui/messages/networkMessages.js +18 -0
- package/dist/ui/messages/session.d.ts +14 -0
- package/dist/ui/messages/session.js +20 -0
- package/dist/utils/async.d.ts +9 -0
- package/dist/utils/async.js +17 -0
- package/dist/utils/color.d.ts +84 -0
- package/dist/utils/color.js +376 -0
- package/dist/utils/cssValues.d.ts +109 -0
- package/dist/utils/cssValues.js +236 -0
- package/dist/utils/selectorFilters.d.ts +12 -0
- package/dist/utils/selectorFilters.js +29 -0
- package/package.json +2 -1
|
@@ -9,18 +9,19 @@
|
|
|
9
9
|
* bdg invocations. Selectors are matched in the page, including open shadow
|
|
10
10
|
* roots and same-origin iframes, like a user sees the page.
|
|
11
11
|
*/
|
|
12
|
+
import { keyAttributes } from './keyAttributes.js';
|
|
12
13
|
import { CommandError } from '../../../errors/index.js';
|
|
13
14
|
import { noNodesFoundError, indexOutOfRangeError, eitherArgumentRequiredError, invalidSelectorError, nodeIdNotFoundError, operationFailedError, similarSelectorsLine, staleNodeError, } from '../../../errors/messages.js';
|
|
14
15
|
import { callCDP } from '../../../ipc/client.js';
|
|
15
16
|
import { ELEMENT_GEOMETRY_JS, VIEWPORT_SIZE_JS, classifyViewportPosition, } from '../../../runtime/dom/elementGeometry.js';
|
|
16
|
-
import { ELEMENT_CONTEXT_JS, ELEMENT_TEXT_JS, ELEMENT_TEXT_LENGTH, textPreview, } from '../../../runtime/dom/elementInfo.js';
|
|
17
|
+
import { ELEMENT_CONTEXT_JS, ELEMENT_STATE_JS, ELEMENT_TEXT_JS, ELEMENT_TEXT_LENGTH, textPreview, } from '../../../runtime/dom/elementInfo.js';
|
|
17
18
|
import { DEEP_QUERY_JS, UNSEARCHED_CONTENT_JS, pageNamesJS, selectorArgsJS, } from '../../../runtime/dom/targetNode.js';
|
|
18
19
|
import { createLogger } from '../../../ui/logging/index.js';
|
|
19
20
|
import { sessionCommand } from '../../../ui/messages/sessionCommand.js';
|
|
20
21
|
import { ConcurrencyLimiter } from '../../../utils/concurrency.js';
|
|
21
22
|
import { getErrorMessage } from '../../../utils/errors.js';
|
|
22
23
|
import { EXIT_CODES } from '../../../utils/exitCodes.js';
|
|
23
|
-
import { parseSelectorFilters, withoutVisibleFilters } from '../../../utils/selectorFilters.js';
|
|
24
|
+
import { leadingCompounds, parseSelectorFilters, withoutVisibleFilters, } from '../../../utils/selectorFilters.js';
|
|
24
25
|
import { findSimilarNames, parseSingleNameSelector } from '../../../utils/suggestions.js';
|
|
25
26
|
const log = createLogger('dom');
|
|
26
27
|
/** Maximum concurrent CDP calls to avoid overwhelming the connection. */
|
|
@@ -113,6 +114,34 @@ async function withSelection(selector, use) {
|
|
|
113
114
|
export async function noMatchesError(selector) {
|
|
114
115
|
return noNodesFoundError(selector, await noMatchContext(selector));
|
|
115
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Page-side index of the first leading compound that matches a shadow host
|
|
119
|
+
* (an element with an open shadow root, anywhere a selector reaches) while
|
|
120
|
+
* the selector after it finds something: then the selector only failed by
|
|
121
|
+
* crossing into the shadow root.
|
|
122
|
+
*
|
|
123
|
+
* @param compounds - Leading compounds of the selector
|
|
124
|
+
* @returns Expression evaluating to the index, or -1
|
|
125
|
+
*/
|
|
126
|
+
function shadowHostJS(compounds) {
|
|
127
|
+
const plain = compounds
|
|
128
|
+
.slice(0, MAX_HOST_COMPOUNDS)
|
|
129
|
+
.map((entry) => [withoutFilters(entry.compound), withoutFilters(entry.rest)]);
|
|
130
|
+
return `${JSON.stringify(plain)}.findIndex(([host, rest]) => { try { const deep = ${DEEP_QUERY_JS}; return deep(host, null).some((el) => el.shadowRoot) && deep(rest, null).length > 0; } catch (e) { return false; } })`;
|
|
131
|
+
}
|
|
132
|
+
/** Leading compounds checked for a shadow host (each check walks the page) */
|
|
133
|
+
const MAX_HOST_COMPOUNDS = 4;
|
|
134
|
+
/**
|
|
135
|
+
* A selector without bdg's filters (`:visible`, `:has-text()`, `:text-is()`),
|
|
136
|
+
* which plain CSS does not know.
|
|
137
|
+
*
|
|
138
|
+
* @param selector - Selector
|
|
139
|
+
* @returns Plain CSS
|
|
140
|
+
*/
|
|
141
|
+
function withoutFilters(selector) {
|
|
142
|
+
return (selector.replace(/:visible\b|:(has-text|text-is)\((?:"[^"]*"|'[^']*'|[^)"'])*\)/g, '').trim() ||
|
|
143
|
+
'*');
|
|
144
|
+
}
|
|
116
145
|
/**
|
|
117
146
|
* What the page says about a selector that matched nothing, in one
|
|
118
147
|
* evaluation: whether it is still loading, how many elements match with the
|
|
@@ -131,9 +160,11 @@ export async function noMatchContext(selector) {
|
|
|
131
160
|
? `(() => { try { return (${DEEP_QUERY_JS})(${JSON.stringify(selector)}, ${JSON.stringify(unfiltered)}).length; } catch (e) { return 0; } })()`
|
|
132
161
|
: '0';
|
|
133
162
|
const names = single ? pageNamesJS(single.kind) : '[]';
|
|
163
|
+
const compounds = leadingCompounds(selector);
|
|
164
|
+
const shadowHost = compounds.length > 0 ? shadowHostJS(compounds) : '-1';
|
|
134
165
|
try {
|
|
135
166
|
const evaluated = await callCDP('Runtime.evaluate', {
|
|
136
|
-
expression: `({ hidden: ${hidden}, readyState: document.readyState, unsearched: ${UNSEARCHED_CONTENT_JS}, names: ${names} })`,
|
|
167
|
+
expression: `({ hidden: ${hidden}, readyState: document.readyState, unsearched: ${UNSEARCHED_CONTENT_JS}, names: ${names}, shadowHost: ${shadowHost} })`,
|
|
137
168
|
returnByValue: true,
|
|
138
169
|
});
|
|
139
170
|
const { result } = (evaluated.data?.result ?? {});
|
|
@@ -151,6 +182,8 @@ export async function noMatchContext(selector) {
|
|
|
151
182
|
},
|
|
152
183
|
}),
|
|
153
184
|
...(similar && { similar }),
|
|
185
|
+
...(typeof value.shadowHost === 'number' &&
|
|
186
|
+
compounds[value.shadowHost] && { shadowHost: compounds[value.shadowHost] }),
|
|
154
187
|
};
|
|
155
188
|
}
|
|
156
189
|
catch (error) {
|
|
@@ -182,7 +215,8 @@ export async function documentReadyState() {
|
|
|
182
215
|
const VIEWPORT_HINT_LIMIT = 100;
|
|
183
216
|
/**
|
|
184
217
|
* Where each element of a page-side array lives (an iframe and/or a shadow
|
|
185
|
-
* root), its text
|
|
218
|
+
* root), its text, its form control state ({@link ELEMENT_STATE_JS}) and, for
|
|
219
|
+
* the first {@link VIEWPORT_HINT_LIMIT}, its position
|
|
186
220
|
* relative to the viewport, plus the viewport size. An element that cannot be
|
|
187
221
|
* read gets empty details instead of failing the whole query.
|
|
188
222
|
*/
|
|
@@ -190,9 +224,10 @@ const ELEMENT_DETAILS_FUNCTION = `function () {
|
|
|
190
224
|
const contextOf = ${ELEMENT_CONTEXT_JS};
|
|
191
225
|
const textOf = ${ELEMENT_TEXT_JS};
|
|
192
226
|
const geometryOf = ${ELEMENT_GEOMETRY_JS};
|
|
227
|
+
const stateOf = ${ELEMENT_STATE_JS};
|
|
193
228
|
const read = (el, index) => {
|
|
194
229
|
try {
|
|
195
|
-
return { context: contextOf(el), text: textOf(el), geometry: index < ${VIEWPORT_HINT_LIMIT} ? geometryOf(el) : null };
|
|
230
|
+
return { context: contextOf(el), text: textOf(el), state: stateOf(el), geometry: index < ${VIEWPORT_HINT_LIMIT} ? geometryOf(el) : null };
|
|
196
231
|
} catch (e) {
|
|
197
232
|
return {};
|
|
198
233
|
}
|
|
@@ -219,6 +254,7 @@ async function elementsWithDetails(arrayObjectId) {
|
|
|
219
254
|
backendNodeId,
|
|
220
255
|
context: details?.context ?? '',
|
|
221
256
|
text: details?.text ?? '',
|
|
257
|
+
state: details?.state ?? {},
|
|
222
258
|
...viewportHint(details?.geometry, viewport),
|
|
223
259
|
},
|
|
224
260
|
];
|
|
@@ -334,18 +370,21 @@ export async function queryDOMElements(selector) {
|
|
|
334
370
|
log.debug(`Querying ${elements.length} elements with selector: ${selector}`);
|
|
335
371
|
}
|
|
336
372
|
const nodes = await mapConcurrently(elements, async (element, index) => {
|
|
337
|
-
const { backendNodeId, context, text, inViewport, clippedBy } = element;
|
|
373
|
+
const { backendNodeId, context, text, state, inViewport, clippedBy } = element;
|
|
338
374
|
const desc = await describeNode({ backendNodeId });
|
|
339
375
|
if (!desc)
|
|
340
376
|
return { index, nodeId: 0 };
|
|
341
377
|
const attributes = unpackAttributes(desc.attributes);
|
|
342
378
|
const classes = attributes['class']?.split(/\s+/).filter(Boolean);
|
|
343
379
|
const preview = textPreview(text);
|
|
380
|
+
const tag = desc.nodeName.toLowerCase();
|
|
381
|
+
const keys = keyAttributes(tag, attributes, state);
|
|
344
382
|
return {
|
|
345
383
|
index,
|
|
346
384
|
nodeId: desc.backendNodeId,
|
|
347
|
-
tag
|
|
385
|
+
tag,
|
|
348
386
|
...identifyingAttributes(attributes, desc.nodeName),
|
|
387
|
+
...(keys && { attributes: keys }),
|
|
349
388
|
...(classes && { classes }),
|
|
350
389
|
...(preview && { preview }),
|
|
351
390
|
...(context && { context }),
|
|
@@ -372,36 +411,39 @@ function identifyingAttributes(attributes, nodeName) {
|
|
|
372
411
|
};
|
|
373
412
|
}
|
|
374
413
|
/**
|
|
375
|
-
* The text of one element as the page renders it ({@link ELEMENT_TEXT_JS})
|
|
414
|
+
* The text of one element as the page renders it ({@link ELEMENT_TEXT_JS})
|
|
415
|
+
* and its form control state ({@link ELEMENT_STATE_JS}).
|
|
376
416
|
*
|
|
377
417
|
* @param ref - Node reference
|
|
378
418
|
* @param full - Read all of a large container's text, not just its start
|
|
379
|
-
* @returns Element text,
|
|
419
|
+
* @returns Element text and state, empty when the node cannot be read
|
|
380
420
|
*/
|
|
381
|
-
async function
|
|
421
|
+
async function elementTextAndState(ref, full) {
|
|
382
422
|
const objectGroup = `bdg-text-${process.pid}-${++queryCount}`;
|
|
383
423
|
const resolved = await callCDP('DOM.resolveNode', { ...ref, objectGroup });
|
|
384
424
|
const objectId = resolved.data?.result?.object
|
|
385
425
|
.objectId;
|
|
386
426
|
if (!objectId)
|
|
387
|
-
return '';
|
|
427
|
+
return { text: '', state: {} };
|
|
388
428
|
try {
|
|
389
429
|
const response = await callCDP('Runtime.callFunctionOn', {
|
|
390
430
|
objectId,
|
|
391
|
-
functionDeclaration: `function (full) { return (${ELEMENT_TEXT_JS})(this, full); }`,
|
|
431
|
+
functionDeclaration: `function (full) { return { text: (${ELEMENT_TEXT_JS})(this, full), state: (${ELEMENT_STATE_JS})(this) }; }`,
|
|
392
432
|
arguments: [{ value: full }],
|
|
393
433
|
returnByValue: true,
|
|
394
434
|
});
|
|
395
|
-
const value = response.data?.result?.result
|
|
396
|
-
|
|
397
|
-
|
|
435
|
+
const value = response.data?.result?.result?.value;
|
|
436
|
+
return {
|
|
437
|
+
text: typeof value?.text === 'string' ? value.text : '',
|
|
438
|
+
state: value?.state ?? {},
|
|
439
|
+
};
|
|
398
440
|
}
|
|
399
441
|
finally {
|
|
400
442
|
await callCDP('Runtime.releaseObjectGroup', { objectGroup });
|
|
401
443
|
}
|
|
402
444
|
}
|
|
403
445
|
/**
|
|
404
|
-
* Get DOM context (tag, classes, text preview) for a node: a one-line
|
|
446
|
+
* Get DOM context (tag, classes, key attributes, text preview) for a node: a one-line
|
|
405
447
|
* preview, and up to {@link ELEMENT_TEXT_LENGTH} characters of text when it
|
|
406
448
|
* is longer (all of it with `full`).
|
|
407
449
|
*
|
|
@@ -417,13 +459,18 @@ export async function getDomContext(ref, options = {}) {
|
|
|
417
459
|
return null;
|
|
418
460
|
}
|
|
419
461
|
const full = options.full === true;
|
|
420
|
-
const
|
|
421
|
-
const
|
|
462
|
+
const attributes = unpackAttributes(desc.attributes);
|
|
463
|
+
const classes = attributes['class']?.split(/\s+/).filter(Boolean);
|
|
464
|
+
const { text, state } = await elementTextAndState(ref, full);
|
|
422
465
|
const preview = textPreview(text);
|
|
423
466
|
const longer = textPreview(text, full ? Number.POSITIVE_INFINITY : ELEMENT_TEXT_LENGTH);
|
|
467
|
+
const tag = desc.nodeName.toLowerCase();
|
|
468
|
+
const keys = keyAttributes(tag, attributes, state);
|
|
424
469
|
return {
|
|
425
|
-
tag
|
|
470
|
+
tag,
|
|
426
471
|
...(classes && classes.length > 0 && { classes }),
|
|
472
|
+
...(keys && { attributes: keys }),
|
|
473
|
+
...(state.sensitive && { sensitive: true }),
|
|
427
474
|
...(preview && { preview }),
|
|
428
475
|
...(longer !== preview && { text: longer }),
|
|
429
476
|
...(!preview && (await childElements(ref))),
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { DomElementResolver } from '../DomElementResolver.js';
|
|
8
8
|
import { noMatchContext } from './query.js';
|
|
9
|
-
import { otherIndexSourceNote, staleNodeError, unreachableElementsNote, withLoadingHint, } from '../../../errors/messages.js';
|
|
9
|
+
import { otherIndexSourceNote, staleNodeError, shadowBoundaryLine, unreachableElementsNote, withLoadingHint, } from '../../../errors/messages.js';
|
|
10
10
|
import { joinLines } from '../../../ui/formatting.js';
|
|
11
11
|
import { EXIT_CODES } from '../../../utils/exitCodes.js';
|
|
12
12
|
/**
|
|
@@ -110,7 +110,8 @@ function failedResultFailure(result, options) {
|
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
112
|
* Add what the page says to a "not found" failure (one page evaluation, on
|
|
113
|
-
* this failure path only, {@link noMatchContext}): similar ids or classes,
|
|
113
|
+
* this failure path only, {@link noMatchContext}): similar ids or classes, a
|
|
114
|
+
* shadow host the selector tries to cross,
|
|
114
115
|
* the places selectors do not search (for a page script that found nothing)
|
|
115
116
|
* and the still-loading hint while the page loads.
|
|
116
117
|
*
|
|
@@ -124,7 +125,7 @@ async function withNotFoundContext(failure, selector, searched) {
|
|
|
124
125
|
return failure;
|
|
125
126
|
const context = await noMatchContext(selector);
|
|
126
127
|
const note = searched ? unreachableElementsNote(selector, context.unsearched) : '';
|
|
127
|
-
const suggestion = withLoadingHint(joinLines(context.similar, failure.errorContext?.suggestion, note ? note : undefined), context.readyState, selector);
|
|
128
|
+
const suggestion = withLoadingHint(joinLines(context.similar, context.shadowHost && shadowBoundaryLine(context.shadowHost), failure.errorContext?.suggestion, note ? note : undefined), context.readyState, selector);
|
|
128
129
|
return suggestion ? { ...failure, errorContext: { suggestion } } : failure;
|
|
129
130
|
}
|
|
130
131
|
//# sourceMappingURL=runElementCommand.js.map
|
|
@@ -138,11 +138,32 @@ async function restoreScrollPosition(position) {
|
|
|
138
138
|
returnByValue: true,
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* The window's size with its scrollbars (`innerWidth`/`innerHeight`): an
|
|
143
|
+
* override at this size keeps the page's layout, where the visible size
|
|
144
|
+
* (without scrollbars) would narrow it and move centered content.
|
|
145
|
+
*
|
|
146
|
+
* @param viewport - Visible viewport size, used when the page does not answer
|
|
147
|
+
* @returns Width and height in CSS px
|
|
148
|
+
*/
|
|
149
|
+
async function windowSize(viewport) {
|
|
150
|
+
const response = await callCDP('Runtime.evaluate', {
|
|
151
|
+
expression: '[window.innerWidth, window.innerHeight]',
|
|
152
|
+
returnByValue: true,
|
|
153
|
+
});
|
|
154
|
+
const value = response.data?.result?.result?.value;
|
|
155
|
+
const [width, height] = Array.isArray(value) ? value : [];
|
|
156
|
+
return {
|
|
157
|
+
width: Math.round(width ?? viewport.clientWidth),
|
|
158
|
+
height: Math.round(height ?? viewport.clientHeight),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
141
161
|
/**
|
|
142
162
|
* Capture at a pixel ratio of 1 (CSS px = image px) on a high-DPI display:
|
|
143
|
-
* the viewport is overridden at
|
|
144
|
-
*
|
|
145
|
-
*
|
|
163
|
+
* the viewport is overridden at the window's size (the session's
|
|
164
|
+
* `--viewport`, else the window with its scrollbars, so the layout does not
|
|
165
|
+
* change) until the returned function puts back what was there before, the
|
|
166
|
+
* session's viewport or none.
|
|
146
167
|
*
|
|
147
168
|
* @param devicePixelRatio - Page's pixel ratio
|
|
148
169
|
* @param viewport - Visible viewport size
|
|
@@ -152,10 +173,7 @@ async function useUnitPixelRatio(devicePixelRatio, viewport) {
|
|
|
152
173
|
if (devicePixelRatio === 1)
|
|
153
174
|
return () => Promise.resolve();
|
|
154
175
|
const sessionViewport = readSessionMetadata()?.viewport;
|
|
155
|
-
const size = sessionViewport ??
|
|
156
|
-
width: Math.round(viewport.clientWidth),
|
|
157
|
-
height: Math.round(viewport.clientHeight),
|
|
158
|
-
};
|
|
176
|
+
const size = sessionViewport ?? (await windowSize(viewport));
|
|
159
177
|
await callCDP('Emulation.setDeviceMetricsOverride', viewportOverride(size, 1));
|
|
160
178
|
return async () => {
|
|
161
179
|
if (sessionViewport) {
|
|
@@ -350,6 +368,53 @@ const CONTENT_OVERFLOW_JS = `function () {
|
|
|
350
368
|
if (!clips(view.getComputedStyle(this))) walk(this);
|
|
351
369
|
return { left: own.left - reach.left, top: own.top - reach.top, right: reach.right - own.right, bottom: reach.bottom - own.bottom };
|
|
352
370
|
}`;
|
|
371
|
+
/**
|
|
372
|
+
* The visible viewport (without scrollbars) in CSS px.
|
|
373
|
+
*
|
|
374
|
+
* @returns Width and height
|
|
375
|
+
*/
|
|
376
|
+
async function visibleViewport() {
|
|
377
|
+
const metrics = (await callCDP('Page.getLayoutMetrics', {})).data?.result;
|
|
378
|
+
const view = metrics?.cssVisualViewport ?? metrics?.visualViewport;
|
|
379
|
+
return { width: view?.clientWidth ?? 0, height: view?.clientHeight ?? 0 };
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Whether an area (viewport coordinates) lies inside the viewport.
|
|
383
|
+
*
|
|
384
|
+
* @param area - Area
|
|
385
|
+
* @param view - Viewport size
|
|
386
|
+
* @returns True when fully inside
|
|
387
|
+
*/
|
|
388
|
+
function insideView(area, view) {
|
|
389
|
+
return (area.x >= 0 &&
|
|
390
|
+
area.y >= 0 &&
|
|
391
|
+
area.x + area.width <= view.width &&
|
|
392
|
+
area.y + area.height <= view.height);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Measure the area to capture and, when it fits in the viewport but is not
|
|
396
|
+
* in view, scroll it to the middle first. A capture inside the viewport
|
|
397
|
+
* keeps the page as it is; one beyond it makes Chrome lay the page out
|
|
398
|
+
* without its scrollbar, which moves centered content by half the
|
|
399
|
+
* scrollbar's width, so it is used only for areas larger than the viewport.
|
|
400
|
+
*
|
|
401
|
+
* @param ref - Node reference
|
|
402
|
+
* @returns Border box, area to capture (viewport coordinates) and whether it is in view
|
|
403
|
+
*/
|
|
404
|
+
async function measureInView(ref) {
|
|
405
|
+
const view = await visibleViewport();
|
|
406
|
+
let box = await getElementBounds(ref);
|
|
407
|
+
let bounds = await captureArea(ref, box);
|
|
408
|
+
const fits = bounds.width <= view.width && bounds.height <= view.height;
|
|
409
|
+
if (fits && !insideView(bounds, view)) {
|
|
410
|
+
const dx = bounds.x + bounds.width / 2 - view.width / 2;
|
|
411
|
+
const dy = bounds.y + bounds.height / 2 - view.height / 2;
|
|
412
|
+
await callCDP('Runtime.evaluate', { expression: `window.scrollBy(${dx}, ${dy})` });
|
|
413
|
+
box = await getElementBounds(ref);
|
|
414
|
+
bounds = await captureArea(ref, box);
|
|
415
|
+
}
|
|
416
|
+
return { box, bounds, inView: insideView(bounds, view) };
|
|
417
|
+
}
|
|
353
418
|
/** Overflow (px) below which the capture keeps to the border box (subpixel rounding) */
|
|
354
419
|
const OVERFLOW_SLACK = 1;
|
|
355
420
|
/**
|
|
@@ -403,8 +468,6 @@ async function captureArea(ref, bounds) {
|
|
|
403
468
|
* (the reported bounds are page coordinates, like `dom layout`'s).
|
|
404
469
|
*/
|
|
405
470
|
export async function captureElementScreenshot(outputPath, ref, options = {}) {
|
|
406
|
-
const box = await getElementBounds(ref);
|
|
407
|
-
const bounds = await captureArea(ref, box);
|
|
408
471
|
const format = options.format ?? 'png';
|
|
409
472
|
const quality = format === 'jpeg' ? (options.quality ?? 90) : undefined;
|
|
410
473
|
const noResize = options.noResize ?? false;
|
|
@@ -413,6 +476,18 @@ export async function captureElementScreenshot(outputPath, ref, options = {}) {
|
|
|
413
476
|
returnByValue: true,
|
|
414
477
|
});
|
|
415
478
|
const devicePixelRatio = dprResponse.data?.result?.result?.value ?? 1;
|
|
479
|
+
const before = (await callCDP('Page.getLayoutMetrics', {})).data?.result;
|
|
480
|
+
const restoreMetrics = await useUnitPixelRatio(devicePixelRatio, before?.visualViewport ?? { clientWidth: 800, clientHeight: 600 });
|
|
481
|
+
let box;
|
|
482
|
+
let bounds;
|
|
483
|
+
let inView;
|
|
484
|
+
try {
|
|
485
|
+
({ box, bounds, inView } = await measureInView(ref));
|
|
486
|
+
}
|
|
487
|
+
catch (error) {
|
|
488
|
+
await restoreMetrics();
|
|
489
|
+
throw error;
|
|
490
|
+
}
|
|
416
491
|
const originalWidth = bounds.width;
|
|
417
492
|
const originalHeight = bounds.height;
|
|
418
493
|
const resized = shouldResize(originalWidth, originalHeight, noResize);
|
|
@@ -421,7 +496,6 @@ export async function captureElementScreenshot(outputPath, ref, options = {}) {
|
|
|
421
496
|
const finalHeight = Math.round(originalHeight * scale);
|
|
422
497
|
const metricsResponse = await callCDP('Page.getLayoutMetrics', {});
|
|
423
498
|
const metricsResult = metricsResponse.data?.result;
|
|
424
|
-
const viewport = metricsResult?.visualViewport ?? { clientWidth: 800, clientHeight: 600 };
|
|
425
499
|
const scroll = metricsResult?.cssLayoutViewport ?? { pageX: 0, pageY: 0 };
|
|
426
500
|
const onPage = (area) => ({
|
|
427
501
|
...area,
|
|
@@ -429,14 +503,13 @@ export async function captureElementScreenshot(outputPath, ref, options = {}) {
|
|
|
429
503
|
y: area.y + scroll.pageY,
|
|
430
504
|
});
|
|
431
505
|
const clip = onPage(bounds);
|
|
432
|
-
const restoreMetrics = await useUnitPixelRatio(devicePixelRatio, viewport);
|
|
433
506
|
let screenshotResult;
|
|
434
507
|
try {
|
|
435
508
|
const screenshotResponse = await callCDP('Page.captureScreenshot', {
|
|
436
509
|
format,
|
|
437
510
|
...(quality !== undefined && { quality }),
|
|
438
511
|
clip: { ...clip, scale },
|
|
439
|
-
captureBeyondViewport:
|
|
512
|
+
captureBeyondViewport: !inView,
|
|
440
513
|
});
|
|
441
514
|
screenshotResult = screenshotResponse.data?.result;
|
|
442
515
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - `frames.ts` — list the page's iframes
|
|
10
10
|
* - `listeners.ts` — list event listeners that run for an element
|
|
11
11
|
* - `layout.ts` — positions, sizes and visibility of elements
|
|
12
|
+
* - `inspect.ts` — what one element looks like (styles, box, layout, child tree)
|
|
12
13
|
* - `wait.ts` — wait for elements to appear, show, contain a text or go away
|
|
13
14
|
*
|
|
14
15
|
* Form-related commands register via `form.ts` and `formInteraction.ts`.
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - `frames.ts` — list the page's iframes
|
|
10
10
|
* - `listeners.ts` — list event listeners that run for an element
|
|
11
11
|
* - `layout.ts` — positions, sizes and visibility of elements
|
|
12
|
+
* - `inspect.ts` — what one element looks like (styles, box, layout, child tree)
|
|
12
13
|
* - `wait.ts` — wait for elements to appear, show, contain a text or go away
|
|
13
14
|
*
|
|
14
15
|
* Form-related commands register via `form.ts` and `formInteraction.ts`.
|
|
@@ -20,11 +21,13 @@ import { handleDomEval } from './eval.js';
|
|
|
20
21
|
import { registerFormCommand } from './form.js';
|
|
21
22
|
import { handleDomFrames } from './frames.js';
|
|
22
23
|
import { DOM_GET_DEFAULT_SELECTOR, handleDomGet } from './get.js';
|
|
24
|
+
import { registerInspectCommand } from './inspect.js';
|
|
23
25
|
import { registerLayoutCommand } from './layout.js';
|
|
24
26
|
import { registerListenersCommand } from './listeners.js';
|
|
25
27
|
import { handleDomQuery } from './query.js';
|
|
26
28
|
import { handleDomScreenshot } from './screenshot.js';
|
|
27
29
|
import { registerWaitCommand } from './wait.js';
|
|
30
|
+
import { SELECTOR_OR_INDEX_ARGUMENT, SELECTOR_SCOPE_HELP, } from '../shared/commonOptions.js';
|
|
28
31
|
import { integerOption, screenshotFormatOption } from '../shared/validation.js';
|
|
29
32
|
/**
|
|
30
33
|
* Register DOM telemetry commands on the root Commander program.
|
|
@@ -38,12 +41,14 @@ export function registerDomCommands(program) {
|
|
|
38
41
|
registerFormCommand(dom);
|
|
39
42
|
registerListenersCommand(dom);
|
|
40
43
|
registerLayoutCommand(dom);
|
|
44
|
+
registerInspectCommand(dom);
|
|
41
45
|
registerWaitCommand(dom);
|
|
42
46
|
dom
|
|
43
47
|
.command('query')
|
|
44
48
|
.description('Find elements by CSS selector')
|
|
45
49
|
.argument('<selector>', 'CSS selector (e.g., ".error", "#app", "button")')
|
|
46
50
|
.option('-j, --json', 'Output as JSON')
|
|
51
|
+
.addHelpText('after', SELECTOR_SCOPE_HELP)
|
|
47
52
|
.action(async (selector, options) => {
|
|
48
53
|
await handleDomQuery(selector, options);
|
|
49
54
|
});
|
|
@@ -51,7 +56,7 @@ export function registerDomCommands(program) {
|
|
|
51
56
|
.command('eval')
|
|
52
57
|
.description('Evaluate JavaScript expression in the page context')
|
|
53
58
|
.argument('<script>', 'JavaScript to execute (e.g., "document.title", "window.location.href")')
|
|
54
|
-
.option('--frame <frame>', 'Evaluate in an iframe, cross-origin ones included: index, name/id attribute, or part of the name, id or URL
|
|
59
|
+
.option('--frame <frame>', 'Evaluate in an iframe, cross-origin ones included: index (from dom frames; 87 when stale), name/id attribute, or part of the name, id or URL')
|
|
55
60
|
.option('-j, --json', 'Output as JSON')
|
|
56
61
|
.action(async (script, options) => {
|
|
57
62
|
await handleDomEval(script, options);
|
|
@@ -67,7 +72,7 @@ export function registerDomCommands(program) {
|
|
|
67
72
|
});
|
|
68
73
|
dom
|
|
69
74
|
.command('frames')
|
|
70
|
-
.description("List the page's iframes (nested and cross-origin ones included) for eval --frame")
|
|
75
|
+
.description("List the page's iframes in document order (nested and cross-origin ones included) for eval --frame")
|
|
71
76
|
.option('-j, --json', 'Output as JSON')
|
|
72
77
|
.action(async (options) => {
|
|
73
78
|
await handleDomFrames(options);
|
|
@@ -75,7 +80,7 @@ export function registerDomCommands(program) {
|
|
|
75
80
|
dom
|
|
76
81
|
.command('get')
|
|
77
82
|
.description('Get semantic accessibility structure (default) or raw HTML (--raw)')
|
|
78
|
-
.argument('[selectorOrIndex]',
|
|
83
|
+
.argument('[selectorOrIndex]', `${SELECTOR_OR_INDEX_ARGUMENT} (e.g. ".error", "#app", 0); default: ${DOM_GET_DEFAULT_SELECTOR}`)
|
|
79
84
|
.option('--raw', 'Output raw HTML with all filtering options')
|
|
80
85
|
.option('--full', 'Show all of the element text (default: the first 500 characters)')
|
|
81
86
|
.option('--all', 'Get all matches (only with --raw)')
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bdg dom inspect <selector|index>` - what one element looks like, without a
|
|
3
|
+
* screenshot: its box, layout (and its place in the parent), typography with
|
|
4
|
+
* the rendered font and contrast, fills, borders, effects, CSS state,
|
|
5
|
+
* pseudo-elements and a compact child tree, as grouped lines (`--json`:
|
|
6
|
+
* Figma-aligned fields).
|
|
7
|
+
*/
|
|
8
|
+
import { type Command } from 'commander';
|
|
9
|
+
/**
|
|
10
|
+
* Register `bdg dom inspect`.
|
|
11
|
+
*
|
|
12
|
+
* @param dom - The `dom` command group
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerInspectCommand(dom: Command): void;
|
|
15
|
+
//# sourceMappingURL=inspect.d.ts.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bdg dom inspect <selector|index>` - what one element looks like, without a
|
|
3
|
+
* screenshot: its box, layout (and its place in the parent), typography with
|
|
4
|
+
* the rendered font and contrast, fills, borders, effects, CSS state,
|
|
5
|
+
* pseudo-elements and a compact child tree, as grouped lines (`--json`:
|
|
6
|
+
* Figma-aligned fields).
|
|
7
|
+
*/
|
|
8
|
+
import { Option } from 'commander';
|
|
9
|
+
import { runElementCommand } from './helpers/runElementCommand.js';
|
|
10
|
+
import { runCommand } from '../shared/CommandRunner.js';
|
|
11
|
+
import { jsonOption, SELECTOR_OR_INDEX_ARGUMENT } from '../shared/commonOptions.js';
|
|
12
|
+
import { cssPropertiesOption, integerOption } from '../shared/validation.js';
|
|
13
|
+
import { domInspect } from '../../ipc/client.js';
|
|
14
|
+
import { DEFAULT_TREE_DEPTH, DEFAULT_TREE_LIMIT } from '../../runtime/dom/inspectTree.js';
|
|
15
|
+
import { formatInspect } from '../../ui/formatters/inspect.js';
|
|
16
|
+
import { INSPECT_OUTPUT_LEGEND } from '../../ui/messages/commands.js';
|
|
17
|
+
import { filterDefined } from '../../utils/objects.js';
|
|
18
|
+
/**
|
|
19
|
+
* Register `bdg dom inspect`.
|
|
20
|
+
*
|
|
21
|
+
* @param dom - The `dom` command group
|
|
22
|
+
*/
|
|
23
|
+
export function registerInspectCommand(dom) {
|
|
24
|
+
dom
|
|
25
|
+
.command('inspect')
|
|
26
|
+
.description('What one element looks like without a screenshot: box, layout, font (rendered, contrast), colors, borders, effects, pseudo-elements and child tree')
|
|
27
|
+
.argument('<selectorOrIndex>', SELECTOR_OR_INDEX_ARGUMENT)
|
|
28
|
+
.option('--index <n>', 'Which match to inspect (0-based; default: the first rendered one)', integerOption(0))
|
|
29
|
+
.option('--tree <depth>', `Child tree depth (default ${DEFAULT_TREE_DEPTH}; 0 for none)`, integerOption(0, 10))
|
|
30
|
+
.option('--tree-limit <n>', `Child tree rows at most (default ${DEFAULT_TREE_LIMIT})`, integerOption(1, 500))
|
|
31
|
+
.addOption(new Option('--all', 'Every computed property that is not its default, collapsed into shorthands, instead of the groups').conflicts('props'))
|
|
32
|
+
.addHelpText('after', INSPECT_OUTPUT_LEGEND)
|
|
33
|
+
.option('--props <names>', "Only these properties, computed and normalized (comma-separated, e.g. padding,color,--brand; '--*' or '--bs-btn-*' lists custom properties)", cssPropertiesOption)
|
|
34
|
+
.addOption(new Option('--rules', 'Also show which CSS rule sets each shown property (selector, file:line, what it overrides); with --props, those properties').conflicts('all'))
|
|
35
|
+
.addOption(new Option('--why <property>', 'Every declaration of one property: the one that applies and those it overrides (e.g. --why color)')
|
|
36
|
+
.conflicts('all')
|
|
37
|
+
.argParser(propertyOption))
|
|
38
|
+
.option('--no-hints', 'Skip the check for declarations that have no effect')
|
|
39
|
+
.addOption(jsonOption())
|
|
40
|
+
.action(async (selectorOrIndex, options) => {
|
|
41
|
+
await runCommand(() => inspectTarget(selectorOrIndex, options), options, formatInspect);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse `--why`: a property name, lowercased (custom properties as given).
|
|
46
|
+
*
|
|
47
|
+
* @param value - Property name
|
|
48
|
+
* @returns Name
|
|
49
|
+
*/
|
|
50
|
+
function propertyOption(value) {
|
|
51
|
+
return value.startsWith('--') ? value.trim() : value.trim().toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the target and ask the daemon to inspect it.
|
|
55
|
+
*
|
|
56
|
+
* @param selectorOrIndex - CSS selector or cached query index
|
|
57
|
+
* @param options - Command options
|
|
58
|
+
* @returns Command result
|
|
59
|
+
*/
|
|
60
|
+
async function inspectTarget(selectorOrIndex, options) {
|
|
61
|
+
return runElementCommand({
|
|
62
|
+
selectorOrIndex,
|
|
63
|
+
index: options.index,
|
|
64
|
+
buildRequest: (target) => ({
|
|
65
|
+
...target,
|
|
66
|
+
...filterDefined({
|
|
67
|
+
tree: options.tree,
|
|
68
|
+
treeLimit: options.treeLimit,
|
|
69
|
+
all: options.all,
|
|
70
|
+
props: options.props,
|
|
71
|
+
rules: options.rules,
|
|
72
|
+
why: options.why,
|
|
73
|
+
...(options.hints === false && { hints: false }),
|
|
74
|
+
}),
|
|
75
|
+
}),
|
|
76
|
+
call: domInspect,
|
|
77
|
+
command: 'inspect',
|
|
78
|
+
action: 'inspect the element',
|
|
79
|
+
failureSuggestion: 'Verify the selector matches an element: bdg dom query "<selector>"',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=inspect.js.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { DomElementResolver } from './DomElementResolver.js';
|
|
8
8
|
import { runElementCommand } from './helpers/runElementCommand.js';
|
|
9
9
|
import { runCommand } from '../shared/CommandRunner.js';
|
|
10
|
-
import { jsonOption } from '../shared/commonOptions.js';
|
|
10
|
+
import { jsonOption, SELECTOR_OR_INDEX_ARGUMENT } from '../shared/commonOptions.js';
|
|
11
11
|
import { integerOption } from '../shared/validation.js';
|
|
12
12
|
import { domLayout } from '../../ipc/client.js';
|
|
13
13
|
import { formatLayout } from '../../ui/formatters/layout.js';
|
|
@@ -20,7 +20,7 @@ export function registerLayoutCommand(dom) {
|
|
|
20
20
|
dom
|
|
21
21
|
.command('layout')
|
|
22
22
|
.description('Positions, sizes and visibility of elements (above/below the fold, hidden, covered) without a screenshot')
|
|
23
|
-
.argument('<selectorOrIndex>',
|
|
23
|
+
.argument('<selectorOrIndex>', SELECTOR_OR_INDEX_ARGUMENT)
|
|
24
24
|
.option('--index <n>', 'Only this match of the selector (0-based)', integerOption(0))
|
|
25
25
|
.addOption(jsonOption())
|
|
26
26
|
.action(async (selectorOrIndex, options) => {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { DomElementResolver } from './DomElementResolver.js';
|
|
14
14
|
import { runElementCommand } from './helpers/runElementCommand.js';
|
|
15
15
|
import { runCommand } from '../shared/CommandRunner.js';
|
|
16
|
-
import { jsonOption } from '../shared/commonOptions.js';
|
|
16
|
+
import { jsonOption, SELECTOR_OR_INDEX_ARGUMENT } from '../shared/commonOptions.js';
|
|
17
17
|
import { eventTypesOption, integerOption } from '../shared/validation.js';
|
|
18
18
|
import { domListeners } from '../../ipc/client.js';
|
|
19
19
|
import { formatListeners } from '../../ui/formatters/listeners.js';
|
|
@@ -26,7 +26,7 @@ export function registerListenersCommand(dom) {
|
|
|
26
26
|
dom
|
|
27
27
|
.command('listeners')
|
|
28
28
|
.description('List event listeners that run for an element (incl. delegated ones on ancestors)')
|
|
29
|
-
.argument('<selectorOrIndex>',
|
|
29
|
+
.argument('<selectorOrIndex>', SELECTOR_OR_INDEX_ARGUMENT)
|
|
30
30
|
.option('--index <n>', 'Element index if selector matches multiple (0-based)', integerOption(0))
|
|
31
31
|
.option('--type <types>', 'Only these event types (comma-separated, e.g. click,keydown; repeatable)', eventTypesOption)
|
|
32
32
|
.option('--all', 'List every listener of framework roots (React) instead of one line per node')
|
|
@@ -17,7 +17,9 @@ export interface SemanticNodeWithContext {
|
|
|
17
17
|
/**
|
|
18
18
|
* Format a semantic node together with DOM context for human-readable output.
|
|
19
19
|
*
|
|
20
|
-
* The role line
|
|
20
|
+
* The role line names the element's key attributes (an image's file name, a
|
|
21
|
+
* link's href, a field's type and name), like `dom query` does, and is
|
|
22
|
+
* followed by up to 500 characters of the element's text
|
|
21
23
|
* (all of it with `dom get --full`) when it is longer than the one-line
|
|
22
24
|
* preview, or, for an element without text or name, what it holds.
|
|
23
25
|
*
|
|
@@ -34,4 +36,15 @@ export declare function formatSemanticNodeWithContext(data: SemanticNodeWithCont
|
|
|
34
36
|
* @param nodeId - CDP nodeId for synthesis
|
|
35
37
|
*/
|
|
36
38
|
export declare function resolveNodeWithFallback(a11yNode: A11yNode | null, domContext: DomContext | null, nodeId: number | undefined): A11yNode | null;
|
|
39
|
+
/**
|
|
40
|
+
* The accessibility node with its value masked when the element holds a
|
|
41
|
+
* secret (`domContext.sensitive`): Chrome reports a password field's value
|
|
42
|
+
* as one bullet per character, and a field switched to text by a "show
|
|
43
|
+
* password" button in clear.
|
|
44
|
+
*
|
|
45
|
+
* @param node - Accessibility node
|
|
46
|
+
* @param domContext - DOM context of the same element
|
|
47
|
+
* @returns The node, with {@link MASKED_VALUE} as its value for a secret
|
|
48
|
+
*/
|
|
49
|
+
export declare function withSecretMasked(node: A11yNode, domContext: DomContext | null): A11yNode;
|
|
37
50
|
//# sourceMappingURL=semanticUtils.d.ts.map
|