diff --git a/packages/core/test/acceptance/BUILD.bazel b/packages/core/test/acceptance/BUILD.bazel index b3abaa7ccad..e6e0e5a7282 100644 --- a/packages/core/test/acceptance/BUILD.bazel +++ b/packages/core/test/acceptance/BUILD.bazel @@ -1,12 +1,17 @@ -load("//tools:defaults.bzl", "jasmine_node_test", "karma_web_test_suite", "ts_library") +load("//tools:defaults.bzl", "jasmine_node_test", "karma_web_test_suite", "ng_module", "ts_library") package(default_visibility = ["//visibility:private"]) +SPEC_FILES_WITH_FORWARD_REFS = [ + "di_forward_ref_spec.ts", +] + ts_library( name = "acceptance_lib", testonly = True, srcs = glob( ["**/*.ts"], + exclude = SPEC_FILES_WITH_FORWARD_REFS, ), # Visible to //:saucelabs_unit_tests visibility = ["//:__pkg__"], @@ -35,11 +40,28 @@ ts_library( ], ) +# Note: The `forward_ref` example tests are built through this `ng_module` sub-target. +# This is done so that DI decorator/type metadata is processed manually by the compiler +# ahead of time. We cannot rely on the official TypeScript decorator downlevel emit (for JIT), +# as the output is not compatible with `forwardRef` and ES2015+. More details here: +# https://github.com/angular/angular/commit/323651bd38909b0f4226bcb6c8f5abafa91cf9d9. +# https://github.com/microsoft/TypeScript/issues/27519. +ng_module( + name = "forward_ref_test_lib", + testonly = True, + srcs = SPEC_FILES_WITH_FORWARD_REFS, + deps = [ + "//packages/core", + "//packages/core/testing", + ], +) + jasmine_node_test( name = "acceptance", bootstrap = ["//tools/testing:node_es2015"], deps = [ ":acceptance_lib", + ":forward_ref_test_lib", "//packages/zone.js/lib:zone_d_ts", "@npm//base64-js", "@npm//source-map", @@ -50,5 +72,6 @@ karma_web_test_suite( name = "acceptance_web", deps = [ ":acceptance_lib", + ":forward_ref_test_lib", ], ) diff --git a/packages/core/test/acceptance/di_forward_ref_spec.ts b/packages/core/test/acceptance/di_forward_ref_spec.ts new file mode 100644 index 00000000000..4551399ee92 --- /dev/null +++ b/packages/core/test/acceptance/di_forward_ref_spec.ts @@ -0,0 +1,67 @@ +/** + * @license + * Copyright Google LLC 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, forwardRef, Host, Inject, ViewChild} from '@angular/core'; +import {TestBed} from '@angular/core/testing'; + +// **NOTE**: More details on why tests relying on `forwardRef` are put into this +// file can be found in the `BUILD.bazel` file declaring the forward ref test target. + +describe('di with forwardRef', () => { + describe('directive injection', () => { + it('should throw if directives try to inject each other', () => { + @Directive({selector: '[dirB]'}) + class DirectiveB { + constructor(@Inject(forwardRef(() => DirectiveA)) siblingDir: DirectiveA) {} + } + + @Directive({selector: '[dirA]'}) + class DirectiveA { + constructor(siblingDir: DirectiveB) {} + } + + @Component({template: '
'}) + class MyComp { + } + + TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]}); + expect(() => TestBed.createComponent(MyComp)) + .toThrowError( + 'NG0200: Circular dependency in DI detected for DirectiveA. Find more at https://angular.io/errors/NG0200'); + }); + + describe('flags', () => { + describe('@Host', () => { + it('should find host component on the host itself', () => { + @Directive({selector: '[dirComp]'}) + class DirectiveComp { + constructor(@Inject(forwardRef(() => MyComp)) @Host() public comp: MyComp) {} + } + + @Component({selector: 'my-comp', template: '
'}) + class MyComp { + @ViewChild(DirectiveComp) dirComp!: DirectiveComp; + } + + @Component({template: '', jit: true}) + class MyApp { + @ViewChild(MyComp) myComp!: MyComp; + } + + TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]}); + const fixture = TestBed.createComponent(MyApp); + fixture.detectChanges(); + + const myComp = fixture.componentInstance.myComp; + const dirComp = myComp.dirComp; + expect(dirComp.comp).toBe(myComp); + }); + }); + }); + }); +}); diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts index 2824bec4b61..a348745229c 100644 --- a/packages/core/test/acceptance/di_spec.ts +++ b/packages/core/test/acceptance/di_spec.ts @@ -684,27 +684,6 @@ describe('di', () => { expect(cmp.componentInstance.testB.a).toBeNull(); }); - it('should throw if directives try to inject each other', () => { - @Directive({selector: '[dirB]'}) - class DirectiveB { - constructor(@Inject(forwardRef(() => DirectiveA)) siblingDir: DirectiveA) {} - } - - @Directive({selector: '[dirA]'}) - class DirectiveA { - constructor(siblingDir: DirectiveB) {} - } - - @Component({template: '
'}) - class MyComp { - } - - TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]}); - expect(() => TestBed.createComponent(MyComp)) - .toThrowError( - 'NG0200: Circular dependency in DI detected for DirectiveA. Find more at https://angular.io/errors/NG0200'); - }); - it('should throw if directive tries to inject itself', () => { @Directive({selector: '[dirA]'}) class DirectiveA { @@ -1781,31 +1760,6 @@ describe('di', () => { expect(dirString.s).toBe('Foo'); }); - it('should find host component on the host itself', () => { - @Directive({selector: '[dirComp]'}) - class DirectiveComp { - constructor(@Inject(forwardRef(() => MyComp)) @Host() public comp: MyComp) {} - } - - @Component({selector: 'my-comp', template: '
'}) - class MyComp { - @ViewChild(DirectiveComp) dirComp!: DirectiveComp; - } - - @Component({template: ''}) - class MyApp { - @ViewChild(MyComp) myComp!: MyComp; - } - - TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]}); - const fixture = TestBed.createComponent(MyApp); - fixture.detectChanges(); - - const myComp = fixture.componentInstance.myComp; - const dirComp = myComp.dirComp; - expect(dirComp.comp).toBe(myComp); - }); - it('should not find providers on the host itself', () => { @Component({ selector: 'my-comp', @@ -1882,19 +1836,19 @@ describe('di', () => { }); it('should not find component above the host', () => { + @Component({template: ''}) + class MyApp { + } + @Directive({selector: '[dirComp]'}) class DirectiveComp { - constructor(@Inject(forwardRef(() => MyApp)) @Host() public comp: MyApp) {} + constructor(@Host() public comp: MyApp) {} } @Component({selector: 'my-comp', template: '
'}) class MyComp { } - @Component({template: ''}) - class MyApp { - } - TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]}); expect(() => TestBed.createComponent(MyApp)) .toThrowError( diff --git a/packages/core/test/acceptance/property_binding_spec.ts b/packages/core/test/acceptance/property_binding_spec.ts index 3a5b9645a34..a6cada6e8fc 100644 --- a/packages/core/test/acceptance/property_binding_spec.ts +++ b/packages/core/test/acceptance/property_binding_spec.ts @@ -510,7 +510,12 @@ describe('property bindings', () => { const fixture = TestBed.createComponent(App); const myDir = fixture.debugElement.query(By.directive(MyDir)).injector.get(MyDir); const myDirB = fixture.debugElement.query(By.directive(MyDirB)).injector.get(MyDirB); - const [buttonEl, listboxEl] = fixture.nativeElement.children; + const fixtureElements = fixture.nativeElement.children; + + // TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used. + const buttonEl = fixtureElements[0]; + const listboxEl = fixtureElements[1]; + fixture.detectChanges(); expect(buttonEl.getAttribute('role')).toBe('button'); @@ -581,7 +586,11 @@ describe('property bindings', () => { expect(fixture.nativeElement.children.length).toBe(2); - const [comp1, comp2] = fixture.nativeElement.children; + const compElements = fixture.nativeElement.children; + + // TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used. + const comp1 = compElements[0]; + const comp2 = compElements[1]; expect(comp1.tagName).toBe('COMP'); expect(comp2.tagName).toBe('COMP'); diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts index 032ed10a109..7fecb884fba 100644 --- a/packages/core/test/acceptance/styling_spec.ts +++ b/packages/core/test/acceptance/styling_spec.ts @@ -60,8 +60,8 @@ describe('styling', () => { TestBed.configureTestingModule({declarations: [Cmp]}); const fixture = TestBed.createComponent(Cmp); + const staticDiv = fixture.nativeElement.querySelectorAll('div')[0]; - const [staticDiv] = fixture.nativeElement.querySelectorAll('div'); expect(getSortedClassName(staticDiv)).toEqual('STATIC'); expect(getSortedStyle(staticDiv)).toEqual('color: blue;'); }); @@ -335,7 +335,12 @@ describe('styling', () => { const fixture = TestBed.createComponent(Cmp); fixture.detectChanges(); - const [div1, div2] = fixture.nativeElement.querySelectorAll('div'); + const divs = fixture.nativeElement.querySelectorAll('div'); + + // TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used. + const div1 = divs[0]; + const div2 = divs[1]; + // Static value `class="s1"` is always written to the DOM. expect(div1.className).toEqual('s1'); expect(div1.getAttribute('shadow-class')).toEqual('s1 d1'); @@ -367,7 +372,12 @@ describe('styling', () => { const fixture = TestBed.createComponent(Cmp); fixture.detectChanges(); - const [divStatic, divBinding] = fixture.nativeElement.querySelectorAll('div'); + const divs = fixture.nativeElement.querySelectorAll('div'); + + // TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used. + const divStatic = divs[0]; + const divBinding = divs[1]; + expectClass(divStatic).toEqual({'DIRECTIVE': true, 's1': true}); expect(divStatic.getAttribute('shadow-class')).toEqual('s1'); @@ -398,7 +408,12 @@ describe('styling', () => { const fixture = TestBed.createComponent(Cmp); fixture.detectChanges(); - const [divStatic, divBinding] = fixture.nativeElement.querySelectorAll('div'); + const divs = fixture.nativeElement.querySelectorAll('div'); + + // TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used. + const divStatic = divs[0]; + const divBinding = divs[1]; + expectStyle(divStatic).toEqual({'color': 'red', 'width': '1px'}); expect(divStatic.getAttribute('shadow-style')).toEqual('width: 1px;'); @@ -2451,11 +2466,10 @@ describe('styling', () => { const items = fixture.nativeElement.querySelectorAll('.item'); expect(items.length).toEqual(4); - const [a, b, c, d] = items; - expect(a.style.height).toEqual('0px'); - expect(b.style.height).toEqual('100px'); - expect(c.style.height).toEqual('200px'); - expect(d.style.height).toEqual('300px'); + expect(items[0].style.height).toEqual('0px'); + expect(items[1].style.height).toEqual('100px'); + expect(items[2].style.height).toEqual('200px'); + expect(items[3].style.height).toEqual('300px'); const section = fixture.nativeElement.querySelector('section'); const p = fixture.nativeElement.querySelector('p'); @@ -3342,7 +3356,11 @@ describe('styling', () => { const fixture = TestBed.createComponent(MyComp); fixture.detectChanges(); - const [div1, div2] = fixture.nativeElement.querySelectorAll('div') as HTMLDivElement[]; + const divs = fixture.nativeElement.querySelectorAll('div') as HTMLDivElement[]; + + // TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used. + const div1 = divs[0]; + const div2 = divs[1]; expect(div1.className).toBe(''); expect(div2.className).toBe('');