66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { CommonModule } from '@angular/common' ;
9+ import { CommonModule , DOCUMENT } from '@angular/common' ;
1010import { IMAGE_LOADER , ImageLoader , ImageLoaderConfig , NgOptimizedImage } from '@angular/common/src/directives/ng_optimized_image' ;
1111import { Component } from '@angular/core' ;
1212import { ComponentFixture , TestBed } from '@angular/core/testing' ;
@@ -25,6 +25,57 @@ describe('Image directive', () => {
2525 expect ( img . src . endsWith ( '/path/img.png' ) ) . toBeTrue ( ) ;
2626 } ) ;
2727
28+ it ( 'should set `loading` and `fetchpriority` attributes before `src`' , ( ) => {
29+ // Only run this test in a browser since the Node-based DOM mocks don't
30+ // allow to override `HTMLImageElement.prototype.setAttribute` easily.
31+ if ( ! isBrowser ) return ;
32+
33+ setupTestingModule ( ) ;
34+
35+ const template = '<img rawSrc="path/img.png" width="150" height="50" priority>' ;
36+ TestBed . overrideComponent ( TestComponent , { set : { template : template } } ) ;
37+
38+ const _document = TestBed . inject ( DOCUMENT ) ;
39+ const _window = _document . defaultView ! ;
40+ const setAttributeSpy =
41+ spyOn ( _window . HTMLImageElement . prototype , 'setAttribute' ) . and . callThrough ( ) ;
42+
43+ const fixture = TestBed . createComponent ( TestComponent ) ;
44+ fixture . detectChanges ( ) ;
45+
46+ const nativeElement = fixture . nativeElement as HTMLElement ;
47+
48+ const img = nativeElement . querySelector ( 'img' ) ! ;
49+ expect ( img . getAttribute ( 'loading' ) ) . toBe ( 'eager' ) ;
50+
51+ let _imgInstance = null ;
52+ let _loadingAttrId = - 1 ;
53+ let _fetchpriorityAttrId = - 1 ;
54+ let _srcAttrId = - 1 ;
55+ const count = setAttributeSpy . calls . count ( ) ;
56+ for ( let i = 0 ; i < count ; i ++ ) {
57+ if ( ! _imgInstance ) {
58+ _imgInstance = setAttributeSpy . calls . thisFor ( i ) ;
59+ } else if ( _imgInstance !== setAttributeSpy . calls . thisFor ( i ) ) {
60+ // Verify that the <img> instance is the same during the test.
61+ fail ( 'Unexpected instance of a second <img> instance present in a test.' ) ;
62+ }
63+
64+ // Note: spy.calls.argsFor(i) returns args as an array: ['src', 'eager']
65+ const attrName = setAttributeSpy . calls . argsFor ( i ) [ 0 ] ;
66+ if ( attrName == 'loading' ) _loadingAttrId = i ;
67+ if ( attrName == 'fetchpriority' ) _fetchpriorityAttrId = i ;
68+ if ( attrName == 'src' ) _srcAttrId = i ;
69+ }
70+ // Verify that both `loading` and `fetchpriority` are set *before* `src`:
71+ expect ( _loadingAttrId ) . toBeGreaterThan ( - 1 ) ; // was actually set
72+ expect ( _loadingAttrId ) . toBeLessThan ( _srcAttrId ) ; // was set after `src`
73+
74+ expect ( _fetchpriorityAttrId ) . toBeGreaterThan ( - 1 ) ; // was actually set
75+ expect ( _fetchpriorityAttrId ) . toBeLessThan ( _srcAttrId ) ; // was set after `src`
76+ } ) ;
77+
78+
2879 it ( 'should use an image loader provided via `IMAGE_LOADER` token' , ( ) => {
2980 const imageLoader = ( config : ImageLoaderConfig ) => `${ config . src } ?w=${ config . width } ` ;
3081 setupTestingModule ( { imageLoader} ) ;
@@ -54,6 +105,56 @@ describe('Image directive', () => {
54105 'the `rawSrc` to compute the final image URL and set the `src` itself.' ) ;
55106 } ) ;
56107 } ) ;
108+
109+ describe ( 'lazy loading' , ( ) => {
110+ it ( 'should eagerly load priority images' , ( ) => {
111+ setupTestingModule ( ) ;
112+
113+ const template = '<img rawSrc="path/img.png" width="150" height="50" priority>' ;
114+ const fixture = createTestComponent ( template ) ;
115+ fixture . detectChanges ( ) ;
116+
117+ const nativeElement = fixture . nativeElement as HTMLElement ;
118+ const img = nativeElement . querySelector ( 'img' ) ! ;
119+ expect ( img . getAttribute ( 'loading' ) ) . toBe ( 'eager' ) ;
120+ } ) ;
121+ it ( 'should lazily load non-priority images' , ( ) => {
122+ setupTestingModule ( ) ;
123+
124+ const template = '<img rawSrc="path/img.png" width="150" height="50">' ;
125+ const fixture = createTestComponent ( template ) ;
126+ fixture . detectChanges ( ) ;
127+
128+ const nativeElement = fixture . nativeElement as HTMLElement ;
129+ const img = nativeElement . querySelector ( 'img' ) ! ;
130+ expect ( img . getAttribute ( 'loading' ) ) . toBe ( 'lazy' ) ;
131+ } ) ;
132+ } ) ;
133+
134+ describe ( 'fetch priority' , ( ) => {
135+ it ( 'should be "high" for priority images' , ( ) => {
136+ setupTestingModule ( ) ;
137+
138+ const template = '<img rawSrc="path/img.png" width="150" height="50" priority>' ;
139+ const fixture = createTestComponent ( template ) ;
140+ fixture . detectChanges ( ) ;
141+
142+ const nativeElement = fixture . nativeElement as HTMLElement ;
143+ const img = nativeElement . querySelector ( 'img' ) ! ;
144+ expect ( img . getAttribute ( 'fetchpriority' ) ) . toBe ( 'high' ) ;
145+ } ) ;
146+ it ( 'should be "auto" for non-priority images' , ( ) => {
147+ setupTestingModule ( ) ;
148+
149+ const template = '<img rawSrc="path/img.png" width="150" height="50">' ;
150+ const fixture = createTestComponent ( template ) ;
151+ fixture . detectChanges ( ) ;
152+
153+ const nativeElement = fixture . nativeElement as HTMLElement ;
154+ const img = nativeElement . querySelector ( 'img' ) ! ;
155+ expect ( img . getAttribute ( 'fetchpriority' ) ) . toBe ( 'auto' ) ;
156+ } ) ;
157+ } ) ;
57158} ) ;
58159
59160// Helpers
0 commit comments