@@ -48,6 +48,7 @@ import {JSACTION_BLOCK_ELEMENT_MAP} from '@angular/core/src/hydration/tokens';
4848import { JSACTION_EVENT_CONTRACT } from '@angular/core/src/event_delegation_utils' ;
4949import { provideRouter , RouterLink , RouterOutlet , Routes } from '@angular/router' ;
5050import { MockPlatformLocation } from '@angular/common/testing' ;
51+ import { TimerScheduler } from '@angular/core/src/defer/timer_scheduler' ;
5152
5253/**
5354 * Emulates a dynamic import promise.
@@ -1341,14 +1342,19 @@ describe('platform-server partial hydration integration', () => {
13411342 } ) ;
13421343
13431344 describe ( 'timer' , ( ) => {
1344- const TEST_TIMEOUT = 10_000 ; // 10 seconds
1345-
1346- it (
1347- 'top level timer' ,
1348- async ( ) => {
1349- @Component ( {
1350- selector : 'app' ,
1351- template : `
1345+ class FakeTimerScheduler {
1346+ add ( delay : number , callback : VoidFunction ) {
1347+ callback ( ) ;
1348+ }
1349+ remove ( callback : VoidFunction ) {
1350+ /* noop */
1351+ }
1352+ }
1353+
1354+ it ( 'top level timer' , async ( ) => {
1355+ @Component ( {
1356+ selector : 'app' ,
1357+ template : `
13521358 <main (click)="fnA()">
13531359 @defer (hydrate on timer(150)) {
13541360 <article>
@@ -1360,62 +1366,56 @@ describe('platform-server partial hydration integration', () => {
13601366 }
13611367 </main>
13621368 ` ,
1363- } )
1364- class SimpleComponent {
1365- value = signal ( 'start' ) ;
1366- fnA ( ) { }
1367- fnB ( ) {
1368- this . value . set ( 'end' ) ;
1369- }
1369+ } )
1370+ class SimpleComponent {
1371+ value = signal ( 'start' ) ;
1372+ fnA ( ) { }
1373+ fnB ( ) {
1374+ this . value . set ( 'end' ) ;
13701375 }
1376+ }
13711377
1372- const appId = 'custom-app-id' ;
1373- const providers = [ { provide : APP_ID , useValue : appId } ] ;
1374- const hydrationFeatures = ( ) => [ withIncrementalHydration ( ) ] ;
1375-
1376- const html = await ssr ( SimpleComponent , { envProviders : providers , hydrationFeatures} ) ;
1377- const ssrContents = getAppContents ( html ) ;
1378-
1379- // <main> uses "eager" `custom-app-id` namespace.
1380- expect ( ssrContents ) . toContain ( '<main jsaction="click:;' ) ;
1381- // <div>s inside a defer block have `d0` as a namespace.
1382- expect ( ssrContents ) . toContain ( '<article>' ) ;
1383- // Outer defer block is rendered.
1384- expect ( ssrContents ) . toContain ( 'defer block rendered' ) ;
1385-
1386- // Internal cleanup before we do server->client transition in this test.
1387- resetTViewsFor ( SimpleComponent ) ;
1388-
1389- ////////////////////////////////
1390- const doc = getDocument ( ) ;
1391- const appRef = await prepareEnvironmentAndHydrate ( doc , html , SimpleComponent , {
1392- envProviders : [ ...providers , { provide : PLATFORM_ID , useValue : 'browser' } ] ,
1393- hydrationFeatures,
1394- } ) ;
1395- const compRef = getComponentRef < SimpleComponent > ( appRef ) ;
1396- appRef . tick ( ) ;
1397- await appRef . whenStable ( ) ;
1398-
1399- const appHostNode = compRef . location . nativeElement ;
1400-
1401- expect ( appHostNode . outerHTML ) . toContain ( '<article>' ) ;
1402-
1403- await timeout ( 500 ) ; // wait for timer
1404- await appRef . whenStable ( ) ;
1405- await allPendingDynamicImports ( ) ;
1406- appRef . tick ( ) ;
1407-
1408- expect ( appHostNode . outerHTML ) . toContain ( '<span id="test">start</span>' ) ;
1409- } ,
1410- TEST_TIMEOUT ,
1411- ) ;
1378+ const appId = 'custom-app-id' ;
1379+ const providers = [
1380+ { provide : APP_ID , useValue : appId } ,
1381+ { provide : TimerScheduler , useClass : FakeTimerScheduler } ,
1382+ ] ;
1383+ const hydrationFeatures = ( ) => [ withIncrementalHydration ( ) ] ;
1384+
1385+ const html = await ssr ( SimpleComponent , { envProviders : providers , hydrationFeatures} ) ;
1386+ const ssrContents = getAppContents ( html ) ;
14121387
1413- it (
1414- 'nested timer' ,
1415- async ( ) => {
1416- @Component ( {
1417- selector : 'app' ,
1418- template : `
1388+ // <main> uses "eager" `custom-app-id` namespace.
1389+ expect ( ssrContents ) . toContain ( '<main jsaction="click:;' ) ;
1390+ // <div>s inside a defer block have `d0` as a namespace.
1391+ expect ( ssrContents ) . toContain ( '<article>' ) ;
1392+ // Outer defer block is rendered.
1393+ expect ( ssrContents ) . toContain ( 'defer block rendered' ) ;
1394+
1395+ // Internal cleanup before we do server->client transition in this test.
1396+ resetTViewsFor ( SimpleComponent ) ;
1397+
1398+ ////////////////////////////////
1399+ const doc = getDocument ( ) ;
1400+ const appRef = await prepareEnvironmentAndHydrate ( doc , html , SimpleComponent , {
1401+ envProviders : [ ...providers , { provide : PLATFORM_ID , useValue : 'browser' } ] ,
1402+ hydrationFeatures,
1403+ } ) ;
1404+ const compRef = getComponentRef < SimpleComponent > ( appRef ) ;
1405+ await appRef . whenStable ( ) ;
1406+
1407+ const appHostNode = compRef . location . nativeElement ;
1408+
1409+ expect ( appHostNode . outerHTML ) . toContain ( '<article>' ) ;
1410+ await allPendingDynamicImports ( ) ;
1411+
1412+ expect ( appHostNode . outerHTML ) . toContain ( '<span id="test">start</span>' ) ;
1413+ } ) ;
1414+
1415+ it ( 'nested timer' , async ( ) => {
1416+ @Component ( {
1417+ selector : 'app' ,
1418+ template : `
14191419 <main (click)="fnA()">
14201420 @defer (on viewport; hydrate on interaction) {
14211421 <div id="main" (click)="fnA()">
@@ -1434,57 +1434,54 @@ describe('platform-server partial hydration integration', () => {
14341434 }
14351435 </main>
14361436 ` ,
1437- } )
1438- class SimpleComponent {
1439- value = signal ( 'start' ) ;
1440- fnA ( ) { }
1441- constructor ( ) {
1442- if ( ! isPlatformServer ( inject ( PLATFORM_ID ) ) ) {
1443- this . value . set ( 'end' ) ;
1444- }
1437+ } )
1438+ class SimpleComponent {
1439+ value = signal ( 'start' ) ;
1440+ fnA ( ) { }
1441+ constructor ( ) {
1442+ if ( ! isPlatformServer ( inject ( PLATFORM_ID ) ) ) {
1443+ this . value . set ( 'end' ) ;
14451444 }
14461445 }
1446+ }
14471447
1448- const appId = 'custom-app-id' ;
1449- const providers = [ { provide : APP_ID , useValue : appId } ] ;
1450- const hydrationFeatures = ( ) => [ withIncrementalHydration ( ) ] ;
1451-
1452- const html = await ssr ( SimpleComponent , { envProviders : providers , hydrationFeatures} ) ;
1453- const ssrContents = getAppContents ( html ) ;
1454-
1455- // <main> uses "eager" `custom-app-id` namespace.
1456- expect ( ssrContents ) . toContain ( '<main jsaction="click:;' ) ;
1457- // <div>s inside a defer block have `d0` as a namespace.
1458- expect ( ssrContents ) . toContain ( '<article>' ) ;
1459- // Outer defer block is rendered.
1460- expect ( ssrContents ) . toContain ( 'defer block rendered' ) ;
1461-
1462- // Internal cleanup before we do server->client transition in this test.
1463- resetTViewsFor ( SimpleComponent ) ;
1464-
1465- ////////////////////////////////
1466- const doc = getDocument ( ) ;
1467- const appRef = await prepareEnvironmentAndHydrate ( doc , html , SimpleComponent , {
1468- envProviders : [ ...providers , { provide : PLATFORM_ID , useValue : 'browser' } ] ,
1469- hydrationFeatures,
1470- } ) ;
1471- const compRef = getComponentRef < SimpleComponent > ( appRef ) ;
1472- appRef . tick ( ) ;
1473- await appRef . whenStable ( ) ;
1474-
1475- const appHostNode = compRef . location . nativeElement ;
1476-
1477- expect ( appHostNode . outerHTML ) . toContain ( '<article>' ) ;
1478-
1479- await timeout ( 500 ) ; // wait for timer
1480- await appRef . whenStable ( ) ;
1481- await allPendingDynamicImports ( ) ;
1482- appRef . tick ( ) ;
1483-
1484- expect ( appHostNode . outerHTML ) . toContain ( '<span id="test">end</span>' ) ;
1485- } ,
1486- TEST_TIMEOUT ,
1487- ) ;
1448+ const appId = 'custom-app-id' ;
1449+ const providers = [
1450+ { provide : APP_ID , useValue : appId } ,
1451+ { provide : TimerScheduler , useClass : FakeTimerScheduler } ,
1452+ ] ;
1453+ const hydrationFeatures = ( ) => [ withIncrementalHydration ( ) ] ;
1454+
1455+ const html = await ssr ( SimpleComponent , { envProviders : providers , hydrationFeatures} ) ;
1456+ const ssrContents = getAppContents ( html ) ;
1457+
1458+ // <main> uses "eager" `custom-app-id` namespace.
1459+ expect ( ssrContents ) . toContain ( '<main jsaction="click:;' ) ;
1460+ // <div>s inside a defer block have `d0` as a namespace.
1461+ expect ( ssrContents ) . toContain ( '<article>' ) ;
1462+ // Outer defer block is rendered.
1463+ expect ( ssrContents ) . toContain ( 'defer block rendered' ) ;
1464+
1465+ // Internal cleanup before we do server->client transition in this test.
1466+ resetTViewsFor ( SimpleComponent ) ;
1467+
1468+ ////////////////////////////////
1469+ const doc = getDocument ( ) ;
1470+ const appRef = await prepareEnvironmentAndHydrate ( doc , html , SimpleComponent , {
1471+ envProviders : [ ...providers , { provide : PLATFORM_ID , useValue : 'browser' } ] ,
1472+ hydrationFeatures,
1473+ } ) ;
1474+ const compRef = getComponentRef < SimpleComponent > ( appRef ) ;
1475+ await appRef . whenStable ( ) ;
1476+
1477+ const appHostNode = compRef . location . nativeElement ;
1478+
1479+ expect ( appHostNode . outerHTML ) . toContain ( '<article>' ) ;
1480+
1481+ await allPendingDynamicImports ( ) ;
1482+
1483+ expect ( appHostNode . outerHTML ) . toContain ( '<span id="test">end</span>' ) ;
1484+ } ) ;
14881485 } ) ;
14891486
14901487 it ( 'when' , async ( ) => {
0 commit comments