@@ -15,12 +15,6 @@ import {
1515 expectNoLog ,
1616} from '../setup' ;
1717
18- /**
19- * Maximum time in milliseconds for single build/rebuild
20- * This accounts for CI variability.
21- */
22- export const BUILD_TIMEOUT = 30_000 ;
23-
2418describeBuilder ( buildApplication , APPLICATION_BUILDER_INFO , ( harness ) => {
2519 describe ( 'Behavior: "Rebuild Error Detection"' , ( ) => {
2620 it ( 'detects template errors with no AOT codegen or TS emit differences' , async ( ) => {
@@ -128,6 +122,116 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
128122 ) ;
129123 } ) ;
130124
125+ it ( 'detects template errors across components on watch rebuild when isolatedModules is enabled' , async ( ) => {
126+ harness . useTarget ( 'build' , {
127+ ...BASE_OPTIONS ,
128+ watch : true ,
129+ } ) ;
130+
131+ await harness . modifyFile ( 'tsconfig.json' , ( content ) => {
132+ const tsconfig = JSON . parse ( content ) ;
133+ tsconfig . compilerOptions = {
134+ ...tsconfig . compilerOptions ,
135+ isolatedModules : true ,
136+ } ;
137+
138+ return JSON . stringify ( tsconfig ) ;
139+ } ) ;
140+
141+ const goodChildComponentContents = `
142+ import { Component, Input } from '@angular/core';
143+ @Component({
144+ selector: 'child',
145+ standalone: true,
146+ template: '<p>{{ value }}</p>',
147+ })
148+ export class ChildComponent {
149+ @Input() value!: number;
150+ }
151+ ` ;
152+
153+ const typeErrorText = `Type 'number' is not assignable to type 'string'.` ;
154+
155+ await harness . writeFiles ( {
156+ 'src/app/child.component.ts' : goodChildComponentContents ,
157+ 'src/app/app.module.ts' : `
158+ import { NgModule } from '@angular/core';
159+ import { BrowserModule } from '@angular/platform-browser';
160+ import { AppComponent } from './app.component';
161+ @NgModule({
162+ imports: [
163+ BrowserModule,
164+ AppComponent,
165+ ],
166+ providers: [],
167+ bootstrap: [AppComponent]
168+ })
169+ export class AppModule { }
170+ ` ,
171+ 'src/app/app.component.ts' : `
172+ import { Component } from '@angular/core';
173+ import { ChildComponent } from './child.component';
174+ @Component({
175+ selector: 'app-root',
176+ standalone: true,
177+ imports: [ChildComponent],
178+ template: '<child [value]="123" />',
179+ })
180+ export class AppComponent {}
181+ ` ,
182+ } ) ;
183+
184+ await harness . executeWithCases (
185+ [
186+ async ( { result } ) => {
187+ expect ( result ?. success ) . toBeTrue ( ) ;
188+
189+ // Update child component to change input type from number to string
190+ await harness . writeFile (
191+ 'src/app/child.component.ts' ,
192+ `
193+ import { Component, Input } from '@angular/core';
194+ @Component({
195+ selector: 'child',
196+ standalone: true,
197+ template: '<p>{{ value }}</p>',
198+ })
199+ export class ChildComponent {
200+ @Input() value!: string;
201+ }
202+ ` ,
203+ ) ;
204+ } ,
205+ async ( { result, logs } ) => {
206+ expect ( result ?. success ) . toBeFalse ( ) ;
207+ expectLog ( logs , typeErrorText ) ;
208+
209+ // Make an unrelated change to verify error persists
210+ await harness . modifyFile ( 'src/main.ts' , ( content ) => content + '\n' ) ;
211+ } ,
212+ async ( { result, logs } ) => {
213+ expect ( result ?. success ) . toBeFalse ( ) ;
214+ expectLog ( logs , typeErrorText ) ;
215+
216+ // Revert back to number
217+ await harness . writeFile ( 'src/app/child.component.ts' , goodChildComponentContents ) ;
218+ } ,
219+ async ( { result, logs } ) => {
220+ expect ( result ?. success ) . toBeTrue ( ) ;
221+ expectNoLog ( logs , typeErrorText ) ;
222+
223+ // Make an unrelated change to verify error cache cleared
224+ await harness . modifyFile ( 'src/main.ts' , ( content ) => content + '\n' ) ;
225+ } ,
226+ ( { result, logs } ) => {
227+ expect ( result ?. success ) . toBeTrue ( ) ;
228+ expectNoLog ( logs , typeErrorText ) ;
229+ } ,
230+ ] ,
231+ { outputLogsOnFailure : false } ,
232+ ) ;
233+ } ) ;
234+
131235 it ( 'detects cumulative block syntax errors' , async ( ) => {
132236 harness . useTarget ( 'build' , {
133237 ...BASE_OPTIONS ,
@@ -234,7 +338,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
234338 await harness . appendToFile ( 'src/app/app.component.html' , '<div>Guten Tag</div>' ) ;
235339 } ,
236340 ( { logs } ) => {
237- expectNoLog ( logs , 'invalid-css-content ' ) ;
341+ expectNoLog ( logs , 'Unexpected character "EOF" ' ) ;
238342
239343 harness . expectFile ( 'dist/browser/main.js' ) . content . toContain ( 'Hello, world!' ) ;
240344 harness . expectFile ( 'dist/browser/main.js' ) . content . toContain ( 'Guten Tag' ) ;
0 commit comments