fix(common): rename rawSrc -> ngSrc in NgOptimizedImage directive (#47362)

As an ongoing effort to stabilize the NgOptimizedImage API before existing the Developer Preview, this commit renames the `rawSrc` attribute used for the NgOptimizedImage selector matching to `ngSrc`. The `rawSrcset` is also renamed to `ngSrcset` for consistency.

The motivation for this change is to align the attribute name better with other built-in directives, such as `ngFor`, `ngIf`, `ngClass`, `ngStyle`, etc.

Note: this is technically a breaking change, but since the NgOptimizedImage directive is in the Developer Preview mode, we land the change in a patch branch.

PR Close #47362
This commit is contained in:
Andrew Kushnir
2022-09-06 12:51:20 -07:00
committed by Dylan Hunn
parent 710d1da4ed
commit ca7bf65933
17 changed files with 312 additions and 272 deletions
@@ -7,8 +7,8 @@
*/
// Assembles directive details string, useful for error messages.
export function imgDirectiveDetails(rawSrc: string, includeRawSrc = true) {
const rawSrcInfo =
includeRawSrc ? `(activated on an <img> element with the \`rawSrc="${rawSrc}"\`) ` : '';
return `The NgOptimizedImage directive ${rawSrcInfo}has detected that`;
export function imgDirectiveDetails(ngSrc: string, includeNgSrc = true) {
const ngSrcInfo =
includeNgSrc ? `(activated on an <img> element with the \`ngSrc="${ngSrc}"\`) ` : '';
return `The NgOptimizedImage directive ${ngSrcInfo}has detected that`;
}
@@ -118,10 +118,10 @@ function throwUnexpectedAbsoluteUrlError(path: string, url: string): never {
throw new RuntimeError(
RuntimeErrorCode.INVALID_LOADER_ARGUMENTS,
ngDevMode &&
`Image loader has detected a \`<img>\` tag with an invalid \`rawSrc\` attribute: ${
`Image loader has detected a \`<img>\` tag with an invalid \`ngSrc\` attribute: ${
url}. ` +
`This image loader expects \`rawSrc\` to be a relative URL - ` +
`This image loader expects \`ngSrc\` to be a relative URL - ` +
`however the provided value is an absolute URL. ` +
`To fix this, provide \`rawSrc\` as a path relative to the base URL ` +
`To fix this, provide \`ngSrc\` as a path relative to the base URL ` +
`configured for this loader (\`${path}\`).`);
}
@@ -27,7 +27,7 @@ import {getUrl} from './url';
*/
@Injectable({providedIn: 'root'})
export class LCPImageObserver implements OnDestroy {
// Map of full image URLs -> original `rawSrc` values.
// Map of full image URLs -> original `ngSrc` values.
private images = new Map<string, string>();
// Keep track of images for which `console.warn` was produced.
private alreadyWarned = new Set<string>();
@@ -65,8 +65,8 @@ export class LCPImageObserver implements OnDestroy {
// Exclude `data:` and `blob:` URLs, since they are not supported by the directive.
if (imgSrc.startsWith('data:') || imgSrc.startsWith('blob:')) return;
const imgRawSrc = this.images.get(imgSrc);
if (imgRawSrc && !this.alreadyWarned.has(imgSrc)) {
const imgNgSrc = this.images.get(imgSrc);
if (imgNgSrc && !this.alreadyWarned.has(imgSrc)) {
this.alreadyWarned.add(imgSrc);
logMissingPriorityWarning(imgSrc);
}
@@ -75,9 +75,9 @@ export class LCPImageObserver implements OnDestroy {
return observer;
}
registerImage(rewrittenSrc: string, rawSrc: string) {
registerImage(rewrittenSrc: string, originalNgSrc: string) {
if (!this.observer) return;
this.images.set(getUrl(rewrittenSrc, this.window!).href, rawSrc);
this.images.set(getUrl(rewrittenSrc, this.window!).href, originalNgSrc);
}
unregisterImage(rewrittenSrc: string) {
@@ -93,8 +93,8 @@ export class LCPImageObserver implements OnDestroy {
}
}
function logMissingPriorityWarning(rawSrc: string) {
const directiveDetails = imgDirectiveDetails(rawSrc);
function logMissingPriorityWarning(ngSrc: string) {
const directiveDetails = imgDirectiveDetails(ngSrc);
console.warn(formatRuntimeError(
RuntimeErrorCode.LCP_IMG_MISSING_PRIORITY,
`${directiveDetails} this image is the Largest Contentful Paint (LCP) ` +
@@ -83,8 +83,8 @@ const OVERSIZED_IMAGE_TOLERANCE = 1000;
* Follow the steps below to enable and use the directive:
* 1. Import it into the necessary NgModule or a standalone Component.
* 2. Optionally provide an `ImageLoader` if you use an image hosting service.
* 3. Update the necessary `<img>` tags in templates and replace `src` attributes with `rawSrc`.
* Using a `rawSrc` allows the directive to control when the `src` gets set, which triggers an image
* 3. Update the necessary `<img>` tags in templates and replace `src` attributes with `ngSrc`.
* Using a `ngSrc` allows the directive to control when the `src` gets set, which triggers an image
* download.
*
* Step 1: import the `NgOptimizedImage` directive.
@@ -110,7 +110,7 @@ const OVERSIZED_IMAGE_TOLERANCE = 1000;
*
* To use the **default loader**: no additional code changes are necessary. The URL returned by the
* generic loader will always match the value of "src". In other words, this loader applies no
* transformations to the resource URL and the value of the `rawSrc` attribute will be used as is.
* transformations to the resource URL and the value of the `ngSrc` attribute will be used as is.
*
* To use an existing loader for a **third-party image service**: add the provider factory for your
* chosen service to the `providers` array. In the example below, the Imgix loader is used:
@@ -150,10 +150,10 @@ const OVERSIZED_IMAGE_TOLERANCE = 1000;
* ],
* ```
*
* Step 3: update `<img>` tags in templates to use `rawSrc` instead of `src`.
* Step 3: update `<img>` tags in templates to use `ngSrc` instead of `src`.
*
* ```
* <img rawSrc="logo.png" width="200" height="100">
* <img ngSrc="logo.png" width="200" height="100">
* ```
*
* @publicApi
@@ -161,7 +161,7 @@ const OVERSIZED_IMAGE_TOLERANCE = 1000;
*/
@Directive({
standalone: true,
selector: 'img[rawSrc]',
selector: 'img[ngSrc],img[rawSrc]',
})
export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
private imageLoader = inject(IMAGE_LOADER);
@@ -180,25 +180,48 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
*/
private _renderedSrc: string|null = null;
/**
* Previously, the `rawSrc` attribute was used to activate the directive.
* The attribute was renamed to `ngSrc` and this input just produces an error,
* suggesting to switch to `ngSrc` instead.
*
* This error should be removed in v15.
*
* @nodoc
* @deprecated Use `ngSrc` instead.
*/
@Input()
set rawSrc(value: string) {
if (ngDevMode) {
throw new RuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(value, false)} the \`rawSrc\` attribute was used ` +
`to activate the directive. Newer version of the directive uses the \`ngSrc\` ` +
`attribute instead. Please replace \`rawSrc\` with \`ngSrc\` and ` +
`\`rawSrcset\` with \`ngSrcset\` attributes in the template to ` +
`enable image optimizations.`);
}
}
/**
* Name of the source image.
* Image name will be processed by the image loader and the final URL will be applied as the `src`
* property of the image.
*/
@Input() rawSrc!: string;
@Input() ngSrc!: string;
/**
* A comma separated list of width or density descriptors.
* The image name will be taken from `rawSrc` and combined with the list of width or density
* The image name will be taken from `ngSrc` and combined with the list of width or density
* descriptors to generate the final `srcset` property of the image.
*
* Example:
* ```
* <img rawSrc="hello.jpg" rawSrcset="100w, 200w" /> =>
* <img ngSrc="hello.jpg" ngSrcset="100w, 200w" /> =>
* <img src="path/hello.jpg" srcset="path/hello.jpg?w=100 100w, path/hello.jpg?w=200 200w" />
* ```
*/
@Input() rawSrcset!: string;
@Input() ngSrcset!: string;
/**
* The intrinsic width of the image in pixels.
@@ -249,7 +272,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
/**
* Value of the `src` attribute if set on the host `<img>` element.
* This input is exclusively read to assert that `src` is not set in conflict
* with `rawSrc` and that images don't start to load until a lazy loading strategy is set.
* with `ngSrc` and that images don't start to load until a lazy loading strategy is set.
* @internal
*/
@Input() src?: string;
@@ -257,15 +280,15 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
/**
* Value of the `srcset` attribute if set on the host `<img>` element.
* This input is exclusively read to assert that `srcset` is not set in conflict
* with `rawSrcset` and that images don't start to load until a lazy loading strategy is set.
* with `ngSrcset` and that images don't start to load until a lazy loading strategy is set.
* @internal
*/
@Input() srcset?: string;
ngOnInit() {
if (ngDevMode) {
assertNonEmptyInput(this, 'rawSrc', this.rawSrc);
assertValidRawSrcset(this, this.rawSrcset);
assertNonEmptyInput(this, 'ngSrc', this.ngSrc);
assertValidNgSrcset(this, this.ngSrcset);
assertNoConflictingSrc(this);
assertNoConflictingSrcset(this);
assertNotBase64Image(this);
@@ -275,7 +298,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
assertNoImageDistortion(this, this.imgElement, this.renderer);
if (this.priority) {
const checker = this.injector.get(PreconnectLinkChecker);
checker.assertPreconnect(this.getRewrittenSrc(), this.rawSrc);
checker.assertPreconnect(this.getRewrittenSrc(), this.ngSrc);
} else {
// Monitor whether an image is an LCP element only in case
// the `priority` attribute is missing. Otherwise, an image
@@ -283,7 +306,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
if (this.lcpObserver !== null) {
const ngZone = this.injector.get(NgZone);
ngZone.runOutsideAngular(() => {
this.lcpObserver!.registerImage(this.getRewrittenSrc(), this.rawSrc);
this.lcpObserver!.registerImage(this.getRewrittenSrc(), this.ngSrc);
});
}
}
@@ -302,7 +325,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
// The `src` and `srcset` attributes should be set last since other attributes
// could affect the image's loading behavior.
this.setHostAttribute('src', this.getRewrittenSrc());
if (this.rawSrcset) {
if (this.ngSrcset) {
this.setHostAttribute('srcset', this.getRewrittenSrcset());
}
}
@@ -310,7 +333,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
ngOnChanges(changes: SimpleChanges) {
if (ngDevMode) {
assertNoPostInitInputChange(
this, changes, ['rawSrc', 'rawSrcset', 'width', 'height', 'priority']);
this, changes, ['ngSrc', 'ngSrcset', 'width', 'height', 'priority']);
}
}
@@ -330,7 +353,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
// because if the developer uses rendered width instead of intrinsic width in the HTML width
// attribute, the image requested may be too small for 2x+ screens.
if (!this._renderedSrc) {
const imgConfig = {src: this.rawSrc};
const imgConfig = {src: this.ngSrc};
// Cache calculated image src to reuse it later in the code.
this._renderedSrc = this.imageLoader(imgConfig);
}
@@ -338,11 +361,11 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
}
private getRewrittenSrcset(): string {
const widthSrcSet = VALID_WIDTH_DESCRIPTOR_SRCSET.test(this.rawSrcset);
const finalSrcs = this.rawSrcset.split(',').filter(src => src !== '').map(srcStr => {
const widthSrcSet = VALID_WIDTH_DESCRIPTOR_SRCSET.test(this.ngSrcset);
const finalSrcs = this.ngSrcset.split(',').filter(src => src !== '').map(srcStr => {
srcStr = srcStr.trim();
const width = widthSrcSet ? parseFloat(srcStr) : parseFloat(srcStr) * this.width!;
return `${this.imageLoader({src: this.rawSrc, width})} ${srcStr}`;
return `${this.imageLoader({src: this.ngSrc, width})} ${srcStr}`;
});
return finalSrcs.join(', ');
}
@@ -386,9 +409,9 @@ function assertNoConflictingSrc(dir: NgOptimizedImage) {
if (dir.src) {
throw new RuntimeError(
RuntimeErrorCode.UNEXPECTED_SRC_ATTR,
`${imgDirectiveDetails(dir.rawSrc)} both \`src\` and \`rawSrc\` have been set. ` +
`${imgDirectiveDetails(dir.ngSrc)} both \`src\` and \`ngSrc\` have been set. ` +
`Supplying both of these attributes breaks lazy loading. ` +
`The NgOptimizedImage directive sets \`src\` itself based on the value of \`rawSrc\`. ` +
`The NgOptimizedImage directive sets \`src\` itself based on the value of \`ngSrc\`. ` +
`To fix this, please remove the \`src\` attribute.`);
}
}
@@ -400,43 +423,43 @@ function assertNoConflictingSrcset(dir: NgOptimizedImage) {
if (dir.srcset) {
throw new RuntimeError(
RuntimeErrorCode.UNEXPECTED_SRCSET_ATTR,
`${imgDirectiveDetails(dir.rawSrc)} both \`srcset\` and \`rawSrcset\` have been set. ` +
`${imgDirectiveDetails(dir.ngSrc)} both \`srcset\` and \`ngSrcset\` have been set. ` +
`Supplying both of these attributes breaks lazy loading. ` +
`The NgOptimizedImage directive sets \`srcset\` itself based on the value of ` +
`\`rawSrcset\`. To fix this, please remove the \`srcset\` attribute.`);
`\`ngSrcset\`. To fix this, please remove the \`srcset\` attribute.`);
}
}
/**
* Verifies that the `rawSrc` is not a Base64-encoded image.
* Verifies that the `ngSrc` is not a Base64-encoded image.
*/
function assertNotBase64Image(dir: NgOptimizedImage) {
let rawSrc = dir.rawSrc.trim();
if (rawSrc.startsWith('data:')) {
if (rawSrc.length > BASE64_IMG_MAX_LENGTH_IN_ERROR) {
rawSrc = rawSrc.substring(0, BASE64_IMG_MAX_LENGTH_IN_ERROR) + '...';
let ngSrc = dir.ngSrc.trim();
if (ngSrc.startsWith('data:')) {
if (ngSrc.length > BASE64_IMG_MAX_LENGTH_IN_ERROR) {
ngSrc = ngSrc.substring(0, BASE64_IMG_MAX_LENGTH_IN_ERROR) + '...';
}
throw new RuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(dir.rawSrc, false)} \`rawSrc\` is a Base64-encoded string ` +
`(${rawSrc}). NgOptimizedImage does not support Base64-encoded strings. ` +
`${imgDirectiveDetails(dir.ngSrc, false)} \`ngSrc\` is a Base64-encoded string ` +
`(${ngSrc}). NgOptimizedImage does not support Base64-encoded strings. ` +
`To fix this, disable the NgOptimizedImage directive for this element ` +
`by removing \`rawSrc\` and using a standard \`src\` attribute instead.`);
`by removing \`ngSrc\` and using a standard \`src\` attribute instead.`);
}
}
/**
* Verifies that the `rawSrc` is not a Blob URL.
* Verifies that the `ngSrc` is not a Blob URL.
*/
function assertNotBlobUrl(dir: NgOptimizedImage) {
const rawSrc = dir.rawSrc.trim();
if (rawSrc.startsWith('blob:')) {
const ngSrc = dir.ngSrc.trim();
if (ngSrc.startsWith('blob:')) {
throw new RuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(dir.rawSrc)} \`rawSrc\` was set to a blob URL (${rawSrc}). ` +
`${imgDirectiveDetails(dir.ngSrc)} \`ngSrc\` was set to a blob URL (${ngSrc}). ` +
`Blob URLs are not supported by the NgOptimizedImage directive. ` +
`To fix this, disable the NgOptimizedImage directive for this element ` +
`by removing \`rawSrc\` and using a regular \`src\` attribute instead.`);
`by removing \`ngSrc\` and using a regular \`src\` attribute instead.`);
}
}
@@ -449,17 +472,17 @@ function assertNonEmptyInput(dir: NgOptimizedImage, name: string, value: unknown
if (!isString || isEmptyString) {
throw new RuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(dir.rawSrc)} \`${name}\` has an invalid value ` +
`${imgDirectiveDetails(dir.ngSrc)} \`${name}\` has an invalid value ` +
`(\`${value}\`). To fix this, change the value to a non-empty string.`);
}
}
/**
* Verifies that the `rawSrcset` is in a valid format, e.g. "100w, 200w" or "1x, 2x".
* Verifies that the `ngSrcset` is in a valid format, e.g. "100w, 200w" or "1x, 2x".
*/
export function assertValidRawSrcset(dir: NgOptimizedImage, value: unknown) {
export function assertValidNgSrcset(dir: NgOptimizedImage, value: unknown) {
if (value == null) return;
assertNonEmptyInput(dir, 'rawSrcset', value);
assertNonEmptyInput(dir, 'ngSrcset', value);
const stringVal = value as string;
const isValidWidthDescriptor = VALID_WIDTH_DESCRIPTOR_SRCSET.test(stringVal);
const isValidDensityDescriptor = VALID_DENSITY_DESCRIPTOR_SRCSET.test(stringVal);
@@ -472,8 +495,8 @@ export function assertValidRawSrcset(dir: NgOptimizedImage, value: unknown) {
if (!isValidSrcset) {
throw new RuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(dir.rawSrc)} \`rawSrcset\` has an invalid value (\`${value}\`). ` +
`To fix this, supply \`rawSrcset\` using a comma-separated list of one or more width ` +
`${imgDirectiveDetails(dir.ngSrc)} \`ngSrcset\` has an invalid value (\`${value}\`). ` +
`To fix this, supply \`ngSrcset\` using a comma-separated list of one or more width ` +
`descriptors (e.g. "100w, 200w") or density descriptors (e.g. "1x, 2x").`);
}
}
@@ -486,7 +509,7 @@ function assertUnderDensityCap(dir: NgOptimizedImage, value: string) {
RuntimeErrorCode.INVALID_INPUT,
`${
imgDirectiveDetails(
dir.rawSrc)} the \`rawSrcset\` contains an unsupported image density:` +
dir.ngSrc)} the \`ngSrcset\` contains an unsupported image density:` +
`\`${value}\`. NgOptimizedImage generally recommends a max image density of ` +
`${RECOMMENDED_SRCSET_DENSITY_CAP}x but supports image densities up to ` +
`${ABSOLUTE_SRCSET_DENSITY_CAP}x. The human eye cannot distinguish between image densities ` +
@@ -503,7 +526,7 @@ function assertUnderDensityCap(dir: NgOptimizedImage, value: string) {
function postInitInputChangeError(dir: NgOptimizedImage, inputName: string): {} {
return new RuntimeError(
RuntimeErrorCode.UNEXPECTED_INPUT_CHANGE,
`${imgDirectiveDetails(dir.rawSrc)} \`${inputName}\` was updated after initialization. ` +
`${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` was updated after initialization. ` +
`The NgOptimizedImage directive will not react to this input change. ` +
`To fix this, switch \`${inputName}\` a static value or wrap the image element ` +
`in an *ngIf that is gated on the necessary value.`);
@@ -517,12 +540,12 @@ function assertNoPostInitInputChange(
inputs.forEach(input => {
const isUpdated = changes.hasOwnProperty(input);
if (isUpdated && !changes[input].isFirstChange()) {
if (input === 'rawSrc') {
// When the `rawSrc` input changes, we detect that only in the
// `ngOnChanges` hook, thus the `rawSrc` is already set. We use
// `rawSrc` in the error message, so we use a previous value, but
if (input === 'ngSrc') {
// When the `ngSrc` input changes, we detect that only in the
// `ngOnChanges` hook, thus the `ngSrc` is already set. We use
// `ngSrc` in the error message, so we use a previous value, but
// not the updated one in it.
dir = {rawSrc: changes[input].previousValue} as NgOptimizedImage;
dir = {ngSrc: changes[input].previousValue} as NgOptimizedImage;
}
throw postInitInputChangeError(dir, input);
}
@@ -539,7 +562,7 @@ function assertGreaterThanZero(dir: NgOptimizedImage, inputValue: unknown, input
if (!validNumber && !validString) {
throw new RuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(dir.rawSrc)} \`${inputName}\` has an invalid value ` +
`${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` has an invalid value ` +
`(\`${inputValue}\`). To fix this, provide \`${inputName}\` ` +
`as a number greater than 0.`);
}
@@ -583,7 +606,7 @@ function assertNoImageDistortion(
if (inaccurateDimensions) {
console.warn(formatRuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(dir.rawSrc)} the aspect ratio of the image does not match ` +
`${imgDirectiveDetails(dir.ngSrc)} the aspect ratio of the image does not match ` +
`the aspect ratio indicated by the width and height attributes. ` +
`\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` +
`(aspect-ratio: ${intrinsicAspectRatio}). \nSupplied width and height attributes: ` +
@@ -592,7 +615,7 @@ function assertNoImageDistortion(
} else if (stylingDistortion) {
console.warn(formatRuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(dir.rawSrc)} the aspect ratio of the rendered image ` +
`${imgDirectiveDetails(dir.ngSrc)} the aspect ratio of the rendered image ` +
`does not match the image's intrinsic aspect ratio. ` +
`\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` +
`(aspect-ratio: ${intrinsicAspectRatio}). \nRendered image size: ` +
@@ -602,8 +625,8 @@ function assertNoImageDistortion(
`image styling. To fix this, adjust image styling. In most cases, ` +
`adding "height: auto" or "width: auto" to the image styling will fix ` +
`this issue.`));
} else if (!dir.rawSrcset && nonZeroRenderedDimensions) {
// If `rawSrcset` hasn't been set, sanity check the intrinsic size.
} else if (!dir.ngSrcset && nonZeroRenderedDimensions) {
// If `ngSrcset` hasn't been set, sanity check the intrinsic size.
const recommendedWidth = RECOMMENDED_SRCSET_DENSITY_CAP * renderedWidth;
const recommendedHeight = RECOMMENDED_SRCSET_DENSITY_CAP * renderedHeight;
const oversizedWidth = (intrinsicWidth - recommendedWidth) >= OVERSIZED_IMAGE_TOLERANCE;
@@ -611,7 +634,7 @@ function assertNoImageDistortion(
if (oversizedWidth || oversizedHeight) {
console.warn(formatRuntimeError(
RuntimeErrorCode.OVERSIZED_IMAGE,
`${imgDirectiveDetails(dir.rawSrc)} the intrinsic image is significantly ` +
`${imgDirectiveDetails(dir.ngSrc)} the intrinsic image is significantly ` +
`larger than necessary. ` +
`\nRendered image size: ${renderedWidth}w x ${renderedHeight}h. ` +
`\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h. ` +
@@ -619,7 +642,7 @@ function assertNoImageDistortion(
recommendedHeight}h. ` +
`\nNote: Recommended intrinsic image size is calculated assuming a maximum DPR of ` +
`${RECOMMENDED_SRCSET_DENSITY_CAP}. To improve loading time, resize the image ` +
`or consider using the "rawSrcset" and "sizes" attributes.`));
`or consider using the "ngSrcset" and "sizes" attributes.`));
}
}
});
@@ -635,7 +658,7 @@ function assertNonEmptyWidthAndHeight(dir: NgOptimizedImage) {
if (missingAttributes.length > 0) {
throw new RuntimeError(
RuntimeErrorCode.REQUIRED_INPUT_MISSING,
`${imgDirectiveDetails(dir.rawSrc)} these required attributes ` +
`${imgDirectiveDetails(dir.ngSrc)} these required attributes ` +
`are missing: ${missingAttributes.map(attr => `"${attr}"`).join(', ')}. ` +
`Including "width" and "height" attributes will prevent image-related layout shifts. ` +
`To fix this, include "width" and "height" attributes on the image tag.`);
@@ -650,7 +673,7 @@ function assertValidLoadingInput(dir: NgOptimizedImage) {
if (dir.loading && dir.priority) {
throw new RuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(dir.rawSrc)} the \`loading\` attribute ` +
`${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` +
`was used on an image that was marked "priority". ` +
`Setting \`loading\` on priority images is not allowed ` +
`because these images will always be eagerly loaded. ` +
@@ -660,7 +683,7 @@ function assertValidLoadingInput(dir: NgOptimizedImage) {
if (typeof dir.loading === 'string' && !validInputs.includes(dir.loading)) {
throw new RuntimeError(
RuntimeErrorCode.INVALID_INPUT,
`${imgDirectiveDetails(dir.rawSrc)} the \`loading\` attribute ` +
`${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` +
`has an invalid value (\`${dir.loading}\`). ` +
`To fix this, provide a valid value ("lazy", "eager", or "auto").`);
}
@@ -96,9 +96,9 @@ export class PreconnectLinkChecker {
* given src.
*
* @param rewrittenSrc src formatted with loader
* @param rawSrc rawSrc value
* @param originalNgSrc ngSrc value
*/
assertPreconnect(rewrittenSrc: string, rawSrc: string): void {
assertPreconnect(rewrittenSrc: string, originalNgSrc: string): void {
if (!this.window) return;
const imgUrl = getUrl(rewrittenSrc, this.window);
@@ -118,8 +118,8 @@ export class PreconnectLinkChecker {
if (!this.preconnectLinks.has(imgUrl.origin)) {
console.warn(formatRuntimeError(
RuntimeErrorCode.PRIORITY_IMG_MISSING_PRECONNECT_TAG,
`${imgDirectiveDetails(rawSrc)} there is no preconnect tag present for this image. ` +
`Preconnecting to the origin(s) that serve priority images ensures that these ` +
`${imgDirectiveDetails(originalNgSrc)} there is no preconnect tag present for this ` +
`image. Preconnecting to the origin(s) that serve priority images ensures that these ` +
`images are delivered as soon as possible. To fix this, please add the following ` +
`element into the <head> of the document:\n` +
` <link rel="preconnect" href="${imgUrl.origin}">`));
@@ -14,7 +14,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
import {withHead} from '@angular/private/testing';
import {IMAGE_LOADER, ImageLoader, ImageLoaderConfig} from '../../src/directives/ng_optimized_image/image_loaders/image_loader';
import {ABSOLUTE_SRCSET_DENSITY_CAP, assertValidRawSrcset, NgOptimizedImage, RECOMMENDED_SRCSET_DENSITY_CAP} from '../../src/directives/ng_optimized_image/ng_optimized_image';
import {ABSOLUTE_SRCSET_DENSITY_CAP, assertValidNgSrcset, NgOptimizedImage, RECOMMENDED_SRCSET_DENSITY_CAP} from '../../src/directives/ng_optimized_image/ng_optimized_image';
import {PRECONNECT_CHECK_BLOCKLIST} from '../../src/directives/ng_optimized_image/preconnect_link_checker';
describe('Image directive', () => {
@@ -25,7 +25,7 @@ describe('Image directive', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" width="150" height="50" priority>';
const template = '<img ngSrc="path/img.png" width="150" height="50" priority>';
TestBed.overrideComponent(TestComponent, {set: {template: template}});
const _document = TestBed.inject(DOCUMENT);
@@ -71,7 +71,7 @@ describe('Image directive', () => {
it('should always reflect the width/height attributes if bound', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" [width]="width" [height]="height">';
const template = '<img ngSrc="path/img.png" [width]="width" [height]="height">';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -82,7 +82,43 @@ describe('Image directive', () => {
});
describe('setup error handling', () => {
it('should throw if both `src` and `rawSrc` are present', () => {
it('should throw if both `src` and `ngSrc` are present', () => {
setupTestingModule();
const template = '<img ngSrc="path/img.png" src="path/img2.png" width="100" height="50">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02950: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `ngSrc="path/img.png"`) has detected that both ' +
'`src` and `ngSrc` have been set. Supplying both of these attributes ' +
'breaks lazy loading. The NgOptimizedImage directive sets `src` ' +
'itself based on the value of `ngSrc`. To fix this, please remove ' +
'the `src` attribute.');
});
it('should throw if both `ngSrc` and `srcset` is present', () => {
setupTestingModule();
const template =
'<img ngSrc="img-100.png" srcset="img-100.png 100w, img-200.png 200w" width="100" height="50">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02951: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `ngSrc="img-100.png"`) has detected that both ' +
'`srcset` and `ngSrcset` have been set. Supplying both of these ' +
'attributes breaks lazy loading. ' +
'The NgOptimizedImage directive sets `srcset` itself based ' +
'on the value of `ngSrcset`. To fix this, please remove the `srcset` ' +
'attribute.');
});
it('should throw if an old `rawSrc` is present', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" src="path/img2.png" width="100" height="50">';
@@ -91,50 +127,29 @@ describe('Image directive', () => {
fixture.detectChanges();
})
.toThrowError(
'NG02950: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc="path/img.png"`) has detected that both ' +
'`src` and `rawSrc` have been set. Supplying both of these attributes ' +
'breaks lazy loading. The NgOptimizedImage directive sets `src` ' +
'itself based on the value of `rawSrc`. To fix this, please remove ' +
'the `src` attribute.');
'NG02952: The NgOptimizedImage directive has detected that the `rawSrc` ' +
'attribute was used to activate the directive. Newer version of the directive uses ' +
'the `ngSrc` attribute instead. Please replace `rawSrc` with `ngSrc` and ' +
'`rawSrcset` with `ngSrcset` attributes in the template to enable image optimizations.');
});
it('should throw if both `rawSrc` and `srcset` is present', () => {
it('should throw if `ngSrc` contains a Base64-encoded image (that starts with `data:`)', () => {
setupTestingModule();
const template =
'<img rawSrc="img-100.png" srcset="img-100.png 100w, img-200.png 200w" width="100" height="50">';
expect(() => {
const template = '<img ngSrc="' + ANGULAR_LOGO_BASE64 + '" width="50" height="50">';
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02951: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc="img-100.png"`) has detected that both ' +
'`srcset` and `rawSrcset` have been set. Supplying both of these ' +
'attributes breaks lazy loading. ' +
'The NgOptimizedImage directive sets `srcset` itself based ' +
'on the value of `rawSrcset`. To fix this, please remove the `srcset` ' +
'attribute.');
});
it('should throw if `rawSrc` contains a Base64-encoded image (that starts with `data:`)', () => {
setupTestingModule();
expect(() => {
const template = '<img rawSrc="' + ANGULAR_LOGO_BASE64 + '" width="50" height="50">';
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02952: The NgOptimizedImage directive has detected that `rawSrc` ' +
'NG02952: The NgOptimizedImage directive has detected that `ngSrc` ' +
'is a Base64-encoded string (data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDov...). ' +
'NgOptimizedImage does not support Base64-encoded strings. ' +
'To fix this, disable the NgOptimizedImage directive for this element ' +
'by removing `rawSrc` and using a standard `src` attribute instead.');
'by removing `ngSrc` and using a standard `src` attribute instead.');
});
it('should throw if `rawSrc` contains a `blob:` URL', (done) => {
it('should throw if `ngSrc` contains a `blob:` URL', (done) => {
// Domino does not support canvas elements properly,
// so run this test only in a browser.
if (!isBrowser) {
@@ -151,9 +166,9 @@ describe('Image directive', () => {
// Note: use RegExp to partially match the error message, since the blob URL
// is created dynamically, so it might be different for each invocation.
const errorMessageRegExp =
/NG02952: The NgOptimizedImage directive (.*?) has detected that `rawSrc` was set to a blob URL \(blob:/;
/NG02952: The NgOptimizedImage directive (.*?) has detected that `ngSrc` was set to a blob URL \(blob:/;
expect(() => {
const template = '<img rawSrc="' + blobURL + '" width="50" height="50">';
const template = '<img ngSrc="' + blobURL + '" width="50" height="50">';
const fixture = createTestComponent(template);
fixture.detectChanges();
}).toThrowError(errorMessageRegExp);
@@ -164,14 +179,14 @@ describe('Image directive', () => {
it('should throw if `width` and `height` are not set', () => {
setupTestingModule();
const template = '<img rawSrc="img.png">';
const template = '<img ngSrc="img.png">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02954: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc="img.png"`) has detected that these ' +
'element with the `ngSrc="img.png"`) has detected that these ' +
'required attributes are missing: "width", "height". Including "width" and ' +
'"height" attributes will prevent image-related layout shifts. ' +
'To fix this, include "width" and "height" attributes on the image tag.');
@@ -180,14 +195,14 @@ describe('Image directive', () => {
it('should throw if `width` is not set', () => {
setupTestingModule();
const template = '<img rawSrc="img.png" height="10">';
const template = '<img ngSrc="img.png" height="10">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02954: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc="img.png"`) has detected that these ' +
'element with the `ngSrc="img.png"`) has detected that these ' +
'required attributes are missing: "width". Including "width" and ' +
'"height" attributes will prevent image-related layout shifts. ' +
'To fix this, include "width" and "height" attributes on the image tag.');
@@ -196,14 +211,14 @@ describe('Image directive', () => {
it('should throw if `width` is 0', () => {
setupTestingModule();
const template = '<img rawSrc="img.png" width="0" height="10">';
const template = '<img ngSrc="img.png" width="0" height="10">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc="img.png"`) has detected that `width` ' +
'element with the `ngSrc="img.png"`) has detected that `width` ' +
'has an invalid value (`0`). To fix this, provide `width` as ' +
'a number greater than 0.');
});
@@ -211,14 +226,14 @@ describe('Image directive', () => {
it('should throw if `width` has an invalid value', () => {
setupTestingModule();
const template = '<img rawSrc="img.png" width="10px" height="10">';
const template = '<img ngSrc="img.png" width="10px" height="10">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc="img.png"`) has detected that `width` ' +
'element with the `ngSrc="img.png"`) has detected that `width` ' +
'has an invalid value (`10px`). To fix this, provide `width` ' +
'as a number greater than 0.');
});
@@ -226,14 +241,14 @@ describe('Image directive', () => {
it('should throw if `height` is not set', () => {
setupTestingModule();
const template = '<img rawSrc="img.png" width="10">';
const template = '<img ngSrc="img.png" width="10">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02954: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc="img.png"`) has detected that these required ' +
'element with the `ngSrc="img.png"`) has detected that these required ' +
'attributes are missing: "height". Including "width" and "height" ' +
'attributes will prevent image-related layout shifts. ' +
'To fix this, include "width" and "height" attributes on the image tag.');
@@ -242,14 +257,14 @@ describe('Image directive', () => {
it('should throw if `height` is 0', () => {
setupTestingModule();
const template = '<img rawSrc="img.png" width="10" height="0">';
const template = '<img ngSrc="img.png" width="10" height="0">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc="img.png"`) has detected that `height` ' +
'element with the `ngSrc="img.png"`) has detected that `height` ' +
'has an invalid value (`0`). To fix this, provide `height` as a number ' +
'greater than 0.');
});
@@ -257,50 +272,50 @@ describe('Image directive', () => {
it('should throw if `height` has an invalid value', () => {
setupTestingModule();
const template = '<img rawSrc="img.png" width="10" height="10%">';
const template = '<img ngSrc="img.png" width="10" height="10%">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> element ' +
'with the `rawSrc="img.png"`) has detected that `height` has an invalid ' +
'with the `ngSrc="img.png"`) has detected that `height` has an invalid ' +
'value (`10%`). To fix this, provide `height` as a number greater than 0.');
});
it('should throw if `rawSrc` value is not provided', () => {
it('should throw if `ngSrc` value is not provided', () => {
setupTestingModule();
const template = '<img rawSrc>';
const template = '<img ngSrc>';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc=""`) has detected that `rawSrc` has an ' +
'element with the `ngSrc=""`) has detected that `ngSrc` has an ' +
'invalid value (``). ' +
'To fix this, change the value to a non-empty string.');
});
it('should throw if `rawSrc` value is set to an empty string', () => {
it('should throw if `ngSrc` value is set to an empty string', () => {
setupTestingModule();
const template = '<img rawSrc=" ">';
const template = '<img ngSrc=" ">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> element ' +
'with the `rawSrc=" "`) has detected that `rawSrc` has an invalid value ' +
'with the `ngSrc=" "`) has detected that `ngSrc` has an invalid value ' +
'(` `). To fix this, change the value to a non-empty string.');
});
describe('invalid `rawSrcset` values', () => {
const mockDirectiveInstance = {rawSrc: 'img.png'} as NgOptimizedImage;
describe('invalid `ngSrcset` values', () => {
const mockDirectiveInstance = {ngSrc: 'img.png'} as NgOptimizedImage;
it('should throw for empty rawSrcSet', () => {
it('should throw for empty ngSrcSet', () => {
const imageLoader = (config: ImageLoaderConfig) => {
const width = config.width ? `-${config.width}` : ``;
return window.location.origin + `/path/${config.src}${width}.png`;
@@ -308,7 +323,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img" rawSrcset="" width="100" height="50">
<img ngSrc="img" ngSrcset="" width="100" height="50">
`;
expect(() => {
const fixture = createTestComponent(template);
@@ -316,12 +331,12 @@ describe('Image directive', () => {
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> ' +
'element with the `rawSrc="img"`) has detected that `rawSrcset` ' +
'element with the `ngSrc="img"`) has detected that `ngSrcset` ' +
'has an invalid value (``). ' +
'To fix this, change the value to a non-empty string.');
});
it('should throw for invalid rawSrcSet', () => {
it('should throw for invalid ngSrcSet', () => {
const imageLoader = (config: ImageLoaderConfig) => {
const width = config.width ? `-${config.width}` : ``;
return window.location.origin + `/path/${config.src}${width}.png`;
@@ -329,7 +344,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img" rawSrcset="100q, 200q" width="100" height="50">
<img ngSrc="img" ngSrcset="100q, 200q" width="100" height="50">
`;
expect(() => {
const fixture = createTestComponent(template);
@@ -337,13 +352,13 @@ describe('Image directive', () => {
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> element ' +
'with the `rawSrc="img"`) has detected that `rawSrcset` has an invalid value ' +
'(`100q, 200q`). To fix this, supply `rawSrcset` using a comma-separated list ' +
'with the `ngSrc="img"`) has detected that `ngSrcset` has an invalid value ' +
'(`100q, 200q`). To fix this, supply `ngSrcset` using a comma-separated list ' +
'of one or more width descriptors (e.g. "100w, 200w") or density descriptors ' +
'(e.g. "1x, 2x").');
});
it('should throw if rawSrcset exceeds the density cap', () => {
it('should throw if ngSrcset exceeds the density cap', () => {
const imageLoader = (config: ImageLoaderConfig) => {
const width = config.width ? `-${config.width}` : ``;
return window.location.origin + `/path/${config.src}${width}.png`;
@@ -351,7 +366,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img" rawSrcset="1x, 2x, 3x, 4x, 5x" width="100" height="50">
<img ngSrc="img" ngSrcset="1x, 2x, 3x, 4x, 5x" width="100" height="50">
`;
expect(() => {
const fixture = createTestComponent(template);
@@ -360,8 +375,8 @@ describe('Image directive', () => {
.toThrowError(
`NG0${
RuntimeErrorCode
.INVALID_INPUT}: The NgOptimizedImage directive (activated on an <img> element with the \`rawSrc="img"\`) ` +
`has detected that the \`rawSrcset\` contains an unsupported image density:` +
.INVALID_INPUT}: The NgOptimizedImage directive (activated on an <img> element with the \`ngSrc="img"\`) ` +
`has detected that the \`ngSrcset\` contains an unsupported image density:` +
`\`1x, 2x, 3x, 4x, 5x\`. NgOptimizedImage generally recommends a max image density of ` +
`${RECOMMENDED_SRCSET_DENSITY_CAP}x but supports image densities up to ` +
`${ABSOLUTE_SRCSET_DENSITY_CAP}x. The human eye cannot distinguish between image densities ` +
@@ -372,7 +387,7 @@ describe('Image directive', () => {
});
it('should throw if rawSrcset exceeds the density cap with multiple digits', () => {
it('should throw if ngSrcset exceeds the density cap with multiple digits', () => {
const imageLoader = (config: ImageLoaderConfig) => {
const width = config.width ? `-${config.width}` : ``;
return window.location.origin + `/path/${config.src}${width}.png`;
@@ -380,7 +395,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img" rawSrcset="1x, 200x" width="100" height="50">
<img ngSrc="img" ngSrcset="1x, 200x" width="100" height="50">
`;
expect(() => {
const fixture = createTestComponent(template);
@@ -389,8 +404,8 @@ describe('Image directive', () => {
.toThrowError(
`NG0${
RuntimeErrorCode
.INVALID_INPUT}: The NgOptimizedImage directive (activated on an <img> element with the \`rawSrc="img"\`) ` +
`has detected that the \`rawSrcset\` contains an unsupported image density:` +
.INVALID_INPUT}: The NgOptimizedImage directive (activated on an <img> element with the \`ngSrc="img"\`) ` +
`has detected that the \`ngSrcset\` contains an unsupported image density:` +
`\`1x, 200x\`. NgOptimizedImage generally recommends a max image density of ` +
`${RECOMMENDED_SRCSET_DENSITY_CAP}x but supports image densities up to ` +
`${ABSOLUTE_SRCSET_DENSITY_CAP}x. The human eye cannot distinguish between image densities ` +
@@ -402,75 +417,75 @@ describe('Image directive', () => {
it('should throw if width srcset is missing a comma', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, '100w 200w');
assertValidNgSrcset(mockDirectiveInstance, '100w 200w');
}).toThrowError();
});
it('should throw if density srcset is missing a comma', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, '1x 2x');
assertValidNgSrcset(mockDirectiveInstance, '1x 2x');
}).toThrowError();
});
it('should throw if density srcset has too many digits', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, '100x, 2x');
assertValidNgSrcset(mockDirectiveInstance, '100x, 2x');
}).toThrowError();
});
it('should throw if width srcset includes a file name', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, 'a.png 100w, b.png 200w');
assertValidNgSrcset(mockDirectiveInstance, 'a.png 100w, b.png 200w');
}).toThrowError();
});
it('should throw if density srcset includes a file name', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, 'a.png 1x, b.png 2x');
assertValidNgSrcset(mockDirectiveInstance, 'a.png 1x, b.png 2x');
}).toThrowError();
});
it('should throw if srcset starts with a letter', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, 'a100w, 200w');
assertValidNgSrcset(mockDirectiveInstance, 'a100w, 200w');
}).toThrowError();
});
it('should throw if srcset starts with another non-digit', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, '--100w, 200w');
assertValidNgSrcset(mockDirectiveInstance, '--100w, 200w');
}).toThrowError();
});
it('should throw if first descriptor in srcset is junk', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, 'foo, 1x');
assertValidNgSrcset(mockDirectiveInstance, 'foo, 1x');
}).toThrowError();
});
it('should throw if later descriptors in srcset are junk', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, '100w, foo');
assertValidNgSrcset(mockDirectiveInstance, '100w, foo');
}).toThrowError();
});
it('should throw if srcset has a density descriptor after a width descriptor', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, '100w, 1x');
assertValidNgSrcset(mockDirectiveInstance, '100w, 1x');
}).toThrowError();
});
it('should throw if srcset has a width descriptor after a density descriptor', () => {
expect(() => {
assertValidRawSrcset(mockDirectiveInstance, '1x, 200w');
assertValidNgSrcset(mockDirectiveInstance, '1x, 200w');
}).toThrowError();
});
});
const inputs = [
['rawSrc', 'new-img.png'], //
['width', 10], //
['height', 20], //
['ngSrc', 'new-img.png'], //
['width', 10], //
['height', 20], //
['priority', true]
];
inputs.forEach(([inputName, value]) => {
@@ -478,7 +493,7 @@ describe('Image directive', () => {
setupTestingModule();
const template =
'<img [rawSrc]="rawSrc" [width]="width" [height]="height" [priority]="priority">';
'<img [ngSrc]="ngSrc" [width]="width" [height]="height" [priority]="priority">';
// Initial render
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -491,7 +506,7 @@ describe('Image directive', () => {
})
.toThrowError(
'NG02953: The NgOptimizedImage directive (activated on an <img> element ' +
`with the \`rawSrc="img.png"\`) has detected that \`${inputName}\` was updated ` +
`with the \`ngSrc="img.png"\`) has detected that \`${inputName}\` was updated ` +
'after initialization. The NgOptimizedImage directive will not react ' +
`to this input change. To fix this, switch \`${inputName}\` a static value or ` +
'wrap the image element in an *ngIf that is gated on the necessary value.');
@@ -503,7 +518,7 @@ describe('Image directive', () => {
it('should eagerly load priority images', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" width="150" height="50" priority>';
const template = '<img ngSrc="path/img.png" width="150" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -515,7 +530,7 @@ describe('Image directive', () => {
it('should lazily load non-priority images', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" width="150" height="50">';
const template = '<img ngSrc="path/img.png" width="150" height="50">';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -529,7 +544,7 @@ describe('Image directive', () => {
it('should override the default loading behavior for non-priority images', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" width="150" height="50" loading="eager">';
const template = '<img ngSrc="path/img.png" width="150" height="50" loading="eager">';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -542,14 +557,14 @@ describe('Image directive', () => {
setupTestingModule();
const template =
'<img rawSrc="path/img.png" width="150" height="50" loading="eager" priority>';
'<img ngSrc="path/img.png" width="150" height="50" loading="eager" priority>';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> element ' +
'with the `rawSrc="path/img.png"`) has detected that the `loading` attribute ' +
'with the `ngSrc="path/img.png"`) has detected that the `loading` attribute ' +
'was used on an image that was marked "priority". Setting `loading` on priority ' +
'images is not allowed because these images will always be eagerly loaded. ' +
'To fix this, remove the “loading” attribute from the priority image.');
@@ -558,7 +573,7 @@ describe('Image directive', () => {
it('should support setting loading priority to "auto"', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" width="150" height="50" loading="auto">';
const template = '<img ngSrc="path/img.png" width="150" height="50" loading="auto">';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -570,14 +585,14 @@ describe('Image directive', () => {
it('should throw for invalid loading inputs', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" width="150" height="150" loading="fast">';
const template = '<img ngSrc="path/img.png" width="150" height="150" loading="fast">';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
})
.toThrowError(
'NG02952: The NgOptimizedImage directive (activated on an <img> element ' +
'with the `rawSrc="path/img.png"`) has detected that the `loading` attribute ' +
'with the `ngSrc="path/img.png"`) has detected that the `loading` attribute ' +
'has an invalid value (`fast`). To fix this, provide a valid value ("lazy", ' +
'"eager", or "auto").');
});
@@ -587,7 +602,7 @@ describe('Image directive', () => {
it('should be "high" for priority images', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" width="150" height="50" priority>';
const template = '<img ngSrc="path/img.png" width="150" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -599,7 +614,7 @@ describe('Image directive', () => {
it('should be "auto" for non-priority images', () => {
setupTestingModule();
const template = '<img rawSrc="path/img.png" width="150" height="50">';
const template = '<img ngSrc="path/img.png" width="150" height="50">';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -621,7 +636,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -629,7 +644,7 @@ describe('Image directive', () => {
expect(consoleWarnSpy.calls.argsFor(0)[0])
.toBe(
'NG02956: The NgOptimizedImage directive (activated on an <img> element ' +
'with the `rawSrc="a.png"`) has detected that there is no preconnect tag ' +
'with the `ngSrc="a.png"`) has detected that there is no preconnect tag ' +
'present for this image. Preconnecting to the origin(s) that serve ' +
'priority images ensures that these images are delivered as soon as ' +
'possible. To fix this, please add the following element into the <head> ' +
@@ -642,7 +657,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50">';
const template = '<img ngSrc="a.png" width="100" height="50">';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -655,7 +670,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -663,7 +678,7 @@ describe('Image directive', () => {
expect(consoleWarnSpy.calls.argsFor(0)[0])
.toBe(
'NG02956: The NgOptimizedImage directive (activated on an <img> element ' +
'with the `rawSrc="a.png"`) has detected that there is no preconnect tag ' +
'with the `ngSrc="a.png"`) has detected that there is no preconnect tag ' +
'present for this image. Preconnecting to the origin(s) that serve priority ' +
'images ensures that these images are delivered as soon as possible. ' +
'To fix this, please add the following element into the <head> of the document:' +
@@ -677,7 +692,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -685,7 +700,7 @@ describe('Image directive', () => {
expect(consoleWarnSpy.calls.argsFor(0)[0])
.toBe(
'NG02956: The NgOptimizedImage directive (activated on an <img> element ' +
'with the `rawSrc="a.png"`) has detected that there is no preconnect tag ' +
'with the `ngSrc="a.png"`) has detected that there is no preconnect tag ' +
'present for this image. Preconnecting to the origin(s) that serve priority ' +
'images ensures that these images are delivered as soon as possible. ' +
'To fix this, please add the following element into the <head> of the document:' +
@@ -697,7 +712,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -715,7 +730,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -732,7 +747,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader, extraProviders: providers});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -747,7 +762,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader, extraProviders: providers});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -762,7 +777,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader, extraProviders: providers});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -777,7 +792,7 @@ describe('Image directive', () => {
setupTestingModule({imageLoader, extraProviders: providers});
const consoleWarnSpy = spyOn(console, 'warn');
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -792,7 +807,7 @@ describe('Image directive', () => {
];
setupTestingModule({imageLoader, extraProviders: providers});
const template = '<img rawSrc="a.png" width="100" height="50" priority>';
const template = '<img ngSrc="a.png" width="100" height="50" priority>';
expect(() => {
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -806,10 +821,10 @@ describe('Image directive', () => {
});
describe('loaders', () => {
it('should set `src` to match `rawSrc` if image loader is not provided', () => {
it('should set `src` to match `ngSrc` if image loader is not provided', () => {
setupTestingModule();
const template = `<img rawSrc="${IMG_BASE_URL}/img.png" width="100" height="50">`;
const template = `<img ngSrc="${IMG_BASE_URL}/img.png" width="100" height="50">`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -824,8 +839,8 @@ describe('Image directive', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img.png" width="150" height="50">
<img rawSrc="img-2.png" width="150" height="50">
<img ngSrc="img.png" width="150" height="50">
<img ngSrc="img-2.png" width="150" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -836,13 +851,13 @@ describe('Image directive', () => {
expect(imgs[1].src.trim()).toBe(`${IMG_BASE_URL}/img-2.png`);
});
it('should pass absolute URLs defined in the `rawSrc` to custom image loaders provided via the `IMAGE_LOADER` token',
it('should pass absolute URLs defined in the `ngSrc` to custom image loaders provided via the `IMAGE_LOADER` token',
() => {
const imageLoader = (config: ImageLoaderConfig) => `${config.src}?rewritten=true`;
setupTestingModule({imageLoader});
const template = `
<img rawSrc="${IMG_BASE_URL}/img.png" width="150" height="50">
<img ngSrc="${IMG_BASE_URL}/img.png" width="150" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -859,7 +874,7 @@ describe('Image directive', () => {
};
setupTestingModule({imageLoader});
const template = '<img rawSrc="img.png" width="150" height="50">';
const template = '<img ngSrc="img.png" width="150" height="50">';
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -868,7 +883,7 @@ describe('Image directive', () => {
expect(img.src).toBe(`${IMG_BASE_URL}/img.png`);
});
describe('`rawSrcset` values', () => {
describe('`ngSrcset` values', () => {
let imageLoader!: ImageLoader;
beforeEach(() => {
@@ -878,11 +893,11 @@ describe('Image directive', () => {
};
});
it('should NOT set `srcset` if no `rawSrcset` value', () => {
it('should NOT set `srcset` if no `ngSrcset` value', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img-100.png" width="100" height="50">
<img ngSrc="img-100.png" width="100" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -893,11 +908,11 @@ describe('Image directive', () => {
expect(img.srcset).toBe('');
});
it('should set the `srcset` using the `rawSrcset` value with width descriptors', () => {
it('should set the `srcset` using the `ngSrcset` value with width descriptors', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img.png" rawSrcset="100w, 200w" width="100" height="50">
<img ngSrc="img.png" ngSrcset="100w, 200w" width="100" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -909,11 +924,11 @@ describe('Image directive', () => {
.toBe(`${IMG_BASE_URL}/img.png?w=100 100w, ${IMG_BASE_URL}/img.png?w=200 200w`);
});
it('should set the `srcset` using the `rawSrcset` value with density descriptors', () => {
it('should set the `srcset` using the `ngSrcset` value with density descriptors', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img.png" rawSrcset="1x, 2x" width="100" height="50">
<img ngSrc="img.png" ngSrcset="1x, 2x" width="100" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -925,11 +940,11 @@ describe('Image directive', () => {
.toBe(`${IMG_BASE_URL}/img.png?w=100 1x, ${IMG_BASE_URL}/img.png?w=200 2x`);
});
it('should set the `srcset` if `rawSrcset` has only one src defined', () => {
it('should set the `srcset` if `ngSrcset` has only one src defined', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img.png" rawSrcset="100w" width="100" height="50">
<img ngSrc="img.png" ngSrcset="100w" width="100" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -940,11 +955,11 @@ describe('Image directive', () => {
expect(img.srcset.trim()).toBe(`${IMG_BASE_URL}/img.png?w=100 100w`);
});
it('should set the `srcset` if `rawSrcSet` has extra spaces', () => {
it('should set the `srcset` if `ngSrcSet` has extra spaces', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img.png" rawSrcset=" 100w, 200w " width="100" height="50">
<img ngSrc="img.png" ngSrcset=" 100w, 200w " width="100" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -956,11 +971,11 @@ describe('Image directive', () => {
.toBe(`${IMG_BASE_URL}/img.png?w=100 100w, ${IMG_BASE_URL}/img.png?w=200 200w`);
});
it('should set the `srcset` if `rawSrcSet` has a trailing comma', () => {
it('should set the `srcset` if `ngSrcSet` has a trailing comma', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img.png" rawSrcset="1x, 2x," width="100" height="50">
<img ngSrc="img.png" ngSrcset="1x, 2x," width="100" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -972,11 +987,11 @@ describe('Image directive', () => {
.toBe(`${IMG_BASE_URL}/img.png?w=100 1x, ${IMG_BASE_URL}/img.png?w=200 2x`);
});
it('should set the `srcset` if `rawSrcSet` has 3+ srcs', () => {
it('should set the `srcset` if `ngSrcSet` has 3+ srcs', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img.png" rawSrcset="100w, 200w, 300w" width="100" height="50">
<img ngSrc="img.png" ngSrcset="100w, 200w, 300w" width="100" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -991,11 +1006,11 @@ describe('Image directive', () => {
`${IMG_BASE_URL}/img.png?w=300 300w`);
});
it('should set the `srcset` if `rawSrcSet` has decimal density descriptors', () => {
it('should set the `srcset` if `ngSrcSet` has decimal density descriptors', () => {
setupTestingModule({imageLoader});
const template = `
<img rawSrc="img.png" rawSrcset="1.75x, 2.5x, 3x" width="100" height="50">
<img ngSrc="img.png" ngSrcset="1.75x, 2.5x, 3x" width="100" height="50">
`;
const fixture = createTestComponent(template);
fixture.detectChanges();
@@ -1032,7 +1047,7 @@ const ANGULAR_LOGO_BASE64 =
class TestComponent {
width = 100;
height = 50;
rawSrc = 'img.png';
ngSrc = 'img.png';
priority = false;
}
@@ -17,9 +17,9 @@ import {TestBed} from '@angular/core/testing';
const absoluteUrlError = (src: string, path: string) =>
`NG02959: Image loader has detected a \`<img>\` tag with an invalid ` +
`\`rawSrc\` attribute: ${src}. This image loader expects \`rawSrc\` ` +
`\`ngSrc\` attribute: ${src}. This image loader expects \`ngSrc\` ` +
`to be a relative URL - however the provided value is an absolute URL. ` +
`To fix this, provide \`rawSrc\` as a path relative to the base URL ` +
`To fix this, provide \`ngSrc\` as a path relative to the base URL ` +
`configured for this loader (\`${path}\`).`;
const invalidPathError = (path: string, formats: string) =>
@@ -13,7 +13,7 @@ import {Component} from '@angular/core';
selector: 'basic',
standalone: true,
imports: [NgOptimizedImage],
template: `<img rawSrc="/e2e/a.png" width="150" height="150" priority>`,
template: `<img ngSrc="/e2e/a.png" width="150" height="150" priority>`,
providers: [{
provide: IMAGE_LOADER,
useValue: () => 'https://angular.io/assets/images/logos/angular/angular.svg'
@@ -36,7 +36,7 @@ describe('NgOptimizedImage directive', () => {
expectErrorMessageInLogs(
logs,
'The NgOptimizedImage directive (activated on an \\u003Cimg> element ' +
'with the \`rawSrc=\\"/e2e/b.png\\"`) has detected that ' +
'with the \`ngSrc=\\"/e2e/b.png\\"`) has detected that ' +
'the aspect ratio of the image does not match the aspect ratio indicated by the width and height attributes. ' +
'\\nIntrinsic image size: 250w x 250h (aspect-ratio: 1). ' +
'\\nSupplied width and height attributes: 26w x 30h (aspect-ratio: 0.8666666666666667). ' +
@@ -45,7 +45,7 @@ describe('NgOptimizedImage directive', () => {
expectErrorMessageInLogs(
logs,
'The NgOptimizedImage directive (activated on an \\u003Cimg> element ' +
'with the \`rawSrc=\\"/e2e/b.png\\"`) has detected that ' +
'with the \`ngSrc=\\"/e2e/b.png\\"`) has detected that ' +
'the aspect ratio of the image does not match the aspect ratio indicated by the width and height attributes. ' +
'\\nIntrinsic image size: 250w x 250h (aspect-ratio: 1). ' +
'\\nSupplied width and height attributes: 24w x 240h (aspect-ratio: 0.1). ' +
@@ -55,7 +55,7 @@ describe('NgOptimizedImage directive', () => {
expectErrorMessageInLogs(
logs,
'The NgOptimizedImage directive (activated on an \\u003Cimg> element ' +
'with the \`rawSrc=\\"/e2e/b.png\\"`) has detected that ' +
'with the \`ngSrc=\\"/e2e/b.png\\"`) has detected that ' +
'the aspect ratio of the rendered image does not match the image\'s intrinsic aspect ratio. ' +
'\\nIntrinsic image size: 250w x 250h (aspect-ratio: 1). ' +
'\\nRendered image size: 250w x 30h (aspect-ratio: 8.333333333333334). ' +
@@ -66,7 +66,7 @@ describe('NgOptimizedImage directive', () => {
expectErrorMessageInLogs(
logs,
'The NgOptimizedImage directive (activated on an \\u003Cimg> element ' +
'with the \`rawSrc=\\"/e2e/b.png\\"`) has detected that ' +
'with the \`ngSrc=\\"/e2e/b.png\\"`) has detected that ' +
'the aspect ratio of the rendered image does not match the image\'s intrinsic aspect ratio. ' +
'\\nIntrinsic image size: 250w x 250h (aspect-ratio: 1). ' +
'\\nRendered image size: 30w x 250h (aspect-ratio: 0.12). ' +
@@ -80,7 +80,7 @@ describe('NgOptimizedImage directive', () => {
expectErrorMessageInLogs(
logs,
'The NgOptimizedImage directive (activated on an \\u003Cimg> element ' +
'with the \`rawSrc=\\"/e2e/b.png\\"`) has detected that ' +
'with the \`ngSrc=\\"/e2e/b.png\\"`) has detected that ' +
'the aspect ratio of the image does not match the aspect ratio indicated by the width and height attributes. ' +
'\\nIntrinsic image size: 250w x 250h (aspect-ratio: 1). ' +
'\\nSupplied width and height attributes: 150w x 250h (aspect-ratio: 0.6). ' +
@@ -16,33 +16,33 @@ import {Component} from '@angular/core';
template: `
<!-- All the images in this template should not throw -->
<!-- This image is here for the sake of making sure the "LCP image is priority" assertion is passed -->
<img rawSrc="/e2e/logo-500w.jpg" width="500" height="500" priority>
<img ngSrc="/e2e/logo-500w.jpg" width="500" height="500" priority>
<br>
<!-- width and height attributes exactly match the intrinsic size of image -->
<img rawSrc="/e2e/a.png" width="25" height="25">
<img ngSrc="/e2e/a.png" width="25" height="25">
<br>
<!-- supplied aspect ratio exactly matches intrinsic aspect ratio-->
<img rawSrc="/e2e/a.png" width="250" height="250">
<img rawSrc="/e2e/b.png" width="40" height="40">
<img rawSrc="/e2e/b.png" width="240" height="240">
<img ngSrc="/e2e/a.png" width="250" height="250">
<img ngSrc="/e2e/b.png" width="40" height="40">
<img ngSrc="/e2e/b.png" width="240" height="240">
<br>
<!-- supplied aspect ratio is similar to intrinsic aspect ratio -->
<!-- Aspect-ratio: 0.93333333333 -->
<img rawSrc="/e2e/b.png" width="28" height="30">
<img ngSrc="/e2e/b.png" width="28" height="30">
<!-- Aspect-ratio: 0.9 -->
<img rawSrc="/e2e/b.png" width="27" height="30">
<img ngSrc="/e2e/b.png" width="27" height="30">
<!-- Aspect-ratio: 1.09375 -->
<img rawSrc="/e2e/b.png" width="350" height="320">
<img ngSrc="/e2e/b.png" width="350" height="320">
<!-- Aspect-ratio: 1.0652173913 -->
<img rawSrc="/e2e/b.png" width="245" height="230">
<img ngSrc="/e2e/b.png" width="245" height="230">
<br>
<!-- Supplied aspect ratio is correct & image has 0x0 rendered size -->
<img rawSrc="/e2e/a.png" width="25" height="25" style="display: none">
<img ngSrc="/e2e/a.png" width="25" height="25" style="display: none">
<br>
<!-- styling is correct -->
<img rawSrc="/e2e/a.png" width="25" height="25" style="width: 100%; height: 100%">
<img rawSrc="/e2e/a.png" width="250" height="250" style="max-width: 100%; height: 100%">
<img rawSrc="/e2e/a.png" width="25" height="25" style="height: 25%; width: 25%;">
<img ngSrc="/e2e/a.png" width="25" height="25" style="width: 100%; height: 100%">
<img ngSrc="/e2e/a.png" width="250" height="250" style="max-width: 100%; height: 100%">
<img ngSrc="/e2e/a.png" width="25" height="25" style="height: 25%; width: 25%;">
<br>
`,
})
@@ -55,23 +55,23 @@ export class ImageDistortionPassingComponent {
template: `
<!-- With the exception of the priority image, all the images in this template should throw -->
<!-- This image is here for the sake of making sure the "LCP image is priority" assertion is passed -->
<img rawSrc="/e2e/logo-500w.jpg" width="500" height="500" priority>
<img ngSrc="/e2e/logo-500w.jpg" width="500" height="500" priority>
<br>
<!-- These images should throw -->
<!-- Supplied aspect ratio differs from intrinsic aspect ratio by > .1 -->
<!-- Aspect-ratio: 0.86666666666 -->
<img rawSrc="/e2e/b.png" width="26" height="30">
<img ngSrc="/e2e/b.png" width="26" height="30">
<!-- Aspect-ratio: 0.1 -->
<img rawSrc="/e2e/b.png" width="24" height="240">
<img ngSrc="/e2e/b.png" width="24" height="240">
<!-- Supplied aspect ratio is incorrect & image has 0x0 rendered size -->
<img rawSrc="/e2e/a.png" width="222" height="25" style="display: none">
<img ngSrc="/e2e/a.png" width="222" height="25" style="display: none">
<br>
<!-- Image styling is causing distortion -->
<div style="width: 300px; height: 300px">
<img rawSrc="/e2e/b.png" width="250" height="250" style="width: 10%">
<img rawSrc="/e2e/b.png" width="250" height="250" style="max-height: 10%">
<img ngSrc="/e2e/b.png" width="250" height="250" style="width: 10%">
<img ngSrc="/e2e/b.png" width="250" height="250" style="max-height: 10%">
<!-- Images dimensions are incorrect AND image styling is incorrect -->
<img rawSrc="/e2e/b.png" width="150" height="250" style="max-height: 10%">
<img ngSrc="/e2e/b.png" width="150" height="250" style="max-height: 10%">
</div>
`,
})
@@ -18,12 +18,12 @@ import {Component} from '@angular/core';
'b.png' should *not* be treated as an LCP element,
since there is a bigger one right below it
-->
<img rawSrc="/e2e/b.png" width="5" height="5">
<img ngSrc="/e2e/b.png" width="5" height="5">
<br>
<!-- 'a.png' should be treated as an LCP element -->
<img rawSrc="/e2e/a.png" width="2500" height="2500">
<img ngSrc="/e2e/a.png" width="2500" height="2500">
<br>
@@ -31,7 +31,7 @@ import {Component} from '@angular/core';
'b.png' should *not* be treated as an LCP element here
as well, since it's below the fold
-->
<img rawSrc="/e2e/b.png" width="10" height="10">
<img ngSrc="/e2e/b.png" width="10" height="10">
`,
})
export class LcpCheckComponent {
@@ -16,12 +16,12 @@ import {Component} from '@angular/core';
template: `
<!-- Image is rendered within threshold range-->
<div style="width: 500px; height: 500px">
<img rawSrc="/e2e/logo-500w.jpg" width="200" height="200" priority>
<img ngSrc="/e2e/logo-500w.jpg" width="200" height="200" priority>
</div>
<!-- Image is rendered too small but rawSrcset set-->
<!-- Image is rendered too small but ngSrcset set-->
<div style="width: 300px; height: 300px">
<img rawSrc="/e2e/logo-1500w.jpg" width="100" height="100" priority
rawSrcset="100w, 200w">
<img ngSrc="/e2e/logo-1500w.jpg" width="100" height="100" priority
ngSrcset="100w, 200w">
</div>
`,
})
@@ -36,7 +36,7 @@ export class OversizedImageComponentPassing {
template: `
<!-- Image is rendered too small -->
<div style="width: 300px; height: 300px">
<img rawSrc="/e2e/logo-1500w.jpg" width="100" height="100" priority>
<img ngSrc="/e2e/logo-1500w.jpg" width="100" height="100" priority>
</div>
`,
})
@@ -14,9 +14,9 @@ import {Component, Inject} from '@angular/core';
standalone: true,
imports: [NgOptimizedImage],
template: `
<img rawSrc="/e2e/a.png" width="50" height="50" priority>
<img rawSrc="/e2e/b.png" width="50" height="50" priority>
<img rawSrc="/e2e/c.png" width="50" height="50">
<img ngSrc="/e2e/a.png" width="50" height="50" priority>
<img ngSrc="/e2e/b.png" width="50" height="50" priority>
<img ngSrc="/e2e/c.png" width="50" height="50">
`,
providers: [{
provide: IMAGE_LOADER,
@@ -34,12 +34,12 @@ import {Component} from '@angular/core';
`],
template: `
<h1>
<img rawSrc="a.png" width="50" height="50" priority rawSrcset="1x, 2x">
<img ngSrc="a.png" width="50" height="50" priority ngSrcset="1x, 2x">
<span>Angular image app</span>
</h1>
<main>
<div class="spacer"></div>
<img rawSrc="hermes2.jpeg" rawSrcset="100w, 200w, 1000w, 2000w" width="1791" height="1008">
<img ngSrc="hermes2.jpeg" ngSrcset="100w, 200w, 1000w, 2000w" width="1791" height="1008">
</main>
`,
standalone: true,