refactor(core): Removed linker test references to TestComponentBuilder (#10903)

Removed references to TestComponentBuilder from:
  query_integration_spec.ts
  regression_integration_spec.ts
  security_integration_spec.ts
  view_injector_integration_spec.ts
This commit is contained in:
Chuck Jazdzewski 2016-08-17 16:52:39 -07:00 committed by Kara
parent c48021ab97
commit a5c0349d88
4 changed files with 1128 additions and 1454 deletions

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ import {NgClass, NgIf} from '@angular/common';
import {Component, Injector, OpaqueToken, Pipe, PipeTransform, forwardRef} from '@angular/core'; import {Component, Injector, OpaqueToken, Pipe, PipeTransform, forwardRef} from '@angular/core';
import {ViewMetadata} from '@angular/core/src/metadata/view'; import {ViewMetadata} from '@angular/core/src/metadata/view';
import {TestBed} from '@angular/core/testing'; import {TestBed} from '@angular/core/testing';
import {AsyncTestCompleter, TestComponentBuilder, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers'; import {expect} from '@angular/platform-browser/testing/matchers';
export function main() { export function main() {
@ -23,199 +23,134 @@ function declareTests({useJit}: {useJit: boolean}) {
// Place to put reproductions for regressions // Place to put reproductions for regressions
describe('regressions', () => { describe('regressions', () => {
describe('platform pipes', () => { beforeEach(() => { TestBed.configureTestingModule({declarations: [MyComp1, PlatformPipe]}); });
beforeEach(() => {
TestBed.configureCompiler({useJit: useJit});
TestBed.configureTestingModule({declarations: [PlatformPipe]});
});
it('should overwrite them by custom pipes', describe('platform pipes', () => {
inject( beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); });
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { it('should overwrite them by custom pipes', () => {
tcb.overrideView( TestBed.configureTestingModule({declarations: [CustomPipe]});
MyComp1, const template = '{{true | somePipe}}';
new ViewMetadata({template: '{{true | somePipe}}', pipes: [CustomPipe]})) TestBed.overrideComponent(MyComp1, {set: {template}});
.createAsync(MyComp1) const fixture = TestBed.createComponent(MyComp1);
.then((fixture) => {
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('someCustomPipe'); expect(fixture.nativeElement).toHaveText('someCustomPipe');
async.done(); });
});
}));
}); });
describe('expressions', () => { describe('expressions', () => {
it('should evaluate conditional and boolean operators with right precedence - #8244', () => {
const template = `{{'red' + (true ? ' border' : '')}}`;
TestBed.overrideComponent(MyComp1, {set: {template}});
const fixture = TestBed.createComponent(MyComp1);
it('should evaluate conditional and boolean operators with right precedence - #8244', fixture.detectChanges();
inject( expect(fixture.nativeElement).toHaveText('red border');
[TestComponentBuilder, AsyncTestCompleter], });
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideView(
MyComp1, new ViewMetadata({template: `{{'red' + (true ? ' border' : '')}}`}))
.createAsync(MyComp1)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('red border');
async.done();
});
}));
it('should evaluate conditional and unary operators with right precedence - #8235', it('should evaluate conditional and unary operators with right precedence - #8235', () => {
inject( const template = `{{!null?.length}}`;
[TestComponentBuilder, AsyncTestCompleter], TestBed.overrideComponent(MyComp1, {set: {template}});
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { const fixture = TestBed.createComponent(MyComp1);
tcb.overrideView(MyComp1, new ViewMetadata({template: `{{!null?.length}}`}))
.createAsync(MyComp1)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('true');
async.done();
});
}));
it('should only evaluate stateful pipes once - #10639', fixture.detectChanges();
inject( expect(fixture.nativeElement).toHaveText('true');
[TestComponentBuilder, AsyncTestCompleter], });
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideView(
MyComp1,
new ViewMetadata(
{template: '{{(null|countingPipe)?.value}}', pipes: [CountingPipe]}))
.createAsync(MyComp1)
.then(fixture => {
CountingPipe.reset();
fixture.detectChanges(/* checkNoChanges */ false);
expect(fixture.nativeElement).toHaveText('counting pipe value');
expect(CountingPipe.calls).toBe(1);
async.done();
});
}));
it('should only evaluate methods once - #10639', it('should only evaluate stateful pipes once - #10639', () => {
inject( TestBed.configureTestingModule({declarations: [CountingPipe]});
[TestComponentBuilder, AsyncTestCompleter], const template = '{{(null|countingPipe)?.value}}';
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { TestBed.overrideComponent(MyComp1, {set: {template}});
tcb.overrideView(MyCountingComp, new ViewMetadata({template: '{{method()?.value}}'})) const fixture = TestBed.createComponent(MyComp1);
.createAsync(MyCountingComp)
.then(fixture => { CountingPipe.reset();
MyCountingComp.reset(); fixture.detectChanges(/* checkNoChanges */ false);
fixture.detectChanges(/* checkNoChanges */ false); expect(fixture.nativeElement).toHaveText('counting pipe value');
expect(fixture.nativeElement).toHaveText('counting method value'); expect(CountingPipe.calls).toBe(1);
expect(MyCountingComp.calls).toBe(1); });
async.done();
}); it('should only evaluate methods once - #10639', () => {
})); TestBed.configureTestingModule({declarations: [MyCountingComp]});
const template = '{{method()?.value}}';
TestBed.overrideComponent(MyCountingComp, {set: {template}});
const fixture = TestBed.createComponent(MyCountingComp);
MyCountingComp.reset();
fixture.detectChanges(/* checkNoChanges */ false);
expect(fixture.nativeElement).toHaveText('counting method value');
expect(MyCountingComp.calls).toBe(1);
});
}); });
describe('providers', () => { describe('providers', () => {
function createInjector(tcb: TestComponentBuilder, proviers: any[]): Promise<Injector> { function createInjector(providers: any[]): Injector {
return tcb.overrideProviders(MyComp1, [proviers]) TestBed.overrideComponent(MyComp1, {add: {providers}});
.createAsync(MyComp1) return TestBed.createComponent(MyComp1).componentInstance.injector;
.then((fixture) => fixture.componentInstance.injector);
} }
it('should support providers with an OpaqueToken that contains a `.` in the name', it('should support providers with an OpaqueToken that contains a `.` in the name', () => {
inject( var token = new OpaqueToken('a.b');
[TestComponentBuilder, AsyncTestCompleter], var tokenValue = 1;
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { const injector = createInjector([{provide: token, useValue: tokenValue}]);
var token = new OpaqueToken('a.b'); expect(injector.get(token)).toEqual(tokenValue);
var tokenValue = 1; });
createInjector(tcb, [
{provide: token, useValue: tokenValue}
]).then((injector: Injector) => {
expect(injector.get(token)).toEqual(tokenValue);
async.done();
});
}));
it('should support providers with string token with a `.` in it', it('should support providers with string token with a `.` in it', () => {
inject( var token = 'a.b';
[TestComponentBuilder, AsyncTestCompleter], var tokenValue = 1;
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { const injector = createInjector([{provide: token, useValue: tokenValue}]);
var token = 'a.b';
var tokenValue = 1;
createInjector(tcb, [
{provide: token, useValue: tokenValue}
]).then((injector: Injector) => {
expect(injector.get(token)).toEqual(tokenValue);
async.done();
});
}));
it('should support providers with an anonymous function', expect(injector.get(token)).toEqual(tokenValue);
inject( });
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var token = () => true;
var tokenValue = 1;
createInjector(tcb, [
{provide: token, useValue: tokenValue}
]).then((injector: Injector) => {
expect(injector.get(token)).toEqual(tokenValue);
async.done();
});
}));
it('should support providers with an OpaqueToken that has a StringMap as value', it('should support providers with an anonymous function', () => {
inject( var token = () => true;
[TestComponentBuilder, AsyncTestCompleter], var tokenValue = 1;
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { const injector = createInjector([{provide: token, useValue: tokenValue}]);
var token1 = new OpaqueToken('someToken');
var token2 = new OpaqueToken('someToken'); expect(injector.get(token)).toEqual(tokenValue);
var tokenValue1 = {'a': 1}; });
var tokenValue2 = {'a': 1};
createInjector(tcb, [ it('should support providers with an OpaqueToken that has a StringMap as value', () => {
{provide: token1, useValue: tokenValue1}, var token1 = new OpaqueToken('someToken');
{provide: token2, useValue: tokenValue2} var token2 = new OpaqueToken('someToken');
]).then((injector: Injector) => { var tokenValue1 = {'a': 1};
expect(injector.get(token1)).toEqual(tokenValue1); var tokenValue2 = {'a': 1};
expect(injector.get(token2)).toEqual(tokenValue2); const injector = createInjector(
async.done(); [{provide: token1, useValue: tokenValue1}, {provide: token2, useValue: tokenValue2}]);
});
})); expect(injector.get(token1)).toEqual(tokenValue1);
expect(injector.get(token2)).toEqual(tokenValue2);
});
}); });
it('should allow logging a previous elements class binding via interpolation', it('should allow logging a previous elements class binding via interpolation', () => {
inject( const template = `<div [class.a]="true" #el>Class: {{el.className}}</div>`;
[TestComponentBuilder, AsyncTestCompleter], TestBed.overrideComponent(MyComp1, {set: {template}});
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { const fixture = TestBed.createComponent(MyComp1);
tcb.overrideTemplate(
MyComp1, `<div [class.a]="true" #el>Class: {{el.className}}</div>`)
.createAsync(MyComp1)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('Class: a');
async.done();
});
}));
it('should support ngClass before a component and content projection inside of an ngIf', fixture.detectChanges();
inject( expect(fixture.nativeElement).toHaveText('Class: a');
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async: any) => { });
tcb.overrideView(
MyComp1, new ViewMetadata({
template: `A<cmp-content *ngIf="true" [ngClass]="'red'">B</cmp-content>C`,
directives: [NgClass, NgIf, CmpWithNgContent]
}))
.createAsync(MyComp1)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('ABC');
async.done();
});
}));
it('should handle mutual recursion entered from multiple sides - #7084', it('should support ngClass before a component and content projection inside of an ngIf', () => {
inject( TestBed.configureTestingModule({declarations: [CmpWithNgContent]});
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async: any) => { const template = `A<cmp-content *ngIf="true" [ngClass]="'red'">B</cmp-content>C`;
tcb.createAsync(FakeRecursiveComp).then((fixture) => { TestBed.overrideComponent(MyComp1, {set: {template}});
fixture.detectChanges(); const fixture = TestBed.createComponent(MyComp1);
expect(fixture.nativeElement).toHaveText('[]');
async.done();
});
}));
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('ABC');
});
it('should handle mutual recursion entered from multiple sides - #7084', () => {
TestBed.configureTestingModule({declarations: [FakeRecursiveComp, LeftComp, RightComp]});
const fixture = TestBed.createComponent(FakeRecursiveComp);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('[]');
});
}); });
} }
@ -262,10 +197,6 @@ class CountingPipe implements PipeTransform {
@Component({ @Component({
selector: 'left', selector: 'left',
template: `L<right *ngIf="false"></right>`, template: `L<right *ngIf="false"></right>`,
directives: [
NgIf,
forwardRef(() => RightComp),
]
}) })
class LeftComp { class LeftComp {
} }
@ -273,10 +204,6 @@ class LeftComp {
@Component({ @Component({
selector: 'right', selector: 'right',
template: `R<left *ngIf="false"></left>`, template: `R<left *ngIf="false"></left>`,
directives: [
NgIf,
forwardRef(() => LeftComp),
]
}) })
class RightComp { class RightComp {
} }
@ -284,11 +211,6 @@ class RightComp {
@Component({ @Component({
selector: 'fakeRecursiveComp', selector: 'fakeRecursiveComp',
template: `[<left *ngIf="false"></left><right *ngIf="false"></right>]`, template: `[<left *ngIf="false"></left><right *ngIf="false"></right>]`,
directives: [
NgIf,
forwardRef(() => LeftComp),
forwardRef(() => RightComp),
]
}) })
export class FakeRecursiveComp { export class FakeRecursiveComp {
} }

View File

@ -7,42 +7,30 @@
*/ */
import {Component} from '@angular/core/src/metadata'; import {Component} from '@angular/core/src/metadata';
import {TestBed} from '@angular/core/testing'; import {TestBed, getTestBed} from '@angular/core/testing';
import {AsyncTestCompleter, TestComponentBuilder, afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {DomSanitizationService} from '@angular/platform-browser/src/security/dom_sanitization_service'; import {DomSanitizationService} from '@angular/platform-browser/src/security/dom_sanitization_service';
export function main() { export function main() {
describe('jit', () => { declareTests({useJit: true}); }); describe('jit', () => { declareTests({useJit: true}); });
describe('no jit', () => { declareTests({useJit: false}); }); describe('no jit', () => { declareTests({useJit: false}); });
} }
@Component({selector: 'my-comp', template: '', directives: []}) @Component({selector: 'my-comp', template: ''})
class SecuredComponent { class SecuredComponent {
ctxProp: string; ctxProp: string;
constructor() { this.ctxProp = 'some value'; } constructor() { this.ctxProp = 'some value'; }
} }
function itAsync(msg: string, injections: Function[], f: Function): void;
function itAsync(
msg: string, f: (tcb: TestComponentBuilder, atc: AsyncTestCompleter) => void): void;
function itAsync(
msg: string, f: Function[] | ((tcb: TestComponentBuilder, atc: AsyncTestCompleter) => void),
fn?: Function): void {
if (f instanceof Function) {
it(msg, inject([TestComponentBuilder, AsyncTestCompleter], <Function>f));
} else {
let injections = f;
it(msg, inject(injections, fn));
}
}
function declareTests({useJit}: {useJit: boolean}) { function declareTests({useJit}: {useJit: boolean}) {
describe('security integration tests', function() { describe('security integration tests', function() {
beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); }); beforeEach(() => {
TestBed.configureCompiler({useJit: useJit});
TestBed.configureTestingModule({declarations: [SecuredComponent]});
});
let originalLog: (msg: any) => any; let originalLog: (msg: any) => any;
beforeEach(() => { beforeEach(() => {
@ -52,175 +40,137 @@ function declareTests({useJit}: {useJit: boolean}) {
afterEach(() => { getDOM().log = originalLog; }); afterEach(() => { getDOM().log = originalLog; });
itAsync( it('should disallow binding on*', () => {
'should disallow binding on*', (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { const template = `<div [attr.onclick]="ctxProp"></div>`;
let tpl = `<div [attr.onclick]="ctxProp"></div>`; TestBed.overrideComponent(SecuredComponent, {set: {template}});
tcb.overrideTemplate(SecuredComponent, tpl) try {
.createAsync(SecuredComponent) TestBed.createComponent(SecuredComponent);
.then(v => async.done(new Error('unexpected success'))) throw 'Should throw';
.catch((e) => { } catch (e) {
expect(e.message).toContain( expect(e.message).toContain(
`Template parse errors:\n` + `Template parse errors:\n` +
`Binding to event attribute 'onclick' is disallowed ` + `Binding to event attribute 'onclick' is disallowed ` +
`for security reasons, please use (click)=... `); `for security reasons, please use (click)=... `);
async.done(); }
return null; });
});
});
describe('safe HTML values', function() { describe('safe HTML values', function() {
itAsync( it('should not escape values marked as trusted', () => {
'should not escape values marked as trusted', const template = `<a [href]="ctxProp">Link Title</a>`;
[TestComponentBuilder, AsyncTestCompleter, DomSanitizationService], TestBed.overrideComponent(SecuredComponent, {set: {template}});
(tcb: TestComponentBuilder, async: AsyncTestCompleter, const fixture = TestBed.createComponent(SecuredComponent);
sanitizer: DomSanitizationService) => { const sanitizer: DomSanitizationService = getTestBed().get(DomSanitizationService);
let tpl = `<a [href]="ctxProp">Link Title</a>`;
tcb.overrideTemplate(SecuredComponent, tpl)
.createAsync(SecuredComponent)
.then((fixture) => {
let e = fixture.debugElement.children[0].nativeElement;
let ci = fixture.debugElement.componentInstance;
let trusted = sanitizer.bypassSecurityTrustUrl('javascript:alert(1)');
ci.ctxProp = trusted;
fixture.detectChanges();
expect(getDOM().getProperty(e, 'href')).toEqual('javascript:alert(1)');
async.done(); let e = fixture.debugElement.children[0].nativeElement;
}); let ci = fixture.debugElement.componentInstance;
}); let trusted = sanitizer.bypassSecurityTrustUrl('javascript:alert(1)');
ci.ctxProp = trusted;
fixture.detectChanges();
expect(getDOM().getProperty(e, 'href')).toEqual('javascript:alert(1)');
});
itAsync( it('should error when using the wrong trusted value', () => {
'should error when using the wrong trusted value', const template = `<a [href]="ctxProp">Link Title</a>`;
[TestComponentBuilder, AsyncTestCompleter, DomSanitizationService], TestBed.overrideComponent(SecuredComponent, {set: {template}});
(tcb: TestComponentBuilder, async: AsyncTestCompleter, const fixture = TestBed.createComponent(SecuredComponent);
sanitizer: DomSanitizationService) => { const sanitizer: DomSanitizationService = getTestBed().get(DomSanitizationService);
let tpl = `<a [href]="ctxProp">Link Title</a>`;
tcb.overrideTemplate(SecuredComponent, tpl)
.createAsync(SecuredComponent)
.then((fixture) => {
let trusted = sanitizer.bypassSecurityTrustScript('javascript:alert(1)');
let ci = fixture.debugElement.componentInstance;
ci.ctxProp = trusted;
expect(() => fixture.detectChanges())
.toThrowError(/Required a safe URL, got a Script/);
async.done(); let trusted = sanitizer.bypassSecurityTrustScript('javascript:alert(1)');
}); let ci = fixture.debugElement.componentInstance;
}); ci.ctxProp = trusted;
expect(() => fixture.detectChanges()).toThrowError(/Required a safe URL, got a Script/);
});
itAsync( it('should warn when using in string interpolation', () => {
'should warn when using in string interpolation', const template = `<a href="/foo/{{ctxProp}}">Link Title</a>`;
[TestComponentBuilder, AsyncTestCompleter, DomSanitizationService], TestBed.overrideComponent(SecuredComponent, {set: {template}});
(tcb: TestComponentBuilder, async: AsyncTestCompleter, const fixture = TestBed.createComponent(SecuredComponent);
sanitizer: DomSanitizationService) => { const sanitizer: DomSanitizationService = getTestBed().get(DomSanitizationService);
let tpl = `<a href="/foo/{{ctxProp}}">Link Title</a>`;
tcb.overrideTemplate(SecuredComponent, tpl)
.createAsync(SecuredComponent)
.then((fixture) => {
let e = fixture.debugElement.children[0].nativeElement;
let trusted = sanitizer.bypassSecurityTrustUrl('bar/baz');
let ci = fixture.debugElement.componentInstance;
ci.ctxProp = trusted;
fixture.detectChanges();
expect(getDOM().getProperty(e, 'href')).toMatch(/SafeValue(%20| )must(%20| )use/);
async.done(); let e = fixture.debugElement.children[0].nativeElement;
}); let trusted = sanitizer.bypassSecurityTrustUrl('bar/baz');
}); let ci = fixture.debugElement.componentInstance;
ci.ctxProp = trusted;
fixture.detectChanges();
expect(getDOM().getProperty(e, 'href')).toMatch(/SafeValue(%20| )must(%20| )use/);
});
}); });
describe('sanitizing', () => { describe('sanitizing', () => {
itAsync( it('should escape unsafe attributes', () => {
'should escape unsafe attributes', const template = `<a [href]="ctxProp">Link Title</a>`;
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { TestBed.overrideComponent(SecuredComponent, {set: {template}});
let tpl = `<a [href]="ctxProp">Link Title</a>`; const fixture = TestBed.createComponent(SecuredComponent);
tcb.overrideTemplate(SecuredComponent, tpl)
.createAsync(SecuredComponent)
.then((fixture) => {
let e = fixture.debugElement.children[0].nativeElement;
let ci = fixture.debugElement.componentInstance;
ci.ctxProp = 'hello';
fixture.detectChanges();
// In the browser, reading href returns an absolute URL. On the server side,
// it just echoes back the property.
expect(getDOM().getProperty(e, 'href')).toMatch(/.*\/?hello$/);
ci.ctxProp = 'javascript:alert(1)'; let e = fixture.debugElement.children[0].nativeElement;
fixture.detectChanges(); let ci = fixture.debugElement.componentInstance;
expect(getDOM().getProperty(e, 'href')).toEqual('unsafe:javascript:alert(1)'); ci.ctxProp = 'hello';
fixture.detectChanges();
// In the browser, reading href returns an absolute URL. On the server side,
// it just echoes back the property.
expect(getDOM().getProperty(e, 'href')).toMatch(/.*\/?hello$/);
async.done(); ci.ctxProp = 'javascript:alert(1)';
}); fixture.detectChanges();
}); expect(getDOM().getProperty(e, 'href')).toEqual('unsafe:javascript:alert(1)');
});
itAsync( it('should escape unsafe style values', () => {
'should escape unsafe style values', const template = `<div [style.background]="ctxProp">Text</div>`;
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { TestBed.overrideComponent(SecuredComponent, {set: {template}});
let tpl = `<div [style.background]="ctxProp">Text</div>`; const fixture = TestBed.createComponent(SecuredComponent);
tcb.overrideTemplate(SecuredComponent, tpl)
.createAsync(SecuredComponent)
.then((fixture) => {
let e = fixture.debugElement.children[0].nativeElement;
let ci = fixture.debugElement.componentInstance;
// Make sure binding harmless values works.
ci.ctxProp = 'red';
fixture.detectChanges();
// In some browsers, this will contain the full background specification, not just
// the color.
expect(getDOM().getStyle(e, 'background')).toMatch(/red.*/);
ci.ctxProp = 'url(javascript:evil())'; let e = fixture.debugElement.children[0].nativeElement;
fixture.detectChanges(); let ci = fixture.debugElement.componentInstance;
// Updated value gets rejected, no value change. // Make sure binding harmless values works.
expect(getDOM().getStyle(e, 'background')).not.toContain('javascript'); ci.ctxProp = 'red';
fixture.detectChanges();
// In some browsers, this will contain the full background specification, not just
// the color.
expect(getDOM().getStyle(e, 'background')).toMatch(/red.*/);
async.done(); ci.ctxProp = 'url(javascript:evil())';
}); fixture.detectChanges();
}); // Updated value gets rejected, no value change.
expect(getDOM().getStyle(e, 'background')).not.toContain('javascript');
});
itAsync( it('should escape unsafe SVG attributes', () => {
'should escape unsafe SVG attributes', const template = `<svg:circle [xlink:href]="ctxProp">Text</svg:circle>`;
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { TestBed.overrideComponent(SecuredComponent, {set: {template}});
let tpl = `<svg:circle [xlink:href]="ctxProp">Text</svg:circle>`;
tcb.overrideTemplate(SecuredComponent, tpl)
.createAsync(SecuredComponent)
.then(v => async.done(new Error('unexpected success')))
.catch((e) => {
expect(e.message).toContain(`Can't bind to 'xlink:href'`);
async.done();
return null;
});
});
itAsync( try {
'should escape unsafe HTML values', TestBed.createComponent(SecuredComponent);
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => { throw 'Should throw';
let tpl = `<div [innerHTML]="ctxProp">Text</div>`; } catch (e) {
tcb.overrideTemplate(SecuredComponent, tpl) expect(e.message).toContain(`Can't bind to 'xlink:href'`);
.createAsync(SecuredComponent) }
.then((fixture) => { });
let e = fixture.debugElement.children[0].nativeElement;
let ci = fixture.debugElement.componentInstance;
// Make sure binding harmless values works.
ci.ctxProp = 'some <p>text</p>';
fixture.detectChanges();
expect(getDOM().getInnerHTML(e)).toEqual('some <p>text</p>');
ci.ctxProp = 'ha <script>evil()</script>'; it('should escape unsafe HTML values', () => {
fixture.detectChanges(); const template = `<div [innerHTML]="ctxProp">Text</div>`;
expect(getDOM().getInnerHTML(e)).toEqual('ha evil()'); TestBed.overrideComponent(SecuredComponent, {set: {template}});
const fixture = TestBed.createComponent(SecuredComponent);
ci.ctxProp = 'also <img src="x" onerror="evil()"> evil'; let e = fixture.debugElement.children[0].nativeElement;
fixture.detectChanges(); let ci = fixture.debugElement.componentInstance;
expect(getDOM().getInnerHTML(e)).toEqual('also <img src="x"> evil'); // Make sure binding harmless values works.
ci.ctxProp = 'some <p>text</p>';
fixture.detectChanges();
expect(getDOM().getInnerHTML(e)).toEqual('some <p>text</p>');
ci.ctxProp = 'also <iframe srcdoc="evil"></iframe> evil'; ci.ctxProp = 'ha <script>evil()</script>';
fixture.detectChanges(); fixture.detectChanges();
expect(getDOM().getInnerHTML(e)).toEqual('also evil'); expect(getDOM().getInnerHTML(e)).toEqual('ha evil()');
async.done(); ci.ctxProp = 'also <img src="x" onerror="evil()"> evil';
}); fixture.detectChanges();
}); expect(getDOM().getInnerHTML(e)).toEqual('also <img src="x"> evil');
ci.ctxProp = 'also <iframe srcdoc="evil"></iframe> evil';
fixture.detectChanges();
expect(getDOM().getInnerHTML(e)).toEqual('also evil');
});
}); });
}); });
} }