Ben Lesh cb6ad971c3 test(ivy): add onlyInIvy perf counter expectations (#30339)
We have an issue where we would like to be able to test perf counter metrics in acceptance tests, but we are unable to do so, because it will break when those same tests are run with ViewEngine. This PR adds a testing utility to `onlyInIvy` that allows for testing of performance counters, and even gives readable errors for what value on `ngDevMode` is incorrect. Has typings for decent autocompletion as well.

PR Close #30339
2019-05-09 11:22:00 -07:00

164 lines
5.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 {Component, Directive, ElementRef} from '@angular/core';
import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode';
import {TestBed} from '@angular/core/testing';
import {DomSanitizer, SafeStyle} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {onlyInIvy} from '@angular/private/testing';
describe('styling', () => {
beforeEach(ngDevModeResetPerfCounters);
it('should render inline style and class attribute values on the element before a directive is instantiated',
() => {
@Component({
template: `
<div directive-expecting-styling style="width:200px" class="abc xyz"></div>
`
})
class Cmp {
}
@Directive({selector: '[directive-expecting-styling]'})
class DirectiveExpectingStyling {
constructor(elm: ElementRef) {
const native = elm.nativeElement;
native.setAttribute('data-captured-width', native.style.width);
native.setAttribute('data-captured-classes', native.className);
}
}
TestBed.configureTestingModule({declarations: [Cmp, DirectiveExpectingStyling]});
const fixture = TestBed.createComponent(Cmp);
fixture.detectChanges();
const element = fixture.nativeElement.querySelector('div');
expect(element.style.width).toEqual('200px');
expect(element.getAttribute('data-captured-width')).toEqual('200px');
expect(element.className.trim()).toEqual('abc xyz');
expect(element.getAttribute('data-captured-classes')).toEqual('abc xyz');
});
it('should only render the same initial styling values once before a directive runs', () => {
@Component({
template: `
<div directive-expecting-styling style="width:200px" class="abc"></div>
`
})
class Cmp {
}
@Directive({selector: '[directive-expecting-styling]'})
class DirectiveExpectingStyling {
constructor(elm: ElementRef) {
const native = elm.nativeElement;
native.style.width = '300px';
native.classList.remove('abc');
}
}
TestBed.configureTestingModule({declarations: [Cmp, DirectiveExpectingStyling]});
const fixture = TestBed.createComponent(Cmp);
fixture.detectChanges();
const element = fixture.nativeElement.querySelector('div');
expect(element.style.width).toEqual('300px');
expect(element.classList.contains('abc')).toBeFalsy();
});
it('should ensure that static classes are assigned to ng-container elements and picked up for content projection',
() => {
@Component({
template: `
<project>
outer
<ng-container class="inner">
inner
</ng-container>
</project>
`
})
class MyApp {
}
@Component({
selector: 'project',
template: `
<div class="outer-area">
<ng-content></ng-content>
</div>
<div class="inner-area">
<ng-content select=".inner"></ng-content>
</div>
`
})
class ProjectCmp {
}
TestBed.configureTestingModule({declarations: [MyApp, ProjectCmp]});
const fixture = TestBed.createComponent(MyApp);
const element = fixture.nativeElement;
fixture.detectChanges();
const inner = element.querySelector('.inner-area');
expect(inner.textContent.trim()).toEqual('inner');
const outer = element.querySelector('.outer-area');
expect(outer.textContent.trim()).toEqual('outer');
});
it('should do nothing for empty style bindings', () => {
@Component({template: '<div [style.color]></div>'})
class App {
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toBe('<div></div>');
});
it('should do nothing for empty class bindings', () => {
@Component({template: '<div [class.is-open]></div>'})
class App {
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toBe('<div></div>');
});
it('should be able to bind a SafeValue to backgroundImage', () => {
@Component({template: '<div [style.backgroundImage]="image"></div>'})
class Cmp {
image !: SafeStyle;
}
TestBed.configureTestingModule({declarations: [Cmp]});
const fixture = TestBed.createComponent(Cmp);
const sanitizer: DomSanitizer = TestBed.get(DomSanitizer);
fixture.componentInstance.image = sanitizer.bypassSecurityTrustStyle('url("#test")');
fixture.detectChanges();
const div = fixture.nativeElement.querySelector('div') as HTMLDivElement;
expect(div.style.backgroundImage).toBe('url("#test")');
onlyInIvy('perf counters').expectPerfCounters({
stylingApply: 2,
stylingApplyCacheMiss: 1,
stylingProp: 2,
stylingPropCacheMiss: 1,
tNode: 3,
});
});
});