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,13 +52,9 @@ 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 #includeAllPages?: boolean ;
5657
57- // Store an aggregator and a mock manager for each page.
58- #issuesAggregators = new WeakMap < Page , IssueAggregator > ( ) ;
59- #mockIssuesManagers = new WeakMap < Page , FakeIssuesManager > ( ) ;
6058
6159 protected storage = new WeakMap < Page , Array < Array < WithSymbolId < T > > > > ( ) ;
6260
@@ -88,7 +86,7 @@ export class PageCollector<T> {
8886 if ( ! page ) {
8987 return ;
9088 }
91- this . # cleanupPageDestroyed( page ) ;
89+ this . cleanupPageDestroyed ( page ) ;
9290 } ) ;
9391 }
9492
@@ -122,7 +120,7 @@ export class PageCollector<T> {
122120 }
123121 } ;
124122
125- await this . subscribeForIssues ( page ) ;
123+ // await this.subscribeForIssues(page);
126124
127125 const listeners = this . #listenersInitializer( collector ) ;
128126
@@ -141,49 +139,6 @@ export class PageCollector<T> {
141139 this . #listeners. set ( page , listeners ) ;
142140 }
143141
144- protected async subscribeForIssues ( page : Page ) {
145- if ( this instanceof NetworkCollector ) {
146- return ;
147- }
148- if ( ! this . #seenIssueKeys. has ( page ) ) {
149- this . #seenIssueKeys. set ( page , new Set ( ) ) ;
150- }
151-
152- const mockManager = new FakeIssuesManager ( ) ;
153- // @ts -expect-error Aggregator receives partial IssuesManager
154- const aggregator = new IssueAggregator ( mockManager ) ;
155- this . #mockIssuesManagers. set ( page , mockManager ) ;
156- this . #issuesAggregators. set ( page , aggregator ) ;
157-
158- aggregator . addEventListener (
159- AggregatorEvents . AGGREGATED_ISSUE_UPDATED ,
160- event => {
161- page . emit ( 'issue' , event . data ) ;
162- } ,
163- ) ;
164-
165- const session = await page . createCDPSession ( ) ;
166- session . on ( 'Audits.issueAdded' , data => {
167- // @ts -expect-error Types of protocol from Puppeteer and CDP are incopatible for Issues but it's the same type
168- const issue = IssuesManager . createIssuesFromProtocolIssue ( null , data . issue , ) [ 0 ] ;
169- if ( ! issue ) {
170- return ;
171- }
172- const seenKeys = this . #seenIssueKeys. get ( page ) ! ;
173- const primaryKey = issue . primaryKey ( ) ;
174- if ( seenKeys . has ( primaryKey ) ) return ;
175- seenKeys . add ( primaryKey ) ;
176-
177- // Trigger the aggregator via our mock manager. Do NOT call collector() here.
178- const mockManager = this . #mockIssuesManagers. get ( page ) ;
179- if ( mockManager ) {
180- // @ts -expect-error we don't care about issies model being null
181- mockManager . dispatchEventToListeners ( IssuesManager . Events . ISSUE_ADDED , { issue, issuesModel : null } ) ;
182- }
183- } ) ;
184- await session . send ( 'Audits.enable' ) ;
185- }
186-
187142 protected splitAfterNavigation ( page : Page ) {
188143 const navigations = this . storage . get ( page ) ;
189144 if ( ! navigations ) {
@@ -193,17 +148,14 @@ export class PageCollector<T> {
193148 navigations . splice ( this . #maxNavigationSaved) ;
194149 }
195150
196- # cleanupPageDestroyed( page : Page ) {
151+ protected cleanupPageDestroyed ( page : Page ) {
197152 const listeners = this . #listeners. get ( page ) ;
198153 if ( listeners ) {
199154 for ( const [ name , listener ] of Object . entries ( listeners ) ) {
200155 page . off ( name , listener as Handler < unknown > ) ;
201156 }
202157 }
203158 this . storage . delete ( page ) ;
204- this . #seenIssueKeys. delete ( page ) ;
205- this . #issuesAggregators. delete ( page ) ;
206- this . #mockIssuesManagers. delete ( page ) ;
207159 }
208160
209161 getData ( page : Page , includePreservedData ?: boolean ) : T [ ] {
@@ -263,6 +215,63 @@ export class PageCollector<T> {
263215 }
264216}
265217
218+ export class ConsoleCollector extends PageCollector < ConsoleMessage | Error | AggregatedIssue > {
219+ #seenIssueKeys = new WeakMap < Page , Set < string > > ( ) ;
220+ #issuesAggregators = new WeakMap < Page , IssueAggregator > ( ) ;
221+ #mockIssuesManagers = new WeakMap < Page , FakeIssuesManager > ( ) ;
222+
223+ override async addPage ( page : Page ) {
224+ await super . addPage ( page ) ;
225+ await this . subscribeForIssues ( page ) ;
226+ }
227+ async subscribeForIssues ( page : Page ) {
228+ if ( ! this . #seenIssueKeys. has ( page ) ) {
229+ this . #seenIssueKeys. set ( page , new Set ( ) ) ;
230+ }
231+
232+ const mockManager = new FakeIssuesManager ( ) ;
233+ // @ts -expect-error Aggregator receives partial IssuesManager
234+ const aggregator = new IssueAggregator ( mockManager ) ;
235+ this . #mockIssuesManagers. set ( page , mockManager ) ;
236+ this . #issuesAggregators. set ( page , aggregator ) ;
237+
238+ aggregator . addEventListener (
239+ IssueAggregatorEvents . AGGREGATED_ISSUE_UPDATED ,
240+ event => {
241+ page . emit ( 'issue' , event . data ) ;
242+ } ,
243+ ) ;
244+
245+ const session = await page . createCDPSession ( ) ;
246+ session . on ( 'Audits.issueAdded' , data => {
247+ // @ts -expect-error Types of protocol from Puppeteer and CDP are incopatible for Issues but it's the same type
248+ const issue = createIssuesFromProtocolIssue ( null , data . issue , ) [ 0 ] ;
249+ if ( ! issue ) {
250+ return ;
251+ }
252+ const seenKeys = this . #seenIssueKeys. get ( page ) ! ;
253+ const primaryKey = issue . primaryKey ( ) ;
254+ if ( seenKeys . has ( primaryKey ) ) return ;
255+ seenKeys . add ( primaryKey ) ;
256+
257+ // Trigger the aggregator via our mock manager. Do NOT call collector() here.
258+ const mockManager = this . #mockIssuesManagers. get ( page ) ;
259+ if ( mockManager ) {
260+ // @ts -expect-error We don't care that issues model is null
261+ mockManager . dispatchEventToListeners ( IssuesManagerEvents . ISSUE_ADDED , { issue, issuesModel : null } ) ;
262+ }
263+ } ) ;
264+ await session . send ( 'Audits.enable' ) ;
265+ }
266+
267+ override cleanupPageDestroyed ( page : Page ) {
268+ super . cleanupPageDestroyed ( page ) ;
269+ this . #seenIssueKeys. delete ( page ) ;
270+ this . #issuesAggregators. delete ( page ) ;
271+ this . #mockIssuesManagers. delete ( page ) ;
272+ }
273+ }
274+
266275export class NetworkCollector extends PageCollector < HTTPRequest > {
267276 constructor (
268277 browser : Browser ,
0 commit comments