@@ -312,5 +312,131 @@ Help me understand?`;
312312 canceled : false
313313 } ) ;
314314 } ) ;
315+
316+ it ( 'caches getNetworkActivitySummary calls and passes them to future requests as facts' , async function ( ) {
317+ const { parsedTrace, insights} = await TraceLoader . traceEngine ( this , 'lcp-images.json.gz' ) ;
318+ assert . isOk ( insights ) ;
319+ const [ firstNav ] = parsedTrace . Meta . mainFrameNavigations ;
320+ const lcpPhases = getInsightOrError ( 'LCPPhases' , insights , firstNav ) ;
321+ const agent = new PerformanceInsightsAgent ( {
322+ aidaClient : mockAidaClient ( [
323+ [ { explanation : '' , functionCalls : [ { name : 'getNetworkActivitySummary' , args : { } } ] } ] , [ { explanation : 'done' } ]
324+ ] )
325+ } ) ;
326+ const activeInsight = new TimelineUtils . InsightAIContext . ActiveInsight ( lcpPhases , parsedTrace ) ;
327+ const context = new InsightContext ( activeInsight ) ;
328+
329+ // Make the first query to trigger the getNetworkActivitySummary function
330+ const responses = await Array . fromAsync ( agent . run ( 'test' , { selected : context } ) ) ;
331+ const action = responses . find ( response => response . type === ResponseType . ACTION ) ;
332+ assert . exists ( action ) ;
333+ assert . strictEqual ( action . code , 'getNetworkActivitySummary()' ) ;
334+
335+ // Trigger another request so that the agent populates the facts.
336+ await Array . fromAsync ( agent . run ( 'test 2' , { selected : context } ) ) ;
337+
338+ assert . strictEqual ( agent . currentFacts ( ) . size , 1 ) ;
339+ const networkSummaryFact = Array . from ( agent . currentFacts ( ) ) . at ( 0 ) ;
340+ assert . exists ( networkSummaryFact ) ;
341+
342+ const expectedRequestUrls = [
343+ 'https://chromedevtools.github.io/performance-stories/lcp-large-image/index.html' ,
344+ 'https://fonts.googleapis.com/css2?family=Poppins:ital,wght@1,800' ,
345+ 'https://chromedevtools.github.io/performance-stories/lcp-large-image/app.css' ,
346+ 'https://via.placeholder.com/50.jpg' , 'https://via.placeholder.com/2000.jpg'
347+ ] ;
348+ // Ensure that each URL was in the fact as a way to validate the fact is accurate.
349+ assert . isTrue ( expectedRequestUrls . every ( url => {
350+ return networkSummaryFact . text . includes ( url ) ;
351+ } ) ) ;
352+
353+ // Now we make one more request; we do this to ensure that we don't add the same fact again.
354+ await Array . fromAsync ( agent . run ( 'test 3' , { selected : context } ) ) ;
355+
356+ assert . strictEqual ( agent . currentFacts ( ) . size , 1 ) ;
357+ } ) ;
358+
359+ it ( 'caches getMainThreadActivity calls and passes them to future requests as facts' , async function ( ) {
360+ const { parsedTrace, insights} = await TraceLoader . traceEngine ( this , 'lcp-discovery-delay.json.gz' ) ;
361+ assert . isOk ( insights ) ;
362+ const [ firstNav ] = parsedTrace . Meta . mainFrameNavigations ;
363+ const lcpPhases = getInsightOrError ( 'LCPPhases' , insights , firstNav ) ;
364+ const agent = new PerformanceInsightsAgent ( {
365+ aidaClient : mockAidaClient (
366+ [ [ { explanation : '' , functionCalls : [ { name : 'getMainThreadActivity' , args : { } } ] } ] , [ { explanation : 'done' } ] ] )
367+ } ) ;
368+ const activeInsight = new TimelineUtils . InsightAIContext . ActiveInsight ( lcpPhases , parsedTrace ) ;
369+ const context = new InsightContext ( activeInsight ) ;
370+
371+ // Make the first query to trigger the getMainThreadActivity function
372+ const responses = await Array . fromAsync ( agent . run ( 'test' , { selected : context } ) ) ;
373+ const action = responses . find ( response => response . type === ResponseType . ACTION ) ;
374+ assert . exists ( action ) ;
375+ assert . strictEqual ( action . code , 'getMainThreadActivity()' ) ;
376+
377+ // Trigger another request so that the agent populates the facts.
378+ await Array . fromAsync ( agent . run ( 'test 2' , { selected : context } ) ) ;
379+
380+ assert . strictEqual ( agent . currentFacts ( ) . size , 1 ) ;
381+ const mainThreadActivityFact = Array . from ( agent . currentFacts ( ) ) . at ( 0 ) ;
382+ assert . exists ( mainThreadActivityFact ) ;
383+
384+ const expectedTree = TimelineUtils . InsightAIContext . AIQueries . mainThreadActivity ( lcpPhases , parsedTrace ) ;
385+ assert . isOk ( expectedTree ) ;
386+ assert . include ( mainThreadActivityFact . text , expectedTree . serialize ( ) ) ;
387+
388+ // Now we make one more request; we do this to ensure that we don't add the same fact again.
389+ await Array . fromAsync ( agent . run ( 'test 3' , { selected : context } ) ) ;
390+
391+ assert . strictEqual ( agent . currentFacts ( ) . size , 1 ) ;
392+ } ) ;
393+
394+ it ( 'will not send facts from a previous insight if the context changes' , async function ( ) {
395+ const { parsedTrace, insights} = await TraceLoader . traceEngine ( this , 'lcp-discovery-delay.json.gz' ) ;
396+ assert . isOk ( insights ) ;
397+ const [ firstNav ] = parsedTrace . Meta . mainFrameNavigations ;
398+ const lcpPhases = getInsightOrError ( 'LCPPhases' , insights , firstNav ) ;
399+ const renderBlocking = getInsightOrError ( 'RenderBlocking' , insights , firstNav ) ;
400+ const agent = new PerformanceInsightsAgent ( {
401+ aidaClient : mockAidaClient ( [
402+ [ { explanation : '' , functionCalls : [ { name : 'getMainThreadActivity' , args : { } } ] } ] ,
403+ ] )
404+ } ) ;
405+ const lcpPhasesActiveInsight = new TimelineUtils . InsightAIContext . ActiveInsight ( lcpPhases , parsedTrace ) ;
406+ const lcpContext = new InsightContext ( lcpPhasesActiveInsight ) ;
407+ const renderBlockingActiveInsight = new TimelineUtils . InsightAIContext . ActiveInsight ( renderBlocking , parsedTrace ) ;
408+ const renderBlockingContext = new InsightContext ( renderBlockingActiveInsight ) ;
409+
410+ // Populate the function calls for the LCP Context
411+ await Array . fromAsync ( agent . run ( 'test 1 LCP' , { selected : lcpContext } ) ) ;
412+ await Array . fromAsync ( agent . run ( 'test 2 LCP' , { selected : lcpContext } ) ) ;
413+ assert . strictEqual ( agent . currentFacts ( ) . size , 1 ) ;
414+ // Now change the context and send a request.
415+ await Array . fromAsync ( agent . run ( 'test 1 RenderBlocking' , { selected : renderBlockingContext } ) ) ;
416+ // Because the context changed, we should now not have any facts.
417+ assert . strictEqual ( agent . currentFacts ( ) . size , 0 ) ;
418+ } ) ;
419+
420+ it ( 'will send multiple facts' , async function ( ) {
421+ const { parsedTrace, insights} = await TraceLoader . traceEngine ( this , 'lcp-discovery-delay.json.gz' ) ;
422+ assert . isOk ( insights ) ;
423+ const [ firstNav ] = parsedTrace . Meta . mainFrameNavigations ;
424+ const lcpPhases = getInsightOrError ( 'LCPPhases' , insights , firstNav ) ;
425+ const agent = new PerformanceInsightsAgent ( {
426+ aidaClient : mockAidaClient ( [
427+ [ { explanation : '' , functionCalls : [ { name : 'getMainThreadActivity' , args : { } } ] } ] ,
428+ [ { explanation : '' , functionCalls : [ { name : 'getNetworkActivitySummary' , args : { } } ] } ] , [ { explanation : 'done' } ]
429+ ] )
430+ } ) ;
431+ const activeInsight = new TimelineUtils . InsightAIContext . ActiveInsight ( lcpPhases , parsedTrace ) ;
432+ const context = new InsightContext ( activeInsight ) ;
433+ // First query to populate the function calls
434+ await Array . fromAsync ( agent . run ( 'test 1' , { selected : context } ) ) ;
435+ // Second query should have two facts
436+ await Array . fromAsync ( agent . run ( 'test 2' , { selected : context } ) ) ;
437+ assert . deepEqual ( Array . from ( agent . currentFacts ( ) , fact => {
438+ return fact . metadata . source ;
439+ } ) , [ 'getMainThreadActivity()' , 'getNetworkActivitySummary()' ] ) ;
440+ } ) ;
315441 } ) ;
316442} ) ;
0 commit comments