From 45cfd2e90a9af66a584b858c6aecba2e8308599d Mon Sep 17 00:00:00 2001 From: Thomas Burleson Date: Fri, 18 Aug 2017 14:53:15 -0500 Subject: [PATCH] feat(api): add responsive API for img elements (#382) * feat(api, img-src): add ImgSrcDirective for responsive API for img elements * add responsive API to img.src: src.md, src.lt-lg, src.gt-xs, etc. * repackage API classes to easily distinguish flexbox APIs and extended responsive APIs Closes #366, Fixes #376. * chore(release): fix package.json version --- LICENSE | 2 +- scripts/release/package.json | 6 +- .../api => api/core}/base-adapter.spec.ts | 0 .../{flexbox/api => api/core}/base-adapter.ts | 2 +- src/lib/{flexbox/api => api/core}/base.ts | 12 +- .../core}/responsive-activation.spec.ts | 0 .../core}/responsive-activation.ts | 14 +- .../{flexbox/api => api/ext}/class.spec.ts | 0 src/lib/{flexbox/api => api/ext}/class.ts | 4 +- src/lib/{flexbox/api => api/ext}/hide.spec.ts | 72 +++--- src/lib/api/ext/img-src.spec.ts | 216 ++++++++++++++++++ src/lib/api/ext/img-src.ts | 130 +++++++++++ src/lib/{flexbox/api => api/ext}/show-hide.ts | 4 +- src/lib/{flexbox/api => api/ext}/show.spec.ts | 56 ++--- .../{flexbox/api => api/ext}/style.spec.ts | 34 +-- src/lib/{flexbox/api => api/ext}/style.ts | 4 +- .../api => api/flexbox}/flex-align.ts | 2 +- .../{flexbox/api => api/flexbox}/flex-fill.ts | 2 +- .../api => api/flexbox}/flex-offset.spec.ts | 16 +- .../api => api/flexbox}/flex-offset.ts | 2 +- .../api => api/flexbox}/flex-order.ts | 2 +- .../{flexbox/api => api/flexbox}/flex.spec.ts | 148 ++++++------ src/lib/{flexbox/api => api/flexbox}/flex.ts | 2 +- .../api => api/flexbox}/layout-align.spec.ts | 70 +++--- .../api => api/flexbox}/layout-align.ts | 2 +- .../api => api/flexbox}/layout-gap.spec.ts | 66 +++--- .../api => api/flexbox}/layout-gap.ts | 4 +- .../api => api/flexbox}/layout-wrap.ts | 4 +- .../api => api/flexbox}/layout.spec.ts | 54 ++--- .../{flexbox/api => api/flexbox}/layout.ts | 2 +- src/lib/api/index.ts | 27 +++ src/lib/flexbox/index.ts | 27 --- src/lib/module.ts | 33 +-- src/lib/package.json | 3 +- src/lib/public_api.ts | 6 +- src/lib/utils/style-utils.spec.ts | 8 +- src/lib/utils/testing/custom-matchers.ts | 123 ++++++---- src/lib/utils/testing/dom-tools.ts | 23 +- 38 files changed, 805 insertions(+), 377 deletions(-) rename src/lib/{flexbox/api => api/core}/base-adapter.spec.ts (100%) rename src/lib/{flexbox/api => api/core}/base-adapter.ts (97%) rename src/lib/{flexbox/api => api/core}/base.ts (95%) rename src/lib/{flexbox/responsive => api/core}/responsive-activation.spec.ts (100%) rename src/lib/{flexbox/responsive => api/core}/responsive-activation.ts (92%) rename src/lib/{flexbox/api => api/ext}/class.spec.ts (100%) rename src/lib/{flexbox/api => api/ext}/class.ts (98%) rename src/lib/{flexbox/api => api/ext}/hide.spec.ts (73%) create mode 100644 src/lib/api/ext/img-src.spec.ts create mode 100644 src/lib/api/ext/img-src.ts rename src/lib/{flexbox/api => api/ext}/show-hide.ts (98%) rename src/lib/{flexbox/api => api/ext}/show.spec.ts (74%) rename src/lib/{flexbox/api => api/ext}/style.spec.ts (77%) rename src/lib/{flexbox/api => api/ext}/style.ts (98%) rename src/lib/{flexbox/api => api/flexbox}/flex-align.ts (98%) rename src/lib/{flexbox/api => api/flexbox}/flex-fill.ts (95%) rename src/lib/{flexbox/api => api/flexbox}/flex-offset.spec.ts (91%) rename src/lib/{flexbox/api => api/flexbox}/flex-offset.ts (99%) rename src/lib/{flexbox/api => api/flexbox}/flex-order.ts (98%) rename src/lib/{flexbox/api => api/flexbox}/flex.spec.ts (75%) rename src/lib/{flexbox/api => api/flexbox}/flex.ts (99%) rename src/lib/{flexbox/api => api/flexbox}/layout-align.spec.ts (89%) rename src/lib/{flexbox/api => api/flexbox}/layout-align.ts (99%) rename src/lib/{flexbox/api => api/flexbox}/layout-gap.spec.ts (74%) rename src/lib/{flexbox/api => api/flexbox}/layout-gap.ts (98%) rename src/lib/{flexbox/api => api/flexbox}/layout-wrap.ts (98%) rename src/lib/{flexbox/api => api/flexbox}/layout.spec.ts (85%) rename src/lib/{flexbox/api => api/flexbox}/layout.ts (99%) create mode 100644 src/lib/api/index.ts delete mode 100644 src/lib/flexbox/index.ts diff --git a/LICENSE b/LICENSE index 47bfda24a..3e151c029 100644 --- a/LICENSE +++ b/LICENSE @@ -10,7 +10,7 @@ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/scripts/release/package.json b/scripts/release/package.json index fe497a277..0780acd54 100644 --- a/scripts/release/package.json +++ b/scripts/release/package.json @@ -1,6 +1,6 @@ { "name": "@angular/flex-layout", - "version": "2.0.0-beta.8-3611003", + "version": "0.0.0-PLACEHOLDER", "description": "Angular Flex-Layout", "main": "./bundles/flex-layout.umd.js", "module": "./@angular/flex-layout.es5.js", @@ -24,8 +24,8 @@ }, "homepage": "https://github.com/angular/flex-layout#readme", "peerDependencies": { - "@angular/core": "^4.0.0", - "@angular/common": "^4.0.0" + "@angular/core": "^4.3.0", + "@angular/common": "^4.3.0" }, "dependencies": { "tslib": "^1.7.1" diff --git a/src/lib/flexbox/api/base-adapter.spec.ts b/src/lib/api/core/base-adapter.spec.ts similarity index 100% rename from src/lib/flexbox/api/base-adapter.spec.ts rename to src/lib/api/core/base-adapter.spec.ts diff --git a/src/lib/flexbox/api/base-adapter.ts b/src/lib/api/core/base-adapter.ts similarity index 97% rename from src/lib/flexbox/api/base-adapter.ts rename to src/lib/api/core/base-adapter.ts index ee48afc8b..3735cac74 100644 --- a/src/lib/flexbox/api/base-adapter.ts +++ b/src/lib/api/core/base-adapter.ts @@ -8,7 +8,7 @@ import {ElementRef, Renderer2} from '@angular/core'; import {BaseFxDirective} from './base'; -import {ResponsiveActivation} from './../responsive/responsive-activation'; +import {ResponsiveActivation} from './responsive-activation'; import {MediaQuerySubscriber} from '../../media-query/media-change'; import {MediaMonitor} from '../../media-query/media-monitor'; diff --git a/src/lib/flexbox/api/base.ts b/src/lib/api/core/base.ts similarity index 95% rename from src/lib/flexbox/api/base.ts rename to src/lib/api/core/base.ts index bc2916365..487656d4b 100644 --- a/src/lib/flexbox/api/base.ts +++ b/src/lib/api/core/base.ts @@ -19,7 +19,7 @@ import { applyStyleToElements } from '../../utils/style-utils'; -import {ResponsiveActivation, KeyOptions} from '../responsive/responsive-activation'; +import {ResponsiveActivation, KeyOptions} from '../core/responsive-activation'; import {MediaMonitor} from '../../media-query/media-monitor'; import {MediaQuerySubscriber} from '../../media-query/media-change'; @@ -78,6 +78,10 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges { return this._elementRef.nativeElement.parentNode; } + protected get nativeElement(): any { + return this._elementRef.nativeElement; + } + /** * Access the current value (if any) of the @Input property. */ @@ -130,7 +134,7 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges { * and optional restore it when the mediaQueries deactivate */ protected _getDisplayStyle(source?: HTMLElement): string { - let element: HTMLElement = source || this._elementRef.nativeElement; + let element: HTMLElement = source || this.nativeElement; return lookupStyle(element, 'display'); } @@ -161,7 +165,7 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges { protected _applyStyleToElement(style: StyleDefinition, value?: string | number, nativeElement?: any) { - let element = nativeElement || this._elementRef.nativeElement; + let element = nativeElement || this.nativeElement; applyStyleToElement(this._renderer, element, style, value); } @@ -209,7 +213,7 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges { * Special accessor to query for all child 'element' nodes regardless of type, class, etc. */ protected get childrenNodes() { - const obj = this._elementRef.nativeElement.children; + const obj = this.nativeElement.children; const buffer = []; // iterate backwards ensuring that length is an UInt32 diff --git a/src/lib/flexbox/responsive/responsive-activation.spec.ts b/src/lib/api/core/responsive-activation.spec.ts similarity index 100% rename from src/lib/flexbox/responsive/responsive-activation.spec.ts rename to src/lib/api/core/responsive-activation.spec.ts diff --git a/src/lib/flexbox/responsive/responsive-activation.ts b/src/lib/api/core/responsive-activation.ts similarity index 92% rename from src/lib/flexbox/responsive/responsive-activation.ts rename to src/lib/api/core/responsive-activation.ts index ffcf791be..5eeb31de4 100644 --- a/src/lib/flexbox/responsive/responsive-activation.ts +++ b/src/lib/api/core/responsive-activation.ts @@ -43,6 +43,7 @@ export class KeyOptions { export class ResponsiveActivation { private _subscribers: SubscriptionList = []; private _activatedInputKey: string; + private _registryMap: BreakPointX[]; /** * Constructor @@ -50,9 +51,20 @@ export class ResponsiveActivation { constructor(private _options: KeyOptions, private _mediaMonitor: MediaMonitor, private _onMediaChanges: MediaQuerySubscriber) { + this._registryMap = this._buildRegistryMap(); this._subscribers = this._configureChangeObservers(); } + /** + * Get a readonly sorted list of the breakpoints corresponding to the directive properties + * defined in the HTML markup: the sorting is done from largest to smallest. The order is + * important when several media queries are 'registered' and from which, the browser uses the + * first matching media query. + */ + get registryFromLargest(): BreakPointX[] { + return [...this._registryMap].reverse(); + } + /** * Accessor to the DI'ed directive property * Each directive instance has a reference to the MediaMonitor which is @@ -107,7 +119,7 @@ export class ResponsiveActivation { private _configureChangeObservers(): SubscriptionList { let subscriptions = []; - this._buildRegistryMap().forEach((bp: BreakPointX) => { + this._registryMap.forEach((bp: BreakPointX) => { if (this._keyInUse(bp.key)) { // Inject directive default property key name: to let onMediaChange() calls // know which property is being triggered... diff --git a/src/lib/flexbox/api/class.spec.ts b/src/lib/api/ext/class.spec.ts similarity index 100% rename from src/lib/flexbox/api/class.spec.ts rename to src/lib/api/ext/class.spec.ts diff --git a/src/lib/flexbox/api/class.ts b/src/lib/api/ext/class.ts similarity index 98% rename from src/lib/flexbox/api/class.ts rename to src/lib/api/ext/class.ts index 578bcda15..3c2efa928 100644 --- a/src/lib/flexbox/api/class.ts +++ b/src/lib/api/ext/class.ts @@ -22,8 +22,8 @@ import { } from '@angular/core'; import {NgClass} from '@angular/common'; -import {BaseFxDirective} from './base'; -import {BaseFxDirectiveAdapter} from './base-adapter'; +import {BaseFxDirective} from '../core/base'; +import {BaseFxDirectiveAdapter} from '../core/base-adapter'; import {MediaChange} from '../../media-query/media-change'; import {MediaMonitor} from '../../media-query/media-monitor'; diff --git a/src/lib/flexbox/api/hide.spec.ts b/src/lib/api/ext/hide.spec.ts similarity index 73% rename from src/lib/flexbox/api/hide.spec.ts rename to src/lib/api/ext/hide.spec.ts index f330767de..15d747678 100644 --- a/src/lib/flexbox/api/hide.spec.ts +++ b/src/lib/api/ext/hide.spec.ts @@ -69,38 +69,38 @@ describe('hide directive', () => { it('should initial with component not visible as default', () => { createTestComponent(`
`); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); }); it('should initial with component visible when set to `false`', () => { createTestComponent(`
`); - expectNativeEl(fixture).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'block'}); }); it('should initial with component visible when set to `0`', () => { createTestComponent(`
`); - expectNativeEl(fixture, {isVisible: 0}).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture, {isVisible: 0}).toHaveStyle({'display': 'block'}); }); it('should update styles with binding changes', () => { createTestComponent(`
`); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); fixture.componentInstance.toggleMenu(); - expectNativeEl(fixture).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'block'}); fixture.componentInstance.toggleMenu(); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); }); it('should use "block" display style when not explicitly defined', () => { createTestComponent(``); - expectNativeEl(fixture, {isHidden: true}).toHaveCssStyle({'display': 'none'}); - expectNativeEl(fixture, {isHidden: false}).toHaveCssStyle({'display': 'inline-block'}); + expectNativeEl(fixture, {isHidden: true}).toHaveStyle({'display': 'none'}); + expectNativeEl(fixture, {isHidden: false}).toHaveStyle({'display': 'inline-block'}); }); it('should use "flex" display style when the element also has an fxLayout', () => { createTestComponent(`
`); - expectNativeEl(fixture, {isHidden: true}).toHaveCssStyle({'display': 'none'}); - expectNativeEl(fixture, {isHidden: false}).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture, {isHidden: true}).toHaveStyle({'display': 'none'}); + expectNativeEl(fixture, {isHidden: false}).toHaveStyle({'display': 'block'}); }); @@ -111,73 +111,73 @@ describe('hide directive', () => { it('should show on `xs` viewports only when the default is included', () => { createTestComponent(`
`); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); matchMedia.activate('xs'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'block'}); matchMedia.activate('md'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); }); it('should preserve display and update only on activated mediaQuery', () => { createTestComponent(`
`); - expectNativeEl(fixture).toHaveCssStyle({'display': 'inline-block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'inline-block'}); // should hide with this activation matchMedia.activate('xs'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); // should reset to original display style matchMedia.activate('md'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'inline-block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'inline-block'}); }); it('should restore original display when disabled', () => { createTestComponent(`
`); - expectNativeEl(fixture).toHaveCssStyle({'display': 'inline-block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'inline-block'}); // should hide with this activation matchMedia.activate('xs'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); // should reset to original display style fixture.componentInstance.isHidden = false; - expectNativeEl(fixture).toHaveCssStyle({'display': 'inline-block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'inline-block'}); }); it('should restore original display when the mediaQuery deactivates', () => { let originalDisplay = {'display': 'table'}; createTestComponent(`
`); - expectNativeEl(fixture).toHaveCssStyle(originalDisplay); + expectNativeEl(fixture).toHaveStyle(originalDisplay); // should hide with this activation matchMedia.activate('xs'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); // should reset to original display style matchMedia.activate('md'); - expectNativeEl(fixture).toHaveCssStyle(originalDisplay); + expectNativeEl(fixture).toHaveStyle(originalDisplay); }); it('should support use of the `media` observable in templates ', () => { createTestComponent(`
`); - expectNativeEl(fixture).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'block'}); matchMedia.activate('xs'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); matchMedia.activate('lg'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'block'}); }); it('should support use of the `media` observable in adaptive templates ', () => { createTestComponent(`
`); - expectNativeEl(fixture).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'block'}); matchMedia.activate('xs'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'block'}); matchMedia.activate('md'); - expectNativeEl(fixture).toHaveCssStyle({'display': 'block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'block'}); }); it('should hide when used with fxLayout and the ".md" breakpoint activates', () => { @@ -198,8 +198,8 @@ describe('hide directive', () => { `; let expectActivation = makeExpectWithActivation(createTestComponent(template), '.hideOnMd'); - expectActivation().toHaveCssStyle({'display': 'block'}); - expectActivation('md').toHaveCssStyle({'display': 'none'}); + expectActivation().toHaveStyle({'display': 'block'}); + expectActivation('md').toHaveStyle({'display': 'none'}); }); it('should restore proper display mode when not hiding', () => { @@ -210,9 +210,9 @@ describe('hide directive', () => { `; let expectActivation = makeExpectWithActivation(createTestComponent(template), '.hideOnXs'); - expectActivation().toHaveCssStyle({'display': 'inline'}); - expectActivation('xs').toHaveCssStyle({'display': 'none'}); - expectActivation('md').toHaveCssStyle({'display': 'inline'}); + expectActivation().toHaveStyle({'display': 'inline'}); + expectActivation('xs').toHaveStyle({'display': 'none'}); + expectActivation('md').toHaveStyle({'display': 'inline'}); }); }); @@ -222,13 +222,13 @@ describe('hide directive', () => { This content to be shown ONLY when gt-sm `); - expectNativeEl(fixture).toHaveCssStyle({'display': 'inline-block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'inline-block'}); matchMedia.activate('md', true); - expectNativeEl(fixture).toHaveCssStyle({'display': 'none'}); + expectNativeEl(fixture).toHaveStyle({'display': 'none'}); matchMedia.activate('xs', true); - expectNativeEl(fixture).toHaveCssStyle({'display': 'inline-block'}); + expectNativeEl(fixture).toHaveStyle({'display': 'inline-block'}); }); }); diff --git a/src/lib/api/ext/img-src.spec.ts b/src/lib/api/ext/img-src.spec.ts new file mode 100644 index 000000000..2338ec1b0 --- /dev/null +++ b/src/lib/api/ext/img-src.spec.ts @@ -0,0 +1,216 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {Component} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {ComponentFixture, TestBed, inject} from '@angular/core/testing'; + +import {DEFAULT_BREAKPOINTS_PROVIDER} from '../../media-query/breakpoints/break-points-provider'; +import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-registry'; +import {MockMatchMedia} from '../../media-query/mock/mock-match-media'; +import {MatchMedia} from '../../media-query/match-media'; +import {FlexLayoutModule} from '../../module'; + +import {customMatchers} from '../../utils/testing/custom-matchers'; +import {makeCreateTestComponent, queryFor} from '../../utils/testing/helpers'; +import {expect} from '../../utils/testing/custom-matchers'; + +const SRC_URLS = { + 'xs': [ + 'https://dummyimage.com/300x200/c7751e/fff.png', + 'https://dummyimage.com/300x200/c7751e/000.png' + ], + 'gt-xs': [ + 'https://dummyimage.com/400x250/c7c224/fff.png', + 'https://dummyimage.com/400x250/c7c224/000.png' + ], + 'md': [ + 'https://dummyimage.com/500x300/76c720/fff.png', + 'https://dummyimage.com/500x300/76c720/000.png' + ], + 'lt-lg': [ + 'https://dummyimage.com/600x350/25c794/fff.png', + 'https://dummyimage.com/600x350/25c794/000.png' + ], + 'lg': [ + 'https://dummyimage.com/700x400/258cc7/fff.png', + 'https://dummyimage.com/700x400/258cc7/000.png' + ], + 'lt-xl': [ + 'https://dummyimage.com/800x500/b925c7/ffffff.png', + 'https://dummyimage.com/800x500/b925c7/000.png' + ] +}; +const DEFAULT_SRC = 'https://dummyimage.com/300x300/c72538/ffffff.png'; + +describe('img-src directive', () => { + let fixture: ComponentFixture; + let matchMedia: MockMatchMedia; + let breakpoints: BreakPointRegistry; + + let componentWithTemplate = (template: string) => { + fixture = makeCreateTestComponent(() => TestSrcComponent)(template); + + inject([MatchMedia, BreakPointRegistry], + (_matchMedia: MockMatchMedia, _breakpoints: BreakPointRegistry) => { + matchMedia = _matchMedia; + breakpoints = _breakpoints; + })(); + }; + + beforeEach(() => { + jasmine.addMatchers(customMatchers); + + // Configure testbed to prepare services + TestBed.configureTestingModule({ + imports: [CommonModule, FlexLayoutModule], + declarations: [TestSrcComponent], + providers: [ + BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER, + {provide: MatchMedia, useClass: MockMatchMedia} + ] + }); + }); + + describe('with static api', () => { + it('should preserve the static src attribute', () => { + componentWithTemplate(` + + `); + const img = queryFor(fixture, 'img')[0].nativeElement; + + fixture.detectChanges(); + expect(img).toHaveAttributes({ + src: 'https://dummyimage.com/300x300/c72538/ffffff.png' + }); + }); + + it('should work with empty src attributes', () => { + componentWithTemplate(` + + `); + const img = queryFor(fixture, 'img')[0].nativeElement; + + fixture.detectChanges(); + expect(img).toHaveAttributes({ + src: '' + }); + }); + + it('should work standard input bindings', () => { + componentWithTemplate(` + + `); + const img = queryFor(fixture, 'img')[0].nativeElement; + + fixture.detectChanges(); + expect(img).toHaveAttributes({ + src: 'https://dummyimage.com/300x300/c72538/ffffff.png' + }); + }); + + it('should work when `src` value is not defined', () => { + componentWithTemplate(` + + `); + + const img = queryFor(fixture, 'img')[0].nativeElement; + fixture.detectChanges(); + expect(img).toHaveAttributes({ + src: '' + }); + }); + + it('should only work with "" elements.', () => { + componentWithTemplate(` +