Skip to content

Commit 869a67c

Browse files
author
Natallia Harshunova
committed
Create console Collector and extract issues subscription logic there
1 parent fd12946 commit 869a67c

File tree

4 files changed

+73
-63
lines changed

4 files changed

+73
-63
lines changed

scripts/post-build.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,16 @@ export const hostConfig = {};
173173
}
174174

175175
function copyDevToolsDescriptionFiles() {
176+
const devtoolsIssuesDescriptionPath = 'node_modules/chrome-devtools-frontend/front_end/models/issues_manager/descriptions';
176177
const sourceDir = path.join(
177178
process.cwd(),
178-
'node_modules/chrome-devtools-frontend/front_end/models/issues_manager/descriptions',
179+
devtoolsIssuesDescriptionPath,
179180
);
180181
const destDir = path.join(
181182
BUILD_DIR,
182-
'node_modules/chrome-devtools-frontend/front_end/models/issues_manager/descriptions',
183+
devtoolsIssuesDescriptionPath,
183184
);
184185
fs.cpSync(sourceDir, destDir, {recursive: true});
185186
}
186187

187-
main();
188+
main();

src/DevtoolsUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import {
88
type Issue,
9-
type IssuesManager,
9+
type IssuesManagerEventTypes,
1010
Common
1111
} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js';
1212

