@@ -197,6 +197,150 @@ describe('instructions', () => {
197197 } ) ;
198198
199199 describe ( 'styleProp' , ( ) => {
200+ it ( 'should warn when a style property is bound to a boolean' , ( ) => {
201+ const warnSpy = spyOn ( console , 'warn' ) ;
202+ const t = new ViewFixture ( {
203+ create : createDiv ,
204+ update : ( ) => ɵɵstyleProp ( 'display' , true ) ,
205+ decls : 1 ,
206+ vars : 2 ,
207+ } ) ;
208+
209+ t . update ( ) ;
210+
211+ expect ( warnSpy ) . toHaveBeenCalledOnceWith (
212+ 'NG0318: `[style.display]` was bound to an invalid value. ' +
213+ 'Expected a string, number, SafeValue, null, or undefined, but received `boolean` (`true`). ' +
214+ 'Find more at https://next.angular.dev/errors/NG0318' ,
215+ ) ;
216+ } ) ;
217+
218+ it ( 'should warn when a style property is bound to an object' , ( ) => {
219+ const warnSpy = spyOn ( console , 'warn' ) ;
220+ const t = new ViewFixture ( {
221+ create : createDiv ,
222+ update : ( ) => ɵɵstyleProp ( 'color' , { r : 255 , g : 0 , b : 0 } ) ,
223+ decls : 1 ,
224+ vars : 2 ,
225+ } ) ;
226+
227+ t . update ( ) ;
228+
229+ expect ( warnSpy ) . toHaveBeenCalledOnceWith (
230+ 'NG0318: `[style.color]` was bound to an invalid value. ' +
231+ 'Expected a string, number, SafeValue, null, or undefined, but received ' +
232+ '`object` (`[object Object]`). ' +
233+ 'Find more at https://next.angular.dev/errors/NG0318' ,
234+ ) ;
235+ } ) ;
236+
237+ it ( 'should warn when a style property is bound to a non-style SafeValue' , ( ) => {
238+ const warnSpy = spyOn ( console , 'warn' ) ;
239+ const t = new ViewFixture ( {
240+ create : createDiv ,
241+ update : ( ) => ɵɵstyleProp ( 'color' , bypassSanitizationTrustHtml ( 'red' ) ) ,
242+ decls : 1 ,
243+ vars : 2 ,
244+ } ) ;
245+
246+ t . update ( ) ;
247+
248+ expect ( warnSpy ) . toHaveBeenCalledOnceWith (
249+ 'NG0318: `[style.color]` was bound to an invalid value. ' +
250+ 'Expected a string, number, SafeValue, null, or undefined, but received `object` ' +
251+ '(`SafeValue must use [property]=binding: red ' +
252+ '(see https://angular.dev/best-practices/security#preventing-cross-site-scripting-xss)`). ' +
253+ 'Find more at https://next.angular.dev/errors/NG0318' ,
254+ ) ;
255+ } ) ;
256+
257+ it ( 'should unwrap a style SafeValue before appending a suffix' , ( ) => {
258+ const warnSpy = spyOn ( console , 'warn' ) ;
259+ const t = new ViewFixture ( {
260+ create : createDiv ,
261+ update : ( ) => ɵɵstyleProp ( 'width' , bypassSanitizationTrustStyle ( '100' ) , 'px' ) ,
262+ decls : 1 ,
263+ vars : 2 ,
264+ } ) ;
265+
266+ t . update ( ) ;
267+
268+ expect ( ( t . host . firstChild as HTMLElement ) . style . width ) . toBe ( '100px' ) ;
269+ expect ( warnSpy ) . not . toHaveBeenCalled ( ) ;
270+ } ) ;
271+
272+ it ( 'should warn when a style property with a suffix is bound to a non-style SafeValue' , ( ) => {
273+ const warnSpy = spyOn ( console , 'warn' ) ;
274+ const t = new ViewFixture ( {
275+ create : createDiv ,
276+ update : ( ) => ɵɵstyleProp ( 'width' , bypassSanitizationTrustHtml ( '100' ) , 'px' ) ,
277+ decls : 1 ,
278+ vars : 2 ,
279+ } ) ;
280+
281+ t . update ( ) ;
282+
283+ expect ( warnSpy ) . toHaveBeenCalledOnceWith (
284+ 'NG0318: `[style.width]` was bound to an invalid value. ' +
285+ 'Expected a string, number, SafeValue, null, or undefined, but received `object` ' +
286+ '(`SafeValue must use [property]=binding: 100 ' +
287+ '(see https://angular.dev/best-practices/security#preventing-cross-site-scripting-xss)`). ' +
288+ 'Find more at https://next.angular.dev/errors/NG0318' ,
289+ ) ;
290+ } ) ;
291+
292+ it ( 'should continue binding when an invalid value has a throwing getter' , ( ) => {
293+ const warnSpy = spyOn ( console , 'warn' ) ;
294+ const value = Object . assign ( ( ) => { } , { toString : ( ) => 'applied-value' } ) ;
295+ Object . defineProperty ( value , 'overriddenName' , {
296+ get : ( ) => {
297+ throw new Error ( 'Unexpected read of overriddenName' ) ;
298+ } ,
299+ } ) ;
300+ const t = new ViewFixture ( {
301+ create : createDiv ,
302+ update : ( ) => ɵɵstyleProp ( '--test-value' , value ) ,
303+ decls : 1 ,
304+ vars : 2 ,
305+ } ) ;
306+
307+ t . update ( ) ;
308+
309+ expect ( ( t . host . firstChild as HTMLElement ) . style . getPropertyValue ( '--test-value' ) ) . toBe (
310+ 'applied-value' ,
311+ ) ;
312+ expect ( warnSpy ) . toHaveBeenCalledOnceWith (
313+ 'NG0318: `[style.--test-value]` was bound to an invalid value. ' +
314+ 'Expected a string, number, SafeValue, null, or undefined, but received ' +
315+ '`function` (`[unstringifiable value]`). ' +
316+ 'Find more at https://next.angular.dev/errors/NG0318' ,
317+ ) ;
318+ } ) ;
319+
320+ it ( 'should not warn when a style property is bound to a supported value' , ( ) => {
321+ const warnSpy = spyOn ( console , 'warn' ) ;
322+ let value : string | number | SafeValue | null | undefined = 'block' ;
323+ const t = new ViewFixture ( {
324+ create : createDiv ,
325+ update : ( ) => ɵɵstyleProp ( 'display' , value ) ,
326+ decls : 1 ,
327+ vars : 2 ,
328+ } ) ;
329+
330+ for ( const supportedValue of [
331+ 'inline' ,
332+ 1 ,
333+ bypassSanitizationTrustStyle ( 'block' ) ,
334+ null ,
335+ undefined ,
336+ ] ) {
337+ value = supportedValue ;
338+ t . update ( ) ;
339+ }
340+
341+ expect ( warnSpy ) . not . toHaveBeenCalled ( ) ;
342+ } ) ;
343+
200344 it ( 'should allow values even if a bypass operation is applied' , ( ) => {
201345 let backgroundImage : string | SafeValue = 'url("http://server")' ;
202346 const t = new ViewFixture ( {
@@ -244,6 +388,25 @@ describe('instructions', () => {
244388 fixture . update ( ) ;
245389 expect ( fixture . html ) . toEqual ( '<div style="background-color: red; height: 10px;"></div>' ) ;
246390 } ) ;
391+
392+ it ( 'should warn when a style map property is bound to a boolean' , ( ) => {
393+ const warnSpy = spyOn ( console , 'warn' ) ;
394+ const fixture = new ViewFixture ( {
395+ create : createDivWithStyle ,
396+ update : ( ) => ɵɵstyleMap ( { display : true } ) ,
397+ decls : 1 ,
398+ vars : 2 ,
399+ consts : attrs ,
400+ } ) ;
401+
402+ fixture . update ( ) ;
403+
404+ expect ( warnSpy ) . toHaveBeenCalledOnceWith (
405+ 'NG0318: `[style.display]` was bound to an invalid value. ' +
406+ 'Expected a string, number, SafeValue, null, or undefined, but received `boolean` (`true`). ' +
407+ 'Find more at https://next.angular.dev/errors/NG0318' ,
408+ ) ;
409+ } ) ;
247410 } ) ;
248411
249412 describe ( 'elementClass' , ( ) => {
0 commit comments