66import assert from 'node:assert' ;
77import { describe , it } from 'node:test' ;
88
9- import type { Browser , Frame , HTTPRequest , Page , Target } from 'puppeteer-core' ;
9+ import type { Browser , Frame , HTTPRequest , Page , Target , CDPSession } from 'puppeteer-core' ;
10+ import sinon from 'sinon' ;
1011
12+ import { AggregatedIssue } from '../node_modules/chrome-devtools-frontend/mcp/mcp.js' ;
1113import type { ListenerMap } from '../src/PageCollector.js' ;
12- import { NetworkCollector , PageCollector } from '../src/PageCollector.js' ;
14+ import {
15+ ConsoleCollector ,
16+ NetworkCollector ,
17+ PageCollector ,
18+ } from '../src/PageCollector.js' ;
1319
1420import { getMockRequest } from './utils.js' ;
1521
@@ -36,10 +42,19 @@ function mockListener() {
3642
3743function getMockPage ( ) : Page {
3844 const mainFrame = { } as Frame ;
45+ const cdpSession = {
46+ ...mockListener ( ) ,
47+ send : ( ) => {
48+ // no-op
49+ } ,
50+ } ;
3951 return {
4052 mainFrame ( ) {
4153 return mainFrame ;
4254 } ,
55+ createCDPSession ( ) {
56+ return Promise . resolve ( cdpSession as unknown as CDPSession ) ;
57+ } ,
4358 ...mockListener ( ) ,
4459 } as Page ;
4560}
@@ -287,7 +302,7 @@ describe('NetworkCollector', () => {
287302 assert . equal ( collector . getData ( page ) . length , 2 ) ;
288303 } ) ;
289304
290- it ( 'works with previous navigations ' , async ( ) => {
305+ it ( 'works with previous navigatedations ' , async ( ) => {
291306 const browser = getMockBrowser ( ) ;
292307 const page = ( await browser . pages ( ) ) [ 0 ] ;
293308 const mainFrame = page . mainFrame ( ) ;
@@ -319,3 +334,110 @@ describe('NetworkCollector', () => {
319334 assert . equal ( collector . getData ( page , true ) . length , 3 ) ;
320335 } ) ;
321336} ) ;
337+
338+ describe ( 'ConsoleCollector' , ( ) => {
339+ it ( 'emits issues on page' , async ( ) => {
340+ const browser = getMockBrowser ( ) ;
341+ const page = ( await browser . pages ( ) ) [ 0 ] ;
342+ const cdpSession = await page . createCDPSession ( ) ;
343+ const onIssuesListener = sinon . spy ( ) ;
344+
345+ page . on ( 'issue' , onIssuesListener ) ;
346+
347+ const collector = new ConsoleCollector ( browser , collect => {
348+ return {
349+ issue : issue => {
350+ collect ( issue as AggregatedIssue ) ;
351+ } ,
352+ } as ListenerMap ;
353+ } ) ;
354+ await collector . init ( ) ;
355+
356+ const issue = {
357+ code : 'MixedContentIssue' as const ,
358+ details : {
359+ mixedContentIssueDetails : {
360+ insecureURL : "test.url"
361+ }
362+ } ,
363+ } ;
364+
365+ // @ts -expect-error Types of protocol from Puppeteer and CDP are incopatible for Issues but it's the same type
366+ cdpSession . emit ( 'Audits.issueAdded' , { issue} ) ;
367+ sinon . assert . calledOnce ( onIssuesListener ) ;
368+
369+ const issueArgument = onIssuesListener . getCall ( 0 ) . args [ 0 ] ;
370+ assert ( issueArgument instanceof AggregatedIssue ) ;
371+ } ) ;
372+
373+ it ( 'collects issues' , async ( ) => {
374+ const browser = getMockBrowser ( ) ;
375+ const page = ( await browser . pages ( ) ) [ 0 ] ;
376+ const cdpSession = await page . createCDPSession ( ) ;
377+
378+ const collector = new ConsoleCollector ( browser , collect => {
379+ return {
380+ issue : issue => {
381+ collect ( issue as AggregatedIssue ) ;
382+ } ,
383+ } as ListenerMap ;
384+ } ) ;
385+ await collector . init ( ) ;
386+
387+ const issue = {
388+ code : 'MixedContentIssue' as const ,
389+ details : {
390+ mixedContentIssueDetails : {
391+ insecureURL : "test.url"
392+ } ,
393+ } ,
394+ } ;
395+ const issue2 = {
396+ code : 'PropertyRuleIssue' as const ,
397+ details : {
398+ propertyRuleIssueDetails : {
399+ test : "test"
400+ } ,
401+ } ,
402+ } ;
403+
404+ // @ts -expect-error Types of protocol from Puppeteer and CDP are incopatible for Issues but it's the same type
405+ cdpSession . emit ( 'Audits.issueAdded' , { issue} ) ;
406+ // @ts -expect-error Types of protocol from Puppeteer and CDP are incopatible for Issues but it's the same type
407+ cdpSession . emit ( 'Audits.issueAdded' , { issue : issue2 } ) ;
408+ const data = collector . getData ( page ) ;
409+ assert . equal ( data . length , 2 ) ;
410+ } ) ;
411+
412+ it ( 'filters duplicated issues' , async ( ) => {
413+ const browser = getMockBrowser ( ) ;
414+ const page = ( await browser . pages ( ) ) [ 0 ] ;
415+ const cdpSession = await page . createCDPSession ( ) ;
416+
417+ const collector = new ConsoleCollector ( browser , collect => {
418+ return {
419+ issue : issue => {
420+ collect ( issue as AggregatedIssue ) ;
421+ } ,
422+ } as ListenerMap ;
423+ } ) ;
424+ await collector . init ( ) ;
425+
426+ const issue = {
427+ code : 'MixedContentIssue' as const ,
428+ details : {
429+ mixedContentIssueDetails : {
430+ insecureURL : "test.url"
431+ } ,
432+ } ,
433+ } ;
434+
435+ // @ts -expect-error Types of protocol from Puppeteer and CDP are incopatible for Issues but it's the same type
436+ cdpSession . emit ( 'Audits.issueAdded' , { issue} ) ;
437+ // @ts -expect-error Types of protocol from Puppeteer and CDP are incopatible for Issues but it's the same type
438+ cdpSession . emit ( 'Audits.issueAdded' , { issue} ) ;
439+ const collectedIssue = collector . getData ( page ) [ 0 ] as AggregatedIssue ;
440+ assert . equal ( collectedIssue . code ( ) , 'MixedContentIssue' ) ;
441+ assert . equal ( collectedIssue . getAggregatedIssuesCount ( ) , 1 ) ;
442+ } ) ;
443+ } ) ;
0 commit comments