@@ -61,7 +61,7 @@ function normalizeUrl(url: string): string {
6161
* A mock implementation of an issues manager that only implements the methods
6262
* that are actually used by the IssuesAggregator
6363
*/
64-
export class FakeIssuesManager extends Common.ObjectWrapper.ObjectWrapper<IssuesManager.EventTypes> {
64+
export class FakeIssuesManager extends Common.ObjectWrapper.ObjectWrapper<IssuesManagerEventTypes> {
6565
issues(): Issue[] {
6666
return [];
6767
}

src/McpContext.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {type AggregatedIssue} from '../node_modules/chrome-devtools-frontend/mcp
1111

1212
import {extractUrlLikeFromDevToolsTitle, urlsEqual} from './DevtoolsUtils.js';
1313
import type {ListenerMap} from './PageCollector.js';
14-
import {NetworkCollector, PageCollector} from './PageCollector.js';
14+
import {NetworkCollector, ConsoleCollector} from './PageCollector.js';
1515
import {Locator} from './third_party/index.js';
1616
import type {
1717
Browser,
@@ -94,7 +94,7 @@ export class McpContext implements Context {
9494
// The most recent snapshot.
9595
#textSnapshot: TextSnapshot | null = null;
9696
#networkCollector: NetworkCollector;
97-
#consoleCollector: PageCollector<ConsoleMessage | Error | AggregatedIssue>;
97+
#consoleCollector: ConsoleCollector;
9898

9999
#isRunningTrace = false;
100100
#networkConditionsMap = new WeakMap<Page, string>();
@@ -120,7 +120,7 @@ export class McpContext implements Context {
120120

121121
this.#networkCollector = new NetworkCollector(this.browser);
122122

123-
this.#consoleCollector = new PageCollector(this.browser, collect => {
123+
this.#consoleCollector = new ConsoleCollector(this.browser, collect => {
124124
return {
125125
console: event => {
126126
collect(event);

src/PageCollector.ts

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
import {
88
type AggregatedIssue,
9-
AggregatorEvents,
10-
IssuesManager,
9+
IssueAggregatorEvents,
10+
IssuesManagerEvents,
11+
createIssuesFromProtocolIssue,
1112
IssueAggregator,
1213
} from '../node_modules/chrome-devtools-frontend/mcp/mcp.js';
1314

1415
import {FakeIssuesManager} from './DevtoolsUtils.js';
16+
import type {ConsoleMessage} from './third_party/index.js';
1517
import {
1618
type Browser,
1719
type Frame,
@@ -50,12 +52,8 @@ export class PageCollector<T> {
5052
collector: (item: T) => void,
5153
) => ListenerMap<PageEvents>;
5254
#listeners = new WeakMap<Page, ListenerMap>();
53-
#seenIssueKeys = new WeakMap<Page, Set<string>>();
5455
#maxNavigationSaved = 3;
5556

56-
// Store an aggregator and a mock manager for each page.
57-
#issuesAggregators = new WeakMap<Page, IssueAggregator>();
58-
#mockIssuesManagers = new WeakMap<Page, FakeIssuesManager>();
5957

6058
protected storage = new WeakMap<Page, Array<Array<WithSymbolId<T>>>>();
6159

@@ -85,7 +83,7 @@ export class PageCollector<T> {
8583
if (!page) {
8684
return;
8785
}
88-
this.#cleanupPageDestroyed(page);
86+
this.cleanupPageDestroyed(page);
8987
});
9088
}
9189

@@ -119,7 +117,7 @@ export class PageCollector<T> {
119117
}
120118
};
121119

122-
await this.subscribeForIssues(page);
120+
// await this.subscribeForIssues(page);
123121

124122
const listeners = this.#listenersInitializer(collector);
125123

@@ -138,49 +136,6 @@ export class PageCollector<T> {
138136
this.#listeners.set(page, listeners);
139137
}
140138

141-
protected async subscribeForIssues(page: Page) {
142-
if (this instanceof NetworkCollector) {
143-
return;
144-
}
145-
if (!this.#seenIssueKeys.has(page)) {
146-
this.#seenIssueKeys.set(page, new Set());
147-
}
148-
149-
const mockManager = new FakeIssuesManager();
150-
// @ts-expect-error Aggregator receives partial IssuesManager
151-
const aggregator = new IssueAggregator(mockManager);
152-
this.#mockIssuesManagers.set(page, mockManager);
153-
this.#issuesAggregators.set(page, aggregator);
154-
155-
aggregator.addEventListener(
156-
AggregatorEvents.AGGREGATED_ISSUE_UPDATED,
157-
event => {
158-
page.emit('issue', event.data);
159-
},
160-
);
161-
162-
const session = await page.createCDPSession();
163-
session.on('Audits.issueAdded', data => {
164-
// @ts-expect-error Types of protocol from Puppeteer and CDP are incopatible for Issues but it's the same type
165-
const issue = IssuesManager.createIssuesFromProtocolIssue(null,data.issue,)[0];
166-
if (!issue) {
167-
return;
168-
}
169-
const seenKeys = this.#seenIssueKeys.get(page)!;
170-
const primaryKey = issue.primaryKey();
171-
if (seenKeys.has(primaryKey)) return;
172-
seenKeys.add(primaryKey);
173-
174-
// Trigger the aggregator via our mock manager. Do NOT call collector() here.
175-
const mockManager = this.#mockIssuesManagers.get(page);
176-
if (mockManager) {
177-
// @ts-expect-error we don't care about issies model being null
178-
mockManager.dispatchEventToListeners(IssuesManager.Events.ISSUE_ADDED, {issue, issuesModel: null});
179-
}
180-
});
181-
await session.send('Audits.enable');
182-
}
183-
184139
protected splitAfterNavigation(page: Page) {
185140
const navigations = this.storage.get(page);
186141
if (!navigations) {
@@ -190,17 +145,14 @@ export class PageCollector<T> {
190145
navigations.splice(this.#maxNavigationSaved);
191146
}
192147

193-
#cleanupPageDestroyed(page: Page) {
148+
protected cleanupPageDestroyed(page: Page) {
194149
const listeners = this.#listeners.get(page);
195150
if (listeners) {
196151
for (const [name, listener] of Object.entries(listeners)) {
197152
page.off(name, listener as Handler<unknown>);
198153
}
199154
}
200155
this.storage.delete(page);
201-
this.#seenIssueKeys.delete(page);
202-
this.#issuesAggregators.delete(page);
203-
this.#mockIssuesManagers.delete(page);
204156
}
205157

206158
getData(page: Page, includePreservedData?: boolean): T[] {
@@ -260,6 +212,63 @@ export class PageCollector<T> {
260212
}
261213
}
262214

215+
export class ConsoleCollector extends PageCollector<ConsoleMessage | Error | AggregatedIssue> {
216+
#seenIssueKeys = new WeakMap<Page, Set<string>>();
217+
#issuesAggregators = new WeakMap<Page, IssueAggregator>();
218+
#mockIssuesManagers = new WeakMap<Page, FakeIssuesManager>();
219+
220+
override async addPage(page: Page) {
221+
await super.addPage(page);
222+
await this.subscribeForIssues(page);
223+
}
224+
async subscribeForIssues(page: Page) {
225+
if (!this.#seenIssueKeys.has(page)) {
226+
this.#seenIssueKeys.set(page, new Set());
227+
}
228+
229+
const mockManager = new FakeIssuesManager();
230+
// @ts-expect-error Aggregator receives partial IssuesManager
231+
const aggregator = new IssueAggregator(mockManager);
232+
this.#mockIssuesManagers.set(page, mockManager);
233+
this.#issuesAggregators.set(page, aggregator);
234+
235+
aggregator.addEventListener(
236+
IssueAggregatorEvents.AGGREGATED_ISSUE_UPDATED,
237+
event => {
238+
page.emit('issue', event.data);
239+
},
240+
);
241+
242+
const session = await page.createCDPSession();
243+
session.on('Audits.issueAdded', data => {
244+
// @ts-expect-error Types of protocol from Puppeteer and CDP are incopatible for Issues but it's the same type
245+
const issue = createIssuesFromProtocolIssue(null,data.issue,)[0];
246+
if (!issue) {
247+
return;
248+
}
249+
const seenKeys = this.#seenIssueKeys.get(page)!;
250+
const primaryKey = issue.primaryKey();
251+
if (seenKeys.has(primaryKey)) return;
252+
seenKeys.add(primaryKey);
253+
254+
// Trigger the aggregator via our mock manager. Do NOT call collector() here.
255+
const mockManager = this.#mockIssuesManagers.get(page);
256+
if (mockManager) {
257+
// @ts-expect-error We don't care that issues model is null
258+
mockManager.dispatchEventToListeners(IssuesManagerEvents.ISSUE_ADDED, {issue, issuesModel: null});
259+
}
260+
});
261+
await session.send('Audits.enable');
262+
}
263+
264+
override cleanupPageDestroyed(page: Page) {
265+
super.cleanupPageDestroyed(page);
266+
this.#seenIssueKeys.delete(page);
267+
this.#issuesAggregators.delete(page);
268+
this.#mockIssuesManagers.delete(page);
269+
}
270+
}
271+
263272
export class NetworkCollector extends PageCollector<HTTPRequest> {
264273
constructor(
265274
browser: Browser,

0 commit comments

Comments
 (0)