test(ivy): move property and attribute tests to acceptance (#30321)

- splits existing property acceptance tests into property_binding and property_interpolation
- ports tests from render3 instructions tests to acceptance tests
- removes redundant or unnecessary tests that are covered by existing acceptance tests

:)

PR Close #30321
This commit is contained in:
Ben Lesh
2019-05-06 12:45:09 -07:00
committed by Alex Rickabaugh
parent f26f036286
commit b1d45ee6d2
4 changed files with 339 additions and 944 deletions

View File

@ -0,0 +1,100 @@
/**
* @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 {TestBed} from '@angular/core/testing';
import {By, DomSanitizer, SafeUrl} from '@angular/platform-browser';
describe('attribute creation', () => {
it('should create an element', () => {
@Component({
template: `<div id="test" title="Hello"></div>`,
})
class Comp {
}
TestBed.configureTestingModule({declarations: [Comp]});
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
const div = fixture.debugElement.query(By.css('div')).nativeElement;
expect(div.id).toEqual('test');
expect(div.title).toEqual('Hello');
});
it('should allow for setting xlink namespaced attributes', () => {
@Component({
template: `<div id="test" xlink:href="bar" title="Hello"></div>`,
})
class Comp {
}
TestBed.configureTestingModule({declarations: [Comp]});
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
const div = fixture.debugElement.query(By.css('div')).nativeElement;
const attrs = div.attributes;
expect(attrs['id'].name).toEqual('id');
expect(attrs['id'].namespaceURI).toEqual(null);
expect(attrs['id'].value).toEqual('test');
expect(attrs['xlink:href'].name).toEqual('xlink:href');
expect(attrs['xlink:href'].namespaceURI).toEqual('http://www.w3.org/1999/xlink');
expect(attrs['xlink:href'].value).toEqual('bar');
expect(attrs['title'].name).toEqual('title');
expect(attrs['title'].namespaceURI).toEqual(null);
expect(attrs['title'].value).toEqual('Hello');
});
});
describe('attribute binding', () => {
it('should set attribute values', () => {
@Component({
template: `<a [attr.href]="url"></a>`,
})
class Comp {
url = 'https://angular.io/robots.txt';
}
TestBed.configureTestingModule({declarations: [Comp]});
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
const a = fixture.debugElement.query(By.css('a')).nativeElement;
// NOTE: different browsers will add `//` into the URI.
expect(a.href).toEqual('https://angular.io/robots.txt');
});
it('should sanitize attribute values', () => {
@Component({
template: `<a [attr.href]="badUrl"></a>`,
})
class Comp {
badUrl: string|SafeUrl = 'javascript:true';
}
TestBed.configureTestingModule({declarations: [Comp]});
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
const a = fixture.debugElement.query(By.css('a')).nativeElement;
// NOTE: different browsers will add `//` into the URI.
expect(a.href.indexOf('unsafe:')).toBe(0);
const domSanitizer: DomSanitizer = TestBed.get(DomSanitizer);
fixture.componentInstance.badUrl =
domSanitizer.bypassSecurityTrustUrl('javascript:alert("this is fine")');
fixture.detectChanges();
// should not start with `unsafe:`.
expect(a.href.indexOf('unsafe:')).toBe(-1);
});
});

View File

@ -0,0 +1,138 @@
/**
* @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, Input} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By, DomSanitizer, SafeUrl} from '@angular/platform-browser';
describe('property bindings', () => {
it('should update bindings when value changes', () => {
@Component({
template: `<a [title]="title"></a>`,
})
class Comp {
title = 'Hello';
}
TestBed.configureTestingModule({declarations: [Comp]});
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
let a = fixture.debugElement.query(By.css('a')).nativeElement;
expect(a.title).toBe('Hello');
fixture.componentInstance.title = 'World';
fixture.detectChanges();
expect(a.title).toBe('World');
});
it('should not update bindings when value does not change', () => {
@Component({
template: `<a [title]="title"></a>`,
})
class Comp {
title = 'Hello';
}
TestBed.configureTestingModule({declarations: [Comp]});
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
let a = fixture.debugElement.query(By.css('a')).nativeElement;
expect(a.title).toBe('Hello');
fixture.detectChanges();
expect(a.title).toBe('Hello');
});
it('should bind to properties whose names do not correspond to their attribute names', () => {
@Component({template: '<label [for]="forValue"></label>'})
class MyComp {
forValue?: string;
}
TestBed.configureTestingModule({declarations: [MyComp]});
const fixture = TestBed.createComponent(MyComp);
const labelNode = fixture.debugElement.query(By.css('label'));
fixture.componentInstance.forValue = 'some-input';
fixture.detectChanges();
expect(labelNode.nativeElement.getAttribute('for')).toBe('some-input');
fixture.componentInstance.forValue = 'some-textarea';
fixture.detectChanges();
expect(labelNode.nativeElement.getAttribute('for')).toBe('some-textarea');
});
it('should not map properties whose names do not correspond to their attribute names, ' +
'if they correspond to inputs',
() => {
@Component({template: '', selector: 'my-comp'})
class MyComp {
@Input() for !:string;
}
@Component({template: '<my-comp [for]="forValue"></my-comp>'})
class App {
forValue?: string;
}
TestBed.configureTestingModule({declarations: [App, MyComp]});
const fixture = TestBed.createComponent(App);
const myCompNode = fixture.debugElement.query(By.directive(MyComp));
fixture.componentInstance.forValue = 'hello';
fixture.detectChanges();
expect(myCompNode.nativeElement.getAttribute('for')).toBeFalsy();
expect(myCompNode.componentInstance.for).toBe('hello');
fixture.componentInstance.forValue = 'hej';
fixture.detectChanges();
expect(myCompNode.nativeElement.getAttribute('for')).toBeFalsy();
expect(myCompNode.componentInstance.for).toBe('hej');
});
it('should use the sanitizer in bound properties', () => {
@Component({
template: `
<a [href]="url">
`
})
class App {
url: string|SafeUrl = 'javascript:alert("haha, I am taking over your computer!!!");';
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const a = fixture.nativeElement.querySelector('a');
expect(a.href.indexOf('unsafe:')).toBe(0);
const domSanitzer: DomSanitizer = TestBed.get(DomSanitizer);
fixture.componentInstance.url =
domSanitzer.bypassSecurityTrustUrl('javascript:alert("the developer wanted this");');
fixture.detectChanges();
expect(a.href.indexOf('unsafe:')).toBe(-1);
});
it('should not stringify non-string values', () => {
@Component({
template: `<input [required]="isRequired"/>`,
})
class Comp {
isRequired = false;
}
TestBed.configureTestingModule({declarations: [Comp]});
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('input')).nativeElement.required).toBe(false);
});
});

View File

@ -5,82 +5,12 @@
* 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, Input} from '@angular/core';
import {Component} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {of } from 'rxjs';
describe('property instructions', () => {
it('should bind to properties whose names do not correspond to their attribute names', () => {
@Component({template: '<label [for]="forValue"></label>'})
class MyComp {
forValue?: string;
}
TestBed.configureTestingModule({declarations: [MyComp]});
const fixture = TestBed.createComponent(MyComp);
const labelNode = fixture.debugElement.query(By.css('label'));
fixture.componentInstance.forValue = 'some-input';
fixture.detectChanges();
expect(labelNode.nativeElement.getAttribute('for')).toBe('some-input');
fixture.componentInstance.forValue = 'some-textarea';
fixture.detectChanges();
expect(labelNode.nativeElement.getAttribute('for')).toBe('some-textarea');
});
it('should not allow unsanitary urls in bound properties', () => {
@Component({
template: `
<img [src]="naughty">
`
})
class App {
naughty = 'javascript:alert("haha, I am taking over your computer!!!");';
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img = fixture.nativeElement.querySelector('img');
expect(img.src.indexOf('unsafe:')).toBe(0);
});
it('should not map properties whose names do not correspond to their attribute names, ' +
'if they correspond to inputs',
() => {
@Component({template: '', selector: 'my-comp'})
class MyComp {
@Input() for !:string;
}
@Component({template: '<my-comp [for]="forValue"></my-comp>'})
class App {
forValue?: string;
}
TestBed.configureTestingModule({declarations: [App, MyComp]});
const fixture = TestBed.createComponent(App);
const myCompNode = fixture.debugElement.query(By.directive(MyComp));
fixture.componentInstance.forValue = 'hello';
fixture.detectChanges();
expect(myCompNode.nativeElement.getAttribute('for')).toBeFalsy();
expect(myCompNode.componentInstance.for).toBe('hello');
fixture.componentInstance.forValue = 'hej';
fixture.detectChanges();
expect(myCompNode.nativeElement.getAttribute('for')).toBeFalsy();
expect(myCompNode.componentInstance.for).toBe('hej');
});
describe('property interpolation', () => {
it('should handle all flavors of interpolated properties', () => {
@Component({
template: `
@ -230,4 +160,70 @@ describe('property instructions', () => {
.toEqual(
`http://g.com/?one=1&two=2&three=3&four=4&five=5&six=6&seven=7&eight=8&nine=9&ten=10`);
});
it('should support the chained use cases of propertyInterpolate instructions', () => {
// The below *just happens* to have two attributes in a row that have the same interpolation
// count.
@Component({
template: `
<img title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i{{nine}}j" alt="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i{{nine}}j"/>
<img title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i" alt="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i"/>
<img title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h" alt="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h"/>
<img title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g" alt="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g"/>
<img title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f" alt="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f"/>
<img title="a{{one}}b{{two}}c{{three}}d{{four}}e" alt="a{{one}}b{{two}}c{{three}}d{{four}}e"/>
<img title="a{{one}}b{{two}}c{{three}}d" alt="a{{one}}b{{two}}c{{three}}d"/>
<img title="a{{one}}b{{two}}c" alt="a{{one}}b{{two}}c"/>
<img title="a{{one}}b" alt="a{{one}}b"/>
<img title="{{one}}" alt="{{one}}"/>
`
})
class AppComp {
one = 1;
two = 2;
three = 3;
four = 4;
five = 5;
six = 6;
seven = 7;
eight = 8;
nine = 9;
}
TestBed.configureTestingModule({declarations: [AppComp]});
const fixture = TestBed.createComponent(AppComp);
fixture.detectChanges();
const titles = Array.from(fixture.nativeElement.querySelectorAll('img[title]'))
.map((img: HTMLImageElement) => img.title);
expect(titles).toEqual([
'a1b2c3d4e5f6g7h8i9j',
'a1b2c3d4e5f6g7h8i',
'a1b2c3d4e5f6g7h',
'a1b2c3d4e5f6g',
'a1b2c3d4e5f',
'a1b2c3d4e',
'a1b2c3d',
'a1b2c',
'a1b',
'1',
]);
const others = Array.from(fixture.nativeElement.querySelectorAll('img[alt]'))
.map((img: HTMLImageElement) => img.alt);
expect(others).toEqual([
'a1b2c3d4e5f6g7h8i9j',
'a1b2c3d4e5f6g7h8i',
'a1b2c3d4e5f6g7h',
'a1b2c3d4e5f6g',
'a1b2c3d4e5f',
'a1b2c3d4e',
'a1b2c3d',
'a1b2c',
'a1b',
'1',
]);
});
});