import * as puppeteer from 'puppeteer' ; import { ConsoleMessage , Page } from 'puppeteer' ; import { SANDBOX_MENU_ITEMS_FILE , SandboxFileInformation } from '../build-sandboxes' ; import { ErrorReporter , REPORT_TYPE } from '../error-reporter' ; import { Config } from '../configure' ; import { waitForNgServe } from '../utils' ; const chalk = require ( 'chalk' ) ; const CHROME_ARGS = [ '--disable-gpu' , '--no-sandbox' ] ; export interface ScenarioSummary { name : string ; description : string ; sandboxKey : string ; scenarioKey : number ; } let browser ; let currentScenario = '' ; let currentScenarioDescription = '' ; let reporter ; process . on ( 'unhandledRejection' , async ( ) => { if ( browser ) { await browser . close ( ) ; } } ) ; export async function verifySandboxes ( config ) { await main ( config ) ; } async function main ( config ) { const hostUrl = `http://localhost:${ config . angularCliPort }` ; const timeoutAttempts = config . timeout ; browser = await puppeteer . launch ( { headless : true , handleSIGINT : false , args : CHROME_ARGS , } ) ; await waitForNgServe ( browser , hostUrl , timeoutAttempts ) ; const scenarios = getSandboxMetadata ( config , hostUrl , config . randomScenario ) ; console . log ( `Retrieved ${ scenarios . length } scenarios.\n` ) ; reporter = new ErrorReporter ( scenarios , config . reportPath , config . reportType ) ; const page = await setupPageAndErrorHandling ( hostUrl ) ; for ( let i = 0 ; i < scenarios . length ; i ++ ) { console . log ( `Checking [${ i + 1 }/${ scenarios . length }]: ${ scenarios [ i ] . name }: ${ scenarios [ i ] . description }` ) ; await openScenario ( scenarios [ i ] , page ) ; } await page . close ( ) ; await browser . close ( ) ; const hasErrors = reporter . errors . length > 0 ; if ( hasErrors || config . reportType !== REPORT_TYPE . LOG ) { reporter . compileReport ( ) ; } const exitCode = hasErrors ? 1 : 0 ; process . exit ( exitCode ) ; } async function setupPageAndErrorHandling ( hostUrl ) { const page = await browser . newPage ( ) ; page . on ( 'console' , ( msg ) => onConsoleErr ( msg ) ) ; console . log ( `Navigating to ${ hostUrl }` ) ; await page . goto ( hostUrl , { waitUntil : 'load' , timeout : 60000 } ) ; return page ; } async function openScenario ( scenario , page ) { currentScenario = scenario . name ; currentScenarioDescription = scenario . description ; await page . evaluate ( ( sandboxKey , scenarioKey ) => window . loadScenario ( sandboxKey , scenarioKey ) , scenario . sandboxKey , scenario . scenarioKey ) ; await page . waitForFunction ( ( ) => window . isPlaygroundComponentLoaded ( ) || window . isPlaygroundComponentLoadedWithErrors ( ) ) ; const sleep = ( ms ) => new Promise ( res => setTimeout ( res , ms ) ) ; await sleep ( 100 ) ; } param config param baseUrl param selectRandomScenario function getSandboxMetadata ( config , baseUrl , selectRandomScenario ) { const scenarios = [ ] ; try { loadSandboxMenuItems ( ) . forEach ( ( sandboxItem ) => { if ( ! config . pathToSandboxes || config . pathToSandboxes . some ( vp => sandboxItem . key . includes ( vp ) ) ) { if ( selectRandomScenario ) { const randomItemKey = getRandomKey ( sandboxItem . scenarioMenuItems . length ) ; for ( const item of sandboxItem . scenarioMenuItems ) { if ( item . key === randomItemKey ) { scenarios . push ( { name : sandboxItem . key , description : item . description , sandboxKey : sandboxItem . key , scenarioKey : item . key } ) ; break ; } } } else { sandboxItem . scenarioMenuItems . forEach ( ( item ) => { scenarios . push ( { name : sandboxItem . key , description : item . description , sandboxKey : sandboxItem . key , scenarioKey : item . key } ) ; } ) ; } } } ) ; } catch ( err ) { throw new Error ( `Error getting sandbox metadata: ${ err }` ) ; } return scenarios ; } /**
 * Attempt to load sandboxes.ts and provide menu items
 */ function loadSandboxMenuItems ( ) { try { return require ( SANDBOX_MENU_ITEMS_FILE ) . getSandboxMenuItems ( ) ; } catch ( err ) { throw new Error ( `Failed to load sandbox menu items. ${ err }` ) ; } } /**
 * Callback when Chromium page encounters a console error
 */ function onConsoleErr ( msg ) { try { if ( msg . type ( ) === 'error' ) { console . error ( chalk . red ( `Error in ${ currentScenario } (${ currentScenarioDescription }):` ) ) ; const getErrors = ( type , getValue ) => msg . args ( ) . map ( a => ( a as any ) . _remoteObject ) . filter ( o => o . type === type ) . map ( getValue ) ; const stackTrace = getErrors ( 'object' , o => o . description ) ; const errorMessage = getErrors ( 'string' , o => o . value ) ; let description = stackTrace . length ? stackTrace : errorMessage ; description = description . length ? description : [ msg . text ( ) ] ; description . map ( d => console . error ( d ) ) ; if ( description . length ) { reporter . addError ( description , currentScenario , currentScenarioDescription ) ; } } } catch ( err ) { console . error ( `Error handling console errors: ${ err }` ) ; } } /**
 * Returns a random value between 1 and the provided length (both inclusive).
 * Note: indexing of keys starts at 1, not 0
 */ function getRandomKey ( menuItemsLength ) { return Math . floor ( Math . random ( ) * menuItemsLength ) + 1 ; }