From 28ceca01630936783129e1159da712b086070322 Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Wed, 12 Dec 2018 16:21:58 +0100 Subject: [PATCH] test(ivy): update root causes for @angular/core TestBed failures (#27627) PR Close #27627 --- .../change_detection_integration_spec.ts | 240 +++++++++--------- packages/core/test/linker/integration_spec.ts | 163 ++++++------ .../linker/projection_integration_spec.ts | 29 ++- 3 files changed, 219 insertions(+), 213 deletions(-) diff --git a/packages/core/test/linker/change_detection_integration_spec.ts b/packages/core/test/linker/change_detection_integration_spec.ts index 82ae3fd7ea..ea57cad708 100644 --- a/packages/core/test/linker/change_detection_integration_spec.ts +++ b/packages/core/test/linker/change_detection_integration_spec.ts @@ -996,32 +996,33 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [ expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); })); - fixmeIvy('unknown').it( - 'should not call ngAfterViewInit again if it throws', fakeAsync(() => { - const ctx = - createCompFixture('
'); + fixmeIvy( + 'FW-830: Exception thrown in ngAfterViewInit triggers ngAfterViewInit re-execution') + .it('should not call ngAfterViewInit again if it throws', fakeAsync(() => { + const ctx = createCompFixture( + '
'); - let errored = false; - // First pass fails, but ngAfterViewInit should be called. - try { - ctx.detectChanges(false); - } catch (e) { - errored = true; - } - expect(errored).toBe(true); + let errored = false; + // First pass fails, but ngAfterViewInit should be called. + try { + ctx.detectChanges(false); + } catch (e) { + errored = true; + } + expect(errored).toBe(true); - expect(directiveLog.filter(['ngAfterViewInit'])).toEqual(['dir.ngAfterViewInit']); - directiveLog.clear(); + expect(directiveLog.filter(['ngAfterViewInit'])).toEqual(['dir.ngAfterViewInit']); + directiveLog.clear(); - // Second change detection also fails, but this time ngAfterViewInit should not be - // called. - try { - ctx.detectChanges(false); - } catch (e) { - throw new Error('Second detectChanges() should not have run detection.'); - } - expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); - })); + // Second change detection also fails, but this time ngAfterViewInit should not be + // called. + try { + ctx.detectChanges(false); + } catch (e) { + throw new Error('Second detectChanges() should not have run detection.'); + } + expect(directiveLog.filter(['ngAfterViewInit'])).toEqual([]); + })); }); describe('ngAfterViewChecked', () => { @@ -1185,14 +1186,14 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [ /Previous value: 'changed: undefined'\. Current value: 'changed: 1'/g); })); - fixmeIvy('unknown').it( - 'should warn when the view has been created in a cd hook', fakeAsync(() => { - const ctx = createCompFixture('
{{ a }}
', TestData); - ctx.componentInstance.a = 1; - expect(() => ctx.detectChanges()) - .toThrowError( - /It seems like the view has been created after its parent and its children have been dirty checked/); - })); + fixmeIvy('FW-831: Views created in a cd hooks throw in view engine') + .it('should warn when the view has been created in a cd hook', fakeAsync(() => { + const ctx = createCompFixture('
{{ a }}
', TestData); + ctx.componentInstance.a = 1; + expect(() => ctx.detectChanges()) + .toThrowError( + /It seems like the view has been created after its parent and its children have been dirty checked/); + })); it('should not throw when two arrays are structurally the same', fakeAsync(() => { const ctx = _bindSimpleValue('a', TestData); @@ -1537,110 +1538,111 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [ childThrows: LifetimeMethods; } - fixmeIvy('unknown').describe('calling init', () => { - function initialize(options: Options) { - @Component({selector: 'my-child', template: ''}) - class MyChild { - private thrown = LifetimeMethods.None; + fixmeIvy('FW-832: View engine supports recursive detectChanges() calls') + .describe('calling init', () => { + function initialize(options: Options) { + @Component({selector: 'my-child', template: ''}) + class MyChild { + private thrown = LifetimeMethods.None; - // TODO(issue/24571): remove '!'. - @Input() inp !: boolean; - @Output() outp = new EventEmitter(); + // TODO(issue/24571): remove '!'. + @Input() inp !: boolean; + @Output() outp = new EventEmitter(); - constructor() {} + constructor() {} - ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); } - ngOnInit() { this.check(LifetimeMethods.ngOnInit); } - ngOnChanges() { this.check(LifetimeMethods.ngOnChanges); } - ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); } - ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); } + ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); } + ngOnInit() { this.check(LifetimeMethods.ngOnInit); } + ngOnChanges() { this.check(LifetimeMethods.ngOnChanges); } + ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); } + ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); } - private check(method: LifetimeMethods) { - log(`MyChild::${LifetimeMethods[method]}()`); + private check(method: LifetimeMethods) { + log(`MyChild::${LifetimeMethods[method]}()`); - if ((options.childRecursion & method) !== 0) { - if (logged.length < 20) { - this.outp.emit(null); - } else { - fail(`Unexpected MyChild::${LifetimeMethods[method]} recursion`); + if ((options.childRecursion & method) !== 0) { + if (logged.length < 20) { + this.outp.emit(null); + } else { + fail(`Unexpected MyChild::${LifetimeMethods[method]} recursion`); + } + } + if ((options.childThrows & method) !== 0) { + if ((this.thrown & method) === 0) { + this.thrown |= method; + log(`()`); + throw new Error(`Throw from MyChild::${LifetimeMethods[method]}`); + } + } } } - if ((options.childThrows & method) !== 0) { - if ((this.thrown & method) === 0) { - this.thrown |= method; - log(`()`); - throw new Error(`Throw from MyChild::${LifetimeMethods[method]}`); + + @Component({ + selector: 'my-component', + template: `` + }) + class MyComponent { + constructor(private changeDetectionRef: ChangeDetectorRef) {} + ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); } + ngOnInit() { this.check(LifetimeMethods.ngOnInit); } + ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); } + ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); } + onOutp() { + log(''); + this.changeDetectionRef.detectChanges(); + log(''); + } + + private check(method: LifetimeMethods) { + log(`MyComponent::${LifetimeMethods[method]}()`); } } - } - } - @Component({ - selector: 'my-component', - template: `` - }) - class MyComponent { - constructor(private changeDetectionRef: ChangeDetectorRef) {} - ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); } - ngOnInit() { this.check(LifetimeMethods.ngOnInit); } - ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); } - ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); } - onOutp() { - log(''); - this.changeDetectionRef.detectChanges(); - log(''); + TestBed.configureTestingModule({declarations: [MyChild, MyComponent]}); + + return createCompFixture(``); } - private check(method: LifetimeMethods) { - log(`MyComponent::${LifetimeMethods[method]}()`); - } - } - - TestBed.configureTestingModule({declarations: [MyChild, MyComponent]}); - - return createCompFixture(``); - } - - function ensureOneInit(options: Options) { - const ctx = initialize(options); + function ensureOneInit(options: Options) { + const ctx = initialize(options); - const throws = options.childThrows != LifetimeMethods.None; - if (throws) { - log(``); - expect(() => { - // Expect child to throw. + const throws = options.childThrows != LifetimeMethods.None; + if (throws) { + log(``); + expect(() => { + // Expect child to throw. + ctx.detectChanges(); + }).toThrow(); + log(``); + log(``); + } ctx.detectChanges(); - }).toThrow(); - log(``); - log(``); - } - ctx.detectChanges(); - if (throws) log(``); - expectOnceAndOnlyOnce('MyComponent::ngOnInit()'); - expectOnceAndOnlyOnce('MyChild::ngOnInit()'); - expectOnceAndOnlyOnce('MyComponent::ngAfterViewInit()'); - expectOnceAndOnlyOnce('MyComponent::ngAfterContentInit()'); - expectOnceAndOnlyOnce('MyChild::ngAfterViewInit()'); - expectOnceAndOnlyOnce('MyChild::ngAfterContentInit()'); - } + if (throws) log(``); + expectOnceAndOnlyOnce('MyComponent::ngOnInit()'); + expectOnceAndOnlyOnce('MyChild::ngOnInit()'); + expectOnceAndOnlyOnce('MyComponent::ngAfterViewInit()'); + expectOnceAndOnlyOnce('MyComponent::ngAfterContentInit()'); + expectOnceAndOnlyOnce('MyChild::ngAfterViewInit()'); + expectOnceAndOnlyOnce('MyChild::ngAfterContentInit()'); + } - forEachMethod(LifetimeMethods.InitMethodsAndChanges, method => { - it(`should ensure that init hooks are called once an only once with recursion in ${LifetimeMethods[method]} `, - () => { - // Ensure all the init methods are called once. - ensureOneInit({childRecursion: method, childThrows: LifetimeMethods.None}); - }); - }); - forEachMethod(LifetimeMethods.All, method => { - it(`should ensure that init hooks are called once an only once with a throw in ${LifetimeMethods[method]} `, - () => { - // Ensure all the init methods are called once. - // the first cycle throws but the next cycle should complete the inits. - ensureOneInit({childRecursion: LifetimeMethods.None, childThrows: method}); - }); - }); - }); + forEachMethod(LifetimeMethods.InitMethodsAndChanges, method => { + it(`should ensure that init hooks are called once an only once with recursion in ${LifetimeMethods[method]} `, + () => { + // Ensure all the init methods are called once. + ensureOneInit({childRecursion: method, childThrows: LifetimeMethods.None}); + }); + }); + forEachMethod(LifetimeMethods.All, method => { + it(`should ensure that init hooks are called once an only once with a throw in ${LifetimeMethods[method]} `, + () => { + // Ensure all the init methods are called once. + // the first cycle throws but the next cycle should complete the inits. + ensureOneInit({childRecursion: LifetimeMethods.None, childThrows: method}); + }); + }); + }); }); }); })(); diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts index c0afbf6e7c..623af7d9c1 100644 --- a/packages/core/test/linker/integration_spec.ts +++ b/packages/core/test/linker/integration_spec.ts @@ -699,33 +699,32 @@ function declareTests(config?: {useJit: boolean}) { expect(cmp.prop).toEqual('two'); }); - if (getDOM().supportsDOMEvents()) { - fixmeIvy('unknown').it( - 'should be checked when an async pipe requests a check', fakeAsync(() => { - TestBed.configureTestingModule( - {declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]}); - const template = ''; - TestBed.overrideComponent(MyComp, {set: {template}}); - const fixture = TestBed.createComponent(MyComp); + fixmeIvy( + 'FW-764: fixture.detectChanges() is not respecting OnPush flag on components in the root template') + .it('should be checked when an async pipe requests a check', fakeAsync(() => { + TestBed.configureTestingModule( + {declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]}); + const template = ''; + TestBed.overrideComponent(MyComp, {set: {template}}); + const fixture = TestBed.createComponent(MyComp); - tick(); + tick(); - const cmp: PushCmpWithAsyncPipe = - fixture.debugElement.children[0].references !['cmp']; - fixture.detectChanges(); - expect(cmp.numberOfChecks).toEqual(1); + const cmp: PushCmpWithAsyncPipe = + fixture.debugElement.children[0].references !['cmp']; + fixture.detectChanges(); + expect(cmp.numberOfChecks).toEqual(1); - fixture.detectChanges(); - fixture.detectChanges(); - expect(cmp.numberOfChecks).toEqual(1); + fixture.detectChanges(); + fixture.detectChanges(); + expect(cmp.numberOfChecks).toEqual(1); - cmp.resolve(2); - tick(); + cmp.resolve(2); + tick(); - fixture.detectChanges(); - expect(cmp.numberOfChecks).toEqual(2); - })); - } + fixture.detectChanges(); + expect(cmp.numberOfChecks).toEqual(2); + })); }); it('should create a component that injects an @Host', () => { @@ -1871,79 +1870,83 @@ function declareTests(config?: {useJit: boolean}) { if (getDOM().supportsDOMEvents()) { describe('svg', () => { - fixmeIvy('unknown').it('should support svg elements', () => { - TestBed.configureTestingModule({declarations: [MyComp]}); - const template = ''; - TestBed.overrideComponent(MyComp, {set: {template}}); - const fixture = TestBed.createComponent(MyComp); + fixmeIvy('FW-672: SVG attribute xlink:href is output as :xlink:href (extra ":")') + .it('should support svg elements', () => { + TestBed.configureTestingModule({declarations: [MyComp]}); + const template = ''; + TestBed.overrideComponent(MyComp, {set: {template}}); + const fixture = TestBed.createComponent(MyComp); - const el = fixture.nativeElement; - const svg = getDOM().childNodes(el)[0]; - const use = getDOM().childNodes(svg)[0]; - expect(getDOM().getProperty(svg, 'namespaceURI')) - .toEqual('http://www.w3.org/2000/svg'); - expect(getDOM().getProperty(use, 'namespaceURI')) - .toEqual('http://www.w3.org/2000/svg'); + const el = fixture.nativeElement; + const svg = getDOM().childNodes(el)[0]; + const use = getDOM().childNodes(svg)[0]; + expect(getDOM().getProperty(svg, 'namespaceURI')) + .toEqual('http://www.w3.org/2000/svg'); + expect(getDOM().getProperty(use, 'namespaceURI')) + .toEqual('http://www.w3.org/2000/svg'); - const firstAttribute = getDOM().getProperty(use, 'attributes')[0]; - expect(firstAttribute.name).toEqual('xlink:href'); - expect(firstAttribute.namespaceURI).toEqual('http://www.w3.org/1999/xlink'); - }); + const firstAttribute = getDOM().getProperty(use, 'attributes')[0]; + expect(firstAttribute.name).toEqual('xlink:href'); + expect(firstAttribute.namespaceURI).toEqual('http://www.w3.org/1999/xlink'); + }); - fixmeIvy('unknown').it('should support foreignObjects with document fragments', () => { - TestBed.configureTestingModule({declarations: [MyComp]}); - const template = - '

Test

'; - TestBed.overrideComponent(MyComp, {set: {template}}); - const fixture = TestBed.createComponent(MyComp); + fixmeIvy('FW-811: Align HTML namespaces between Ivy and Render2') + .it('should support foreignObjects with document fragments', () => { + TestBed.configureTestingModule({declarations: [MyComp]}); + const template = + '

Test

'; + TestBed.overrideComponent(MyComp, {set: {template}}); + const fixture = TestBed.createComponent(MyComp); - const el = fixture.nativeElement; - const svg = getDOM().childNodes(el)[0]; - const foreignObject = getDOM().childNodes(svg)[0]; - const p = getDOM().childNodes(foreignObject)[0]; - expect(getDOM().getProperty(svg, 'namespaceURI')) - .toEqual('http://www.w3.org/2000/svg'); - expect(getDOM().getProperty(foreignObject, 'namespaceURI')) - .toEqual('http://www.w3.org/2000/svg'); - expect(getDOM().getProperty(p, 'namespaceURI')) - .toEqual('http://www.w3.org/1999/xhtml'); - }); + const el = fixture.nativeElement; + const svg = getDOM().childNodes(el)[0]; + const foreignObject = getDOM().childNodes(svg)[0]; + const p = getDOM().childNodes(foreignObject)[0]; + expect(getDOM().getProperty(svg, 'namespaceURI')) + .toEqual('http://www.w3.org/2000/svg'); + expect(getDOM().getProperty(foreignObject, 'namespaceURI')) + .toEqual('http://www.w3.org/2000/svg'); + expect(getDOM().getProperty(p, 'namespaceURI')) + .toEqual('http://www.w3.org/1999/xhtml'); + }); }); describe('attributes', () => { - fixmeIvy('unknown').it('should support attributes with namespace', () => { - TestBed.configureTestingModule({declarations: [MyComp, SomeCmp]}); - const template = ''; - TestBed.overrideComponent(SomeCmp, {set: {template}}); - const fixture = TestBed.createComponent(SomeCmp); + fixmeIvy('FW-672: SVG attribute xlink:href is output as :xlink:href (extra ":")') + .it('should support attributes with namespace', () => { + TestBed.configureTestingModule({declarations: [MyComp, SomeCmp]}); + const template = ''; + TestBed.overrideComponent(SomeCmp, {set: {template}}); + const fixture = TestBed.createComponent(SomeCmp); - const useEl = getDOM().firstChild(fixture.nativeElement); - expect(getDOM().getAttributeNS(useEl, 'http://www.w3.org/1999/xlink', 'href')) - .toEqual('#id'); - }); + const useEl = getDOM().firstChild(fixture.nativeElement); + expect(getDOM().getAttributeNS(useEl, 'http://www.w3.org/1999/xlink', 'href')) + .toEqual('#id'); + }); - fixmeIvy('unknown').it('should support binding to attributes with namespace', () => { - TestBed.configureTestingModule({declarations: [MyComp, SomeCmp]}); - const template = ''; - TestBed.overrideComponent(SomeCmp, {set: {template}}); - const fixture = TestBed.createComponent(SomeCmp); + fixmeIvy('FW-672: SVG attribute xlink:href is output as :xlink:href (extra ":")') + .it('should support binding to attributes with namespace', () => { + TestBed.configureTestingModule({declarations: [MyComp, SomeCmp]}); + const template = ''; + TestBed.overrideComponent(SomeCmp, {set: {template}}); + const fixture = TestBed.createComponent(SomeCmp); - const cmp = fixture.componentInstance; - const useEl = getDOM().firstChild(fixture.nativeElement); + const cmp = fixture.componentInstance; + const useEl = getDOM().firstChild(fixture.nativeElement); - cmp.value = '#id'; - fixture.detectChanges(); + cmp.value = '#id'; + fixture.detectChanges(); - expect(getDOM().getAttributeNS(useEl, 'http://www.w3.org/1999/xlink', 'href')) - .toEqual('#id'); + expect(getDOM().getAttributeNS(useEl, 'http://www.w3.org/1999/xlink', 'href')) + .toEqual('#id'); - cmp.value = null; - fixture.detectChanges(); + cmp.value = null; + fixture.detectChanges(); - expect(getDOM().hasAttributeNS(useEl, 'http://www.w3.org/1999/xlink', 'href')) - .toEqual(false); - }); + expect(getDOM().hasAttributeNS(useEl, 'http://www.w3.org/1999/xlink', 'href')) + .toEqual(false); + }); }); } }); diff --git a/packages/core/test/linker/projection_integration_spec.ts b/packages/core/test/linker/projection_integration_spec.ts index 8e6f0f0bdc..182e5baa46 100644 --- a/packages/core/test/linker/projection_integration_spec.ts +++ b/packages/core/test/linker/projection_integration_spec.ts @@ -81,21 +81,22 @@ describe('projection', () => { expect(main.nativeElement).toHaveText(''); }); - fixmeIvy('unknown').it('should support multiple content tags', () => { - TestBed.configureTestingModule({declarations: [MultipleContentTagsComponent]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '' + - '
B
' + - '
C
' + - '
A
' + - '
' - } - }); - const main = TestBed.createComponent(MainComp); + fixmeIvy('FW-833: Directive / projected node matching against class name') + .it('should support multiple content tags', () => { + TestBed.configureTestingModule({declarations: [MultipleContentTagsComponent]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '' + + '
B
' + + '
C
' + + '
A
' + + '
' + } + }); + const main = TestBed.createComponent(MainComp); - expect(main.nativeElement).toHaveText('(A, BC)'); - }); + expect(main.nativeElement).toHaveText('(A, BC)'); + }); it('should redistribute only direct children', () => { TestBed.configureTestingModule({declarations: [MultipleContentTagsComponent]});