Igor Minar 0b1e34de40 fix(common): cleanup the StylingDiffer and related code (#34307)
Since I was learning the codebase and had a hard time understanding what was going on I've done a
bunch of changes in one commit that under normal circumstances should have been split into several
commits. Because this code is likely going to be overwritten with Misko's changes I'm not going to
spend the time with trying to split this up.

Overall I've done the following:
- I processed review feedback from #34307
- I did a bunch of renaming to make the code easier to understand
- I refactored some internal functions that were either inefficient or hard to read
- I also updated lots of type signatures to correct them and to remove many casts in the code

PR Close #34307
2020-01-17 14:07:27 -05:00

205 lines
7.3 KiB
TypeScript

/**
* @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 {CommonModule} from '@angular/common';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
{
describe('NgStyle', () => {
let fixture: ComponentFixture<TestComponent>;
function getComponent(): TestComponent { return fixture.componentInstance; }
function expectNativeEl(fixture: ComponentFixture<any>): any {
return expect(fixture.debugElement.children[0].nativeElement);
}
afterEach(() => { fixture = null !; });
beforeEach(() => {
TestBed.configureTestingModule({declarations: [TestComponent], imports: [CommonModule]});
});
it('should add styles specified in an object literal', async(() => {
const template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
fixture = createTestComponent(template);
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
}));
it('should add and change styles specified in an object expression', async(() => {
const template = `<div [ngStyle]="expr"></div>`;
fixture = createTestComponent(template);
getComponent().expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
let expr = getComponent().expr;
expr['max-width'] = '30%';
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});
}));
it('should add and remove styles specified using style.unit notation', async(() => {
const template = `<div [ngStyle]="{'max-width.px': expr}"></div>`;
fixture = createTestComponent(template);
getComponent().expr = '40';
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
getComponent().expr = null;
fixture.detectChanges();
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
}));
// https://github.com/angular/angular/issues/21064
it('should add and remove styles which names are not dash-cased', async(() => {
fixture = createTestComponent(`<div [ngStyle]="{'color': expr}"></div>`);
getComponent().expr = 'green';
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'color': 'green'});
getComponent().expr = null;
fixture.detectChanges();
expectNativeEl(fixture).not.toHaveCssStyle('color');
}));
it('should update styles using style.unit notation when unit changes', async(() => {
const template = `<div [ngStyle]="expr"></div>`;
fixture = createTestComponent(template);
getComponent().expr = {'max-width.px': '40'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
getComponent().expr = {'max-width.em': '40'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40em'});
}));
// keyValueDiffer is sensitive to key order #9115
it('should change styles specified in an object expression', async(() => {
const template = `<div [ngStyle]="expr"></div>`;
fixture = createTestComponent(template);
getComponent().expr = {
// height, width order is important here
height: '10px',
width: '10px'
};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'height': '10px', 'width': '10px'});
getComponent().expr = {
// width, height order is important here
width: '5px',
height: '5px',
};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'height': '5px', 'width': '5px'});
}));
it('should remove styles when deleting a key in an object expression', async(() => {
const template = `<div [ngStyle]="expr"></div>`;
fixture = createTestComponent(template);
getComponent().expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
delete getComponent().expr['max-width'];
fixture.detectChanges();
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
}));
it('should co-operate with the style attribute', async(() => {
const template = `<div style="font-size: 12px" [ngStyle]="expr"></div>`;
fixture = createTestComponent(template);
getComponent().expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px', 'font-size': '12px'});
delete getComponent().expr['max-width'];
fixture.detectChanges();
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
}));
it('should co-operate with the style.[styleName]="expr" special-case in the compiler',
async(() => {
const template = `<div [style.font-size.px]="12" [ngStyle]="expr"></div>`;
fixture = createTestComponent(template);
getComponent().expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px', 'font-size': '12px'});
delete getComponent().expr['max-width'];
fixture.detectChanges();
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
}));
it('should not write to the native node unless the bound expression has changed', () => {
const template = `<div [ngStyle]="{'color': expr}"></div>`;
fixture = createTestComponent(template);
fixture.componentInstance.expr = 'red';
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'color': 'red'});
// Overwrite native styles so that we can check if ngStyle has performed DOM manupulation to
// update it.
fixture.debugElement.children[0].nativeElement.style.color = 'blue';
fixture.detectChanges();
// Assert that the style hasn't been updated
expectNativeEl(fixture).toHaveCssStyle({'color': 'blue'});
fixture.componentInstance.expr = 'yellow';
fixture.detectChanges();
// Assert that the style has changed now that the model has changed
expectNativeEl(fixture).toHaveCssStyle({'color': 'yellow'});
});
it('should correctly update style with units (.px) when the model is set to number', () => {
const template = `<div [ngStyle]="{'width.px': expr}"></div>`;
fixture = createTestComponent(template);
fixture.componentInstance.expr = 400;
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'width': '400px'});
});
});
}
@Component({selector: 'test-cmp', template: ''})
class TestComponent {
expr: any;
}
function createTestComponent(template: string): ComponentFixture<TestComponent> {
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
.createComponent(TestComponent);
}