@@ -28,6 +28,7 @@ type CreateAppFixtureOptions = {
2828 addOns ?: Array < string >
2929 postCreateAddOns ?: Array < string >
3030 skipDevServer ?: boolean
31+ runQualityGatesChecks ?: boolean
3132}
3233
3334export type RuntimeGuards = {
@@ -291,18 +292,68 @@ async function runQualityGates(
291292 } )
292293}
293294
295+ function envBoolean ( name : string , defaultValue : boolean ) {
296+ const value = process . env [ name ]
297+ if ( value == null ) {
298+ return defaultValue
299+ }
300+
301+ const normalized = value . trim ( ) . toLowerCase ( )
302+ return normalized === '1' || normalized === 'true' || normalized === 'yes'
303+ }
304+
305+ const IMAGE_GLOB = '**/*.{png,jpg,jpeg,gif,webp,avif,ico}'
306+ const FONT_GLOB = '**/*.{woff,woff2,ttf,otf,eot}'
307+ const MEDIA_GLOB = '**/*.{mp4,webm,mp3,wav,ogg,mov,m4a}'
308+ const CSS_GLOB = '**/*.css'
309+
310+ const abortRoute = ( route : { abort : ( reason ?: string ) => Promise < void > } ) =>
311+ route . abort ( 'blockedbyclient' )
312+
313+ export async function optimizePageForFastE2E ( page : Page ) {
314+ const blockAssets = envBoolean ( 'E2E_BLOCK_NON_ESSENTIAL' , true )
315+ if ( ! blockAssets ) {
316+ return
317+ }
318+
319+ await page . route ( IMAGE_GLOB , abortRoute )
320+ await page . route ( FONT_GLOB , abortRoute )
321+ await page . route ( MEDIA_GLOB , abortRoute )
322+
323+ if ( envBoolean ( 'E2E_BLOCK_CSS' , false ) ) {
324+ await page . route ( CSS_GLOB , abortRoute )
325+ }
326+ }
327+
294328export async function createAppFixture (
295329 options : CreateAppFixtureOptions ,
296330) : Promise < E2EApp > {
297331 await access ( cliDistPath )
298332
333+ const timings : Array < [ string , number ] > = [ ]
334+ const now = ( ) => performance . now ( )
335+ const start = now ( )
336+ const mark = ( label : string , startedAt : number ) => {
337+ timings . push ( [ label , now ( ) - startedAt ] )
338+ }
339+ const logTimings = ( ) => {
340+ if ( ! envBoolean ( 'E2E_TIMINGS' , true ) ) {
341+ return
342+ }
343+
344+ const totalMs = now ( ) - start
345+ const summary = timings . map ( ( [ label , ms ] ) => `${ label } =${ Math . round ( ms ) } ms` ) . join ( ' | ' )
346+ console . log ( `[e2e:timing] ${ options . appName } :: ${ summary } | total=${ Math . round ( totalMs ) } ms` )
347+ }
348+
299349 const rootDir = await mkdtemp ( join ( tmpdir ( ) , 'tanstack-cli-e2e-' ) )
300350 const {
301351 appName,
302352 template,
303353 addOns,
304354 postCreateAddOns,
305355 skipDevServer,
356+ runQualityGatesChecks = false ,
306357 framework = 'react' ,
307358 packageManager = 'pnpm' ,
308359 routerOnly = false ,
@@ -332,6 +383,7 @@ export async function createAppFixture(
332383 createArgs . push ( '--add-ons' , addOns . join ( ',' ) )
333384 }
334385
386+ const createStartedAt = now ( )
335387 await runCommand (
336388 'node' ,
337389 createArgs ,
@@ -342,23 +394,32 @@ export async function createAppFixture(
342394 } ,
343395 } ,
344396 )
397+ mark ( 'create' , createStartedAt )
345398
346399 await patchViteConfigForE2E ( appDir )
347400
348401 if ( postCreateAddOns ?. length ) {
402+ const postAddOnsStartedAt = now ( )
349403 await runCommand ( 'node' , [ cliDistPath , 'add' , ...postCreateAddOns ] , {
350404 cwd : appDir ,
351405 env : {
352406 CI : '1' ,
353407 } ,
354408 } )
355409
410+ mark ( 'post-add-ons' , postAddOnsStartedAt )
411+
356412 await patchViteConfigForE2E ( appDir )
357413 }
358414
359- await runQualityGates ( appDir , packageManager )
415+ if ( runQualityGatesChecks ) {
416+ const qualityGatesStartedAt = now ( )
417+ await runQualityGates ( appDir , packageManager )
418+ mark ( 'quality-gates' , qualityGatesStartedAt )
419+ }
360420
361421 if ( skipDevServer ) {
422+ logTimings ( )
362423 return {
363424 rootDir,
364425 appDir,
@@ -374,6 +435,7 @@ export async function createAppFixture(
374435
375436 const dev = getPackageManagerCommandForScript ( packageManager , 'dev' )
376437
438+ const devServerStartedAt = now ( )
377439 const server = spawn ( dev . command , dev . args , {
378440 cwd : appDir ,
379441 env : {
@@ -398,13 +460,16 @@ export async function createAppFixture(
398460 try {
399461 url = await waitForDevServerURL ( ( ) => serverStdout )
400462 await waitForServer ( url )
463+ mark ( 'dev-server-ready' , devServerStartedAt )
401464 } catch ( error ) {
402465 await stopChild ( server )
403466 throw new Error (
404467 `Failed to start app server at ${ url } \nstdout:\n${ serverStdout } \n\nstderr:\n${ serverStderr } \n\n${ error } ` ,
405468 )
406469 }
407470
471+ logTimings ( )
472+
408473 return {
409474 rootDir,
410475 appDir,
@@ -429,6 +494,10 @@ function toSameOrigin(url: string, appOrigin: URL) {
429494 }
430495}
431496
497+ function isBlockedByClientError ( value : string ) {
498+ return value . includes ( 'ERR_BLOCKED_BY_CLIENT' )
499+ }
500+
432501export function attachRuntimeGuards ( page : Page , appUrl : string ) : RuntimeGuards {
433502 const appOrigin = new URL ( appUrl )
434503 const pageErrors : Array < string > = [ ]
@@ -442,7 +511,11 @@ export function attachRuntimeGuards(page: Page, appUrl: string): RuntimeGuards {
442511
443512 const onConsole = ( message : { type : ( ) => string ; text : ( ) => string } ) => {
444513 if ( message . type ( ) === 'error' ) {
445- consoleErrors . push ( message . text ( ) )
514+ const text = message . text ( )
515+ if ( isBlockedByClientError ( text ) ) {
516+ return
517+ }
518+ consoleErrors . push ( text )
446519 }
447520 }
448521
@@ -457,7 +530,7 @@ export function attachRuntimeGuards(page: Page, appUrl: string): RuntimeGuards {
457530 }
458531 const errorText = request . failure ( ) ?. errorText || 'unknown error'
459532
460- if ( errorText . includes ( 'ERR_ABORTED' ) ) {
533+ if ( errorText . includes ( 'ERR_ABORTED' ) || isBlockedByClientError ( errorText ) ) {
461534 return
462535 }
463536
0 commit comments