66
77import {
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
1415import { FakeIssuesManager } from './DevtoolsUtils.js' ;
16+ import type { ConsoleMessage } from './third_party/index.js' ;
1517import {
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+
263272export class NetworkCollector extends PageCollector < HTTPRequest > {
264273 constructor (
265274 browser : Browser ,
0 commit comments