mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-09-14 19:45:30 +08:00
fix(snapshot): resolve element ids on the correct snapshot (#2295)
closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2243 This probably regressed with the addition of third party developer tools because the resolveCdpElementId was not moved from page to TextSnapshot.
This commit is contained in:
+2
-25
@@ -296,7 +296,8 @@ export class McpPage implements ContextPage {
|
||||
);
|
||||
return `stashed-${index}`;
|
||||
}
|
||||
const cdpElementId = this.resolveCdpElementId(backendNodeId);
|
||||
const cdpElementId =
|
||||
this.textSnapshot?.resolveCdpElementId(backendNodeId);
|
||||
if (!cdpElementId) {
|
||||
logger?.(
|
||||
`Could not get cdpElementId for backend node ${backendNodeId}`,
|
||||
@@ -369,30 +370,6 @@ export class McpPage implements ContextPage {
|
||||
return this.textSnapshot?.idToNode.get(uid);
|
||||
}
|
||||
|
||||
resolveCdpElementId(cdpBackendNodeId: number): string | undefined {
|
||||
if (!cdpBackendNodeId) {
|
||||
logger?.('no cdpBackendNodeId');
|
||||
return;
|
||||
}
|
||||
const snapshot = this.textSnapshot;
|
||||
if (!snapshot) {
|
||||
logger?.('no text snapshot');
|
||||
return;
|
||||
}
|
||||
// TODO: index by backendNodeId instead.
|
||||
const queue = [snapshot.root];
|
||||
while (queue.length) {
|
||||
const current = queue.pop()!;
|
||||
if (current.backendNodeId === cdpBackendNodeId) {
|
||||
return current.id;
|
||||
}
|
||||
for (const child of current.children) {
|
||||
queue.push(child);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
async getDevToolsData(): Promise<DevToolsData> {
|
||||
try {
|
||||
logger?.('Getting DevTools UI data');
|
||||
|
||||
+3
-1
@@ -652,7 +652,9 @@ export class McpResponse implements Response {
|
||||
context,
|
||||
this.#page,
|
||||
),
|
||||
elementIdResolver: this.#page.resolveCdpElementId.bind(this.#page),
|
||||
elementIdResolver: this.#page.textSnapshot?.resolveCdpElementId.bind(
|
||||
this.#page.textSnapshot,
|
||||
),
|
||||
});
|
||||
if (!formatter.isValid()) {
|
||||
throw new Error(
|
||||
|
||||
+20
-1
@@ -133,7 +133,7 @@ export class TextSnapshot {
|
||||
const data = options.devtoolsData ?? (await page.getDevToolsData());
|
||||
if (data?.cdpBackendNodeId) {
|
||||
snapshot.hasSelectedElement = true;
|
||||
snapshot.selectedElementUid = page.resolveCdpElementId(
|
||||
snapshot.selectedElementUid = snapshot.resolveCdpElementId(
|
||||
data.cdpBackendNodeId,
|
||||
);
|
||||
}
|
||||
@@ -148,6 +148,25 @@ export class TextSnapshot {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
resolveCdpElementId(cdpBackendNodeId: number): string | undefined {
|
||||
if (!cdpBackendNodeId) {
|
||||
logger?.('no cdpBackendNodeId');
|
||||
return;
|
||||
}
|
||||
// TODO: index by backendNodeId instead.
|
||||
const queue = [this.root];
|
||||
while (queue.length) {
|
||||
const current = queue.pop()!;
|
||||
if (current.backendNodeId === cdpBackendNodeId) {
|
||||
return current.id;
|
||||
}
|
||||
for (const child of current.children) {
|
||||
queue.push(child);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ExtraHandles represent DOM nodes which might not be part of the accessibility tree, e.g. DOM nodes
|
||||
// returned by third-party developer tools. We insert them into the tree by finding the closest ancestor
|
||||
// in the tree and inserting the node as a child. The ancestor's child nodes are re-parented if necessary.
|
||||
|
||||
@@ -188,6 +188,9 @@ describe('snapshotFormatter', () => {
|
||||
idToNode: new Map(),
|
||||
hasSelectedElement: true,
|
||||
verbose: false,
|
||||
resolveCdpElementId() {
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
const formatted = formatter.toString();
|
||||
|
||||
@@ -222,6 +225,9 @@ describe('snapshotFormatter', () => {
|
||||
idToNode: new Map(),
|
||||
hasSelectedElement: true,
|
||||
verbose: true,
|
||||
resolveCdpElementId() {
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
const formatted = formatter.toString();
|
||||
|
||||
@@ -257,6 +263,9 @@ describe('snapshotFormatter', () => {
|
||||
hasSelectedElement: true,
|
||||
selectedElementUid: '1_1',
|
||||
verbose: false,
|
||||
resolveCdpElementId() {
|
||||
return '1_1';
|
||||
},
|
||||
});
|
||||
const formatted = formatter.toString();
|
||||
|
||||
|
||||
@@ -767,10 +767,6 @@ describe('thirdPartyDeveloperTools', () => {
|
||||
};
|
||||
});
|
||||
|
||||
const stub = sinon
|
||||
.stub(page, 'resolveCdpElementId')
|
||||
.returns('mock-uid');
|
||||
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
@@ -785,10 +781,8 @@ describe('thirdPartyDeveloperTools', () => {
|
||||
|
||||
assert.strictEqual(
|
||||
response.responseLines[0],
|
||||
JSON.stringify({uid: 'mock-uid'}, null, 2),
|
||||
JSON.stringify({uid: '1_1'}, null, 2),
|
||||
);
|
||||
|
||||
stub.restore();
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalThirdParty: true},
|
||||
@@ -826,14 +820,6 @@ describe('thirdPartyDeveloperTools', () => {
|
||||
};
|
||||
});
|
||||
|
||||
const stubSnapshot = sinon
|
||||
.stub(TextSnapshot, 'create')
|
||||
.resolves({} as TextSnapshot);
|
||||
|
||||
const stubResolve = sinon
|
||||
.stub(page, 'resolveCdpElementId')
|
||||
.returns('mock-uid');
|
||||
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
@@ -846,17 +832,10 @@ describe('thirdPartyDeveloperTools', () => {
|
||||
context,
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
stubSnapshot.calledOnce,
|
||||
'Expected TextSnapshot.create to be called',
|
||||
);
|
||||
assert.strictEqual(
|
||||
response.responseLines[0],
|
||||
JSON.stringify({uid: 'mock-uid'}, null, 2),
|
||||
JSON.stringify({uid: '1_1'}, null, 2),
|
||||
);
|
||||
|
||||
stubResolve.restore();
|
||||
stubSnapshot.restore();
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalThirdParty: true},
|
||||
|
||||
Reference in New Issue
Block a user