diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts index 9b026df04a..c94cfbd785 100644 --- a/packages/core/test/linker/integration_spec.ts +++ b/packages/core/test/linker/integration_spec.ts @@ -625,23 +625,22 @@ function declareTests(config?: {useJit: boolean}) { }); if (getDOM().supportsDOMEvents()) { - fixmeIvy('unknown').it( - 'should allow to destroy a component from within a host event handler', - fakeAsync(() => { - TestBed.configureTestingModule({declarations: [MyComp, [[PushCmpWithHostEvent]]]}); - const template = ''; - TestBed.overrideComponent(MyComp, {set: {template}}); - const fixture = TestBed.createComponent(MyComp); + it('should allow to destroy a component from within a host event handler', + fakeAsync(() => { + TestBed.configureTestingModule({declarations: [MyComp, [[PushCmpWithHostEvent]]]}); + const template = ''; + TestBed.overrideComponent(MyComp, {set: {template}}); + const fixture = TestBed.createComponent(MyComp); - tick(); - fixture.detectChanges(); + tick(); + fixture.detectChanges(); - const cmpEl = fixture.debugElement.children[0]; - const cmp: PushCmpWithHostEvent = cmpEl.injector.get(PushCmpWithHostEvent); - cmp.ctxCallback = (_: any) => fixture.destroy(); + const cmpEl = fixture.debugElement.children[0]; + const cmp: PushCmpWithHostEvent = cmpEl.injector.get(PushCmpWithHostEvent); + cmp.ctxCallback = (_: any) => fixture.destroy(); - expect(() => cmpEl.triggerEventHandler('click', {})).not.toThrow(); - })); + expect(() => cmpEl.triggerEventHandler('click', {})).not.toThrow(); + })); } fixmeIvy('FW-758: OnPush events not marking view dirty when using renderer2') diff --git a/packages/core/test/linker/ng_container_integration_spec.ts b/packages/core/test/linker/ng_container_integration_spec.ts index af516d6ed2..b0cf9c2e27 100644 --- a/packages/core/test/linker/ng_container_integration_spec.ts +++ b/packages/core/test/linker/ng_container_integration_spec.ts @@ -136,8 +136,8 @@ function declareTests(config?: {useJit: boolean}) { expect(dir.text).toEqual('container'); }); - fixmeIvy('unknown').it( - 'should contain all direct child directives in a (content dom)', () => { + fixmeIvy('FW-795: Queries with descendants: true don\'t descent into ') + .it('should contain all direct child directives in a (content dom)', () => { const template = '
'; TestBed.overrideComponent(MyComp, {set: {template}}); diff --git a/packages/core/test/linker/projection_integration_spec.ts b/packages/core/test/linker/projection_integration_spec.ts index ecaa9e2fe9..be24f38070 100644 --- a/packages/core/test/linker/projection_integration_spec.ts +++ b/packages/core/test/linker/projection_integration_spec.ts @@ -13,521 +13,510 @@ import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {fixmeIvy} from '@angular/private/testing'; -{ - describe('projection', () => { - beforeEach(() => TestBed.configureTestingModule({declarations: [MainComp, OtherComp, Simple]})); +describe('projection', () => { + beforeEach(() => TestBed.configureTestingModule({declarations: [MainComp, OtherComp, Simple]})); - it('should support simple components', () => { - const template = '
A
'; - TestBed.overrideComponent(MainComp, {set: {template}}); - const main = TestBed.createComponent(MainComp); + it('should support simple components', () => { + const template = '
A
'; + TestBed.overrideComponent(MainComp, {set: {template}}); + const main = TestBed.createComponent(MainComp); - expect(main.nativeElement).toHaveText('SIMPLE(A)'); - }); - - it('should support simple components with text interpolation as direct children', () => { - const template = '{{\'START(\'}}' + - '{{text}}' + - '{{\')END\'}}'; - TestBed.overrideComponent(MainComp, {set: {template}}); - const main = TestBed.createComponent(MainComp); - - main.componentInstance.text = 'A'; - main.detectChanges(); - expect(main.nativeElement).toHaveText('START(SIMPLE(A))END'); - }); - - it('should support projecting text interpolation to a non bound element', () => { - TestBed.overrideComponent( - Simple, {set: {template: 'SIMPLE(
)'}}); - TestBed.overrideComponent(MainComp, {set: {template: '{{text}}'}}); - const main = TestBed.createComponent(MainComp); - - main.componentInstance.text = 'A'; - main.detectChanges(); - expect(main.nativeElement).toHaveText('SIMPLE(A)'); - }); - - it('should support projecting text interpolation to a non bound element with other bound elements after it', - () => { - TestBed.overrideComponent(Simple, { - set: { - template: 'SIMPLE(
EL
)' - } - }); - TestBed.overrideComponent(MainComp, {set: {template: '{{text}}'}}); - const main = TestBed.createComponent(MainComp); - - main.componentInstance.text = 'A'; - main.detectChanges(); - expect(main.nativeElement).toHaveText('SIMPLE(AEL)'); - }); - - it('should project content components', () => { - TestBed.overrideComponent( - Simple, {set: {template: 'SIMPLE({{0}}||{{2}})'}}); - TestBed.overrideComponent(OtherComp, {set: {template: '{{1}}'}}); - TestBed.overrideComponent(MainComp, {set: {template: ''}}); - const main = TestBed.createComponent(MainComp); - - main.detectChanges(); - expect(main.nativeElement).toHaveText('SIMPLE(0|1|2)'); - }); - - it('should not show the light dom even if there is no content tag', () => { - TestBed.configureTestingModule({declarations: [Empty]}); - TestBed.overrideComponent(MainComp, {set: {template: 'A'}}); - const main = TestBed.createComponent(MainComp); - - expect(main.nativeElement).toHaveText(''); - }); - - fixmeIvy('FW-789: select attribute on should not be case-sensitive') - .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)'); - }); - - it('should redistribute only direct children', () => { - TestBed.configureTestingModule({declarations: [MultipleContentTagsComponent]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '' + - '
B
A
' + - '
C
' + - '
' - } - }); - const main = TestBed.createComponent(MainComp); - - expect(main.nativeElement).toHaveText('(, BAC)'); - }); - - fixmeIvy('FW-665: Unable to find the given context data for the given target') - .it('should redistribute direct child viewcontainers when the light dom changes', () => { - TestBed.configureTestingModule( - {declarations: [MultipleContentTagsComponent, ManualViewportDirective]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '' + - '
A1
' + - '
B
' + - '
' - } - }); - const main = TestBed.createComponent(MainComp); - - const viewportDirectives = main.debugElement.children[0] - .childNodes.filter(By.directive(ManualViewportDirective)) - .map(de => de.injector.get(ManualViewportDirective)); - - expect(main.nativeElement).toHaveText('(, B)'); - - viewportDirectives.forEach(d => d.show()); - main.detectChanges(); - expect(main.nativeElement).toHaveText('(A1, B)'); - - viewportDirectives.forEach(d => d.hide()); - main.detectChanges(); - expect(main.nativeElement).toHaveText('(, B)'); - }); - - it('should support nested components', () => { - TestBed.configureTestingModule({declarations: [OuterWithIndirectNestedComponent]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '' + - '
A
' + - '
B
' + - '
' - } - }); - const main = TestBed.createComponent(MainComp); - - expect(main.nativeElement).toHaveText('OUTER(SIMPLE(AB))'); - }); - - fixmeIvy('FW-665: Unable to find the given context data for the given target') - .it('should support nesting with content being direct child of a nested component', () => { - TestBed.configureTestingModule({ - declarations: - [InnerComponent, InnerInnerComponent, OuterComponent, ManualViewportDirective] - }); - TestBed.overrideComponent(MainComp, { - set: { - template: '' + - '
A
' + - '
B
' + - '
C
' + - '
' - } - }); - const main = TestBed.createComponent(MainComp); - - const viewportDirective = - main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0] - .injector.get(ManualViewportDirective); - - expect(main.nativeElement).toHaveText('OUTER(INNER(INNERINNER(,BC)))'); - viewportDirective.show(); - - expect(main.nativeElement).toHaveText('OUTER(INNER(INNERINNER(A,BC)))'); - }); - - fixmeIvy( - 'FW-745: Compiler isn\'t generating projectionDefs for tags inside ') - .it('should redistribute when the shadow dom changes', () => { - TestBed.configureTestingModule( - {declarations: [ConditionalContentComponent, ManualViewportDirective]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '' + - '
A
' + - '
B
' + - '
C
' + - '
' - } - }); - const main = TestBed.createComponent(MainComp); - - const viewportDirective = - main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0] - .injector.get(ManualViewportDirective); - - expect(main.nativeElement).toHaveText('(, BC)'); - - viewportDirective.show(); - main.detectChanges(); - expect(main.nativeElement).toHaveText('(A, BC)'); - - viewportDirective.hide(); - main.detectChanges(); - expect(main.nativeElement).toHaveText('(, BC)'); - }); - - // GH-2095 - https://github.com/angular/angular/issues/2095 - // important as we are removing the ng-content element during compilation, - // which could skrew up text node indices. - it('should support text nodes after content tags', () => { - TestBed.overrideComponent(MainComp, {set: {template: ''}}); - TestBed.overrideComponent( - Simple, {set: {template: '

P,

{{stringProp}}'}}); - const main = TestBed.createComponent(MainComp); - - main.detectChanges(); - - expect(main.nativeElement).toHaveText('P,text'); - }); - - // important as we are moving style tags around during compilation, - // which could skrew up text node indices. - it('should support text nodes after style tags', () => { - TestBed.overrideComponent(MainComp, {set: {template: ''}}); - TestBed.overrideComponent( - Simple, {set: {template: '

P,

{{stringProp}}'}}); - const main = TestBed.createComponent(MainComp); - - main.detectChanges(); - expect(main.nativeElement).toHaveText('P,text'); - }); - - it('should support moving non projected light dom around', () => { - let sourceDirective: ManualViewportDirective = undefined !; - - @Directive({selector: '[manual]'}) - class ManualViewportDirective { - constructor(public templateRef: TemplateRef) { sourceDirective = this; } - } - - TestBed.configureTestingModule( - {declarations: [Empty, ProjectDirective, ManualViewportDirective]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '' + - '
A
' + - '
' + - 'START(
)END' - } - }); - const main = TestBed.createComponent(MainComp); - - const projectDirective: ProjectDirective = - main.debugElement.queryAllNodes(By.directive(ProjectDirective))[0].injector.get( - ProjectDirective); - - expect(main.nativeElement).toHaveText('START()END'); - - projectDirective.show(sourceDirective.templateRef); - expect(main.nativeElement).toHaveText('START(A)END'); - }); - - it('should support moving projected light dom around', () => { - TestBed.configureTestingModule( - {declarations: [Empty, ProjectDirective, ManualViewportDirective]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '
A
' + - 'START(
)END' - } - }); - const main = TestBed.createComponent(MainComp); - - const sourceDirective: ManualViewportDirective = - main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( - ManualViewportDirective); - const projectDirective: ProjectDirective = - main.debugElement.queryAllNodes(By.directive(ProjectDirective))[0].injector.get( - ProjectDirective); - expect(main.nativeElement).toHaveText('SIMPLE()START()END'); - - projectDirective.show(sourceDirective.templateRef); - expect(main.nativeElement).toHaveText('SIMPLE()START(A)END'); - }); - - fixmeIvy('FW-665: Unable to find the given context data for the given target') - .it('should support moving ng-content around', () => { - TestBed.configureTestingModule({ - declarations: - [ConditionalContentComponent, ProjectDirective, ManualViewportDirective] - }); - TestBed.overrideComponent(MainComp, { - set: { - template: '' + - '
A
' + - '
B
' + - '
' + - 'START(
)END' - } - }); - const main = TestBed.createComponent(MainComp); - - const sourceDirective: ManualViewportDirective = - main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0] - .injector.get(ManualViewportDirective); - const projectDirective: ProjectDirective = - main.debugElement.queryAllNodes(By.directive(ProjectDirective))[0].injector.get( - ProjectDirective); - expect(main.nativeElement).toHaveText('(, B)START()END'); - - projectDirective.show(sourceDirective.templateRef); - expect(main.nativeElement).toHaveText('(, B)START(A)END'); - - // Stamping ng-content multiple times should not produce the content multiple - // times... - projectDirective.show(sourceDirective.templateRef); - expect(main.nativeElement).toHaveText('(, B)START(A)END'); - }); - - // Note: This does not use a ng-content element, but - // is still important as we are merging proto views independent of - // the presence of ng-content elements! - it('should still allow to implement a recursive trees', () => { - TestBed.configureTestingModule({declarations: [Tree, ManualViewportDirective]}); - TestBed.overrideComponent(MainComp, {set: {template: ''}}); - const main = TestBed.createComponent(MainComp); - - main.detectChanges(); - const manualDirective: ManualViewportDirective = - main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( - ManualViewportDirective); - expect(main.nativeElement).toHaveText('TREE(0:)'); - manualDirective.show(); - main.detectChanges(); - expect(main.nativeElement).toHaveText('TREE(0:TREE(1:))'); - }); - - // Note: This does not use a ng-content element, but - // is still important as we are merging proto views independent of - // the presence of ng-content elements! - it('should still allow to implement a recursive trees via multiple components', () => { - TestBed.configureTestingModule({declarations: [Tree, Tree2, ManualViewportDirective]}); - TestBed.overrideComponent(MainComp, {set: {template: ''}}); - TestBed.overrideComponent( - Tree, {set: {template: 'TREE({{depth}}:)'}}); - const main = TestBed.createComponent(MainComp); - - main.detectChanges(); - - expect(main.nativeElement).toHaveText('TREE(0:)'); - - const tree = main.debugElement.query(By.directive(Tree)); - let manualDirective: ManualViewportDirective = tree.queryAllNodes(By.directive( - ManualViewportDirective))[0].injector.get(ManualViewportDirective); - manualDirective.show(); - main.detectChanges(); - expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:))'); - - const tree2 = main.debugElement.query(By.directive(Tree2)); - manualDirective = tree2.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( - ManualViewportDirective); - manualDirective.show(); - main.detectChanges(); - expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:TREE(2:)))'); - }); - - if (getDOM().supportsNativeShadowDOM()) { - it('should support native content projection and isolate styles per component', () => { - TestBed.configureTestingModule({declarations: [SimpleNative1, SimpleNative2]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '
A
' + - '
B
' - } - }); - const main = TestBed.createComponent(MainComp); - - const childNodes = getDOM().childNodes(main.nativeElement); - expect(childNodes[0]).toHaveText('div {color: red}SIMPLE1(A)'); - expect(childNodes[1]).toHaveText('div {color: blue}SIMPLE2(B)'); - main.destroy(); - }); - } - - if (getDOM().supportsDOMEvents()) { - it('should support non emulated styles', () => { - TestBed.configureTestingModule({declarations: [OtherComp]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '
', - styles: ['.redStyle { color: red}'], - encapsulation: ViewEncapsulation.None, - } - }); - const main = TestBed.createComponent(MainComp); - - const mainEl = main.nativeElement; - const div1 = getDOM().firstChild(mainEl); - const div2 = getDOM().createElement('div'); - getDOM().setAttribute(div2, 'class', 'redStyle'); - getDOM().appendChild(mainEl, div2); - expect(getDOM().getComputedStyle(div1).color).toEqual('rgb(255, 0, 0)'); - expect(getDOM().getComputedStyle(div2).color).toEqual('rgb(255, 0, 0)'); - }); - - it('should support emulated style encapsulation', () => { - TestBed.configureTestingModule({declarations: [OtherComp]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '
', - styles: ['div { color: red}'], - encapsulation: ViewEncapsulation.Emulated, - } - }); - const main = TestBed.createComponent(MainComp); - - const mainEl = main.nativeElement; - const div1 = getDOM().firstChild(mainEl); - const div2 = getDOM().createElement('div'); - getDOM().appendChild(mainEl, div2); - expect(getDOM().getComputedStyle(div1).color).toEqual('rgb(255, 0, 0)'); - expect(getDOM().getComputedStyle(div2).color).toEqual('rgb(0, 0, 0)'); - }); - } - - fixmeIvy('FW-665: Unable to find the given context data for the given target') - .it('should support nested conditionals that contain ng-contents', () => { - TestBed.configureTestingModule( - {declarations: [ConditionalTextComponent, ManualViewportDirective]}); - TestBed.overrideComponent( - MainComp, {set: {template: `a`}}); - const main = TestBed.createComponent(MainComp); - - expect(main.nativeElement).toHaveText('MAIN()'); - - let viewportElement = - main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0]; - viewportElement.injector.get(ManualViewportDirective).show(); - expect(main.nativeElement).toHaveText('MAIN(FIRST())'); - - viewportElement = - main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[1]; - viewportElement.injector.get(ManualViewportDirective).show(); - expect(main.nativeElement).toHaveText('MAIN(FIRST(SECOND(a)))'); - }); - - it('should allow to switch the order of nested components via ng-content', () => { - TestBed.configureTestingModule({declarations: [CmpA, CmpB, CmpD, CmpC]}); - TestBed.overrideComponent(MainComp, {set: {template: ``}}); - const main = TestBed.createComponent(MainComp); - - main.detectChanges(); - expect(getDOM().getInnerHTML(main.nativeElement)) - .toEqual( - 'cmp-d' + - 'cmp-c'); - }); - - it('should create nested components in the right order', () => { - TestBed.configureTestingModule( - {declarations: [CmpA1, CmpA2, CmpB11, CmpB12, CmpB21, CmpB22]}); - TestBed.overrideComponent(MainComp, {set: {template: ``}}); - const main = TestBed.createComponent(MainComp); - - main.detectChanges(); - expect(getDOM().getInnerHTML(main.nativeElement)) - .toEqual( - 'a1b11b12' + - 'a2b21b22'); - }); - - fixmeIvy( - 'FW-745: Compiler isn\'t generating projectionDefs for tags inside ') - .it('should project filled view containers into a view container', () => { - TestBed.configureTestingModule( - {declarations: [ConditionalContentComponent, ManualViewportDirective]}); - TestBed.overrideComponent(MainComp, { - set: { - template: '' + - '
A
' + - 'B' + - '
C
' + - '
D
' + - '
' - } - }); - const main = TestBed.createComponent(MainComp); - - const conditionalComp = - main.debugElement.query(By.directive(ConditionalContentComponent)); - - const viewViewportDir = - conditionalComp.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( - ManualViewportDirective); - - expect(main.nativeElement).toHaveText('(, D)'); - expect(main.nativeElement).toHaveText('(, D)'); - - viewViewportDir.show(); - main.detectChanges(); - expect(main.nativeElement).toHaveText('(AC, D)'); - - const contentViewportDir = - conditionalComp.queryAllNodes(By.directive(ManualViewportDirective))[1].injector.get( - ManualViewportDirective); - - contentViewportDir.show(); - main.detectChanges(); - expect(main.nativeElement).toHaveText('(ABC, D)'); - - // hide view viewport, and test that it also hides - // the content viewport's views - viewViewportDir.hide(); - main.detectChanges(); - expect(main.nativeElement).toHaveText('(, D)'); - }); + expect(main.nativeElement).toHaveText('SIMPLE(A)'); }); -} + + it('should support simple components with text interpolation as direct children', () => { + const template = '{{\'START(\'}}' + + '{{text}}' + + '{{\')END\'}}'; + TestBed.overrideComponent(MainComp, {set: {template}}); + const main = TestBed.createComponent(MainComp); + + main.componentInstance.text = 'A'; + main.detectChanges(); + expect(main.nativeElement).toHaveText('START(SIMPLE(A))END'); + }); + + it('should support projecting text interpolation to a non bound element', () => { + TestBed.overrideComponent( + Simple, {set: {template: 'SIMPLE(
)'}}); + TestBed.overrideComponent(MainComp, {set: {template: '{{text}}'}}); + const main = TestBed.createComponent(MainComp); + + main.componentInstance.text = 'A'; + main.detectChanges(); + expect(main.nativeElement).toHaveText('SIMPLE(A)'); + }); + + it('should support projecting text interpolation to a non bound element with other bound elements after it', + () => { + TestBed.overrideComponent(Simple, { + set: { + template: 'SIMPLE(
EL
)' + } + }); + TestBed.overrideComponent(MainComp, {set: {template: '{{text}}'}}); + const main = TestBed.createComponent(MainComp); + + main.componentInstance.text = 'A'; + main.detectChanges(); + expect(main.nativeElement).toHaveText('SIMPLE(AEL)'); + }); + + it('should project content components', () => { + TestBed.overrideComponent( + Simple, {set: {template: 'SIMPLE({{0}}||{{2}})'}}); + TestBed.overrideComponent(OtherComp, {set: {template: '{{1}}'}}); + TestBed.overrideComponent(MainComp, {set: {template: ''}}); + const main = TestBed.createComponent(MainComp); + + main.detectChanges(); + expect(main.nativeElement).toHaveText('SIMPLE(0|1|2)'); + }); + + it('should not show the light dom even if there is no content tag', () => { + TestBed.configureTestingModule({declarations: [Empty]}); + TestBed.overrideComponent(MainComp, {set: {template: 'A'}}); + const main = TestBed.createComponent(MainComp); + + expect(main.nativeElement).toHaveText(''); + }); + + fixmeIvy('FW-789: select attribute on should not be case-sensitive') + .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)'); + }); + + it('should redistribute only direct children', () => { + TestBed.configureTestingModule({declarations: [MultipleContentTagsComponent]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '' + + '
B
A
' + + '
C
' + + '
' + } + }); + const main = TestBed.createComponent(MainComp); + + expect(main.nativeElement).toHaveText('(, BAC)'); + }); + + it('should redistribute direct child viewcontainers when the light dom changes', () => { + TestBed.configureTestingModule( + {declarations: [MultipleContentTagsComponent, ManualViewportDirective]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '' + + '
A1
' + + '
B
' + + '
' + } + }); + const main = TestBed.createComponent(MainComp); + + const viewportDirectives = main.debugElement.children[0] + .childNodes.filter(By.directive(ManualViewportDirective)) + .map(de => de.injector.get(ManualViewportDirective)); + + expect(main.nativeElement).toHaveText('(, B)'); + + viewportDirectives.forEach(d => d.show()); + main.detectChanges(); + expect(main.nativeElement).toHaveText('(A1, B)'); + + viewportDirectives.forEach(d => d.hide()); + main.detectChanges(); + expect(main.nativeElement).toHaveText('(, B)'); + }); + + it('should support nested components', () => { + TestBed.configureTestingModule({declarations: [OuterWithIndirectNestedComponent]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '' + + '
A
' + + '
B
' + + '
' + } + }); + const main = TestBed.createComponent(MainComp); + + expect(main.nativeElement).toHaveText('OUTER(SIMPLE(AB))'); + }); + + it('should support nesting with content being direct child of a nested component', () => { + TestBed.configureTestingModule({ + declarations: + [InnerComponent, InnerInnerComponent, OuterComponent, ManualViewportDirective] + }); + TestBed.overrideComponent(MainComp, { + set: { + template: '' + + '
A
' + + '
B
' + + '
C
' + + '
' + } + }); + const main = TestBed.createComponent(MainComp); + + const viewportDirective = + main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( + ManualViewportDirective); + + expect(main.nativeElement).toHaveText('OUTER(INNER(INNERINNER(,BC)))'); + viewportDirective.show(); + + expect(main.nativeElement).toHaveText('OUTER(INNER(INNERINNER(A,BC)))'); + }); + + fixmeIvy('FW-796: Content projection logic is incorrect for in nested templates') + .it('should redistribute when the shadow dom changes', () => { + TestBed.configureTestingModule( + {declarations: [ConditionalContentComponent, ManualViewportDirective]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '' + + '
A
' + + '
B
' + + '
C
' + + '
' + } + }); + const main = TestBed.createComponent(MainComp); + + const viewportDirective = + main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( + ManualViewportDirective); + + expect(main.nativeElement).toHaveText('(, BC)'); + + viewportDirective.show(); + main.detectChanges(); + expect(main.nativeElement).toHaveText('(A, BC)'); + + viewportDirective.hide(); + main.detectChanges(); + expect(main.nativeElement).toHaveText('(, BC)'); + }); + + // GH-2095 - https://github.com/angular/angular/issues/2095 + // important as we are removing the ng-content element during compilation, + // which could skrew up text node indices. + it('should support text nodes after content tags', () => { + TestBed.overrideComponent(MainComp, {set: {template: ''}}); + TestBed.overrideComponent( + Simple, {set: {template: '

P,

{{stringProp}}'}}); + const main = TestBed.createComponent(MainComp); + + main.detectChanges(); + + expect(main.nativeElement).toHaveText('P,text'); + }); + + // important as we are moving style tags around during compilation, + // which could skrew up text node indices. + it('should support text nodes after style tags', () => { + TestBed.overrideComponent(MainComp, {set: {template: ''}}); + TestBed.overrideComponent(Simple, {set: {template: '

P,

{{stringProp}}'}}); + const main = TestBed.createComponent(MainComp); + + main.detectChanges(); + expect(main.nativeElement).toHaveText('P,text'); + }); + + it('should support moving non projected light dom around', () => { + let sourceDirective: ManualViewportDirective = undefined !; + + @Directive({selector: '[manual]'}) + class ManualViewportDirective { + constructor(public templateRef: TemplateRef) { sourceDirective = this; } + } + + TestBed.configureTestingModule( + {declarations: [Empty, ProjectDirective, ManualViewportDirective]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '' + + '
A
' + + '
' + + 'START(
)END' + } + }); + const main = TestBed.createComponent(MainComp); + + const projectDirective: ProjectDirective = + main.debugElement.queryAllNodes(By.directive(ProjectDirective))[0].injector.get( + ProjectDirective); + + expect(main.nativeElement).toHaveText('START()END'); + + projectDirective.show(sourceDirective.templateRef); + expect(main.nativeElement).toHaveText('START(A)END'); + }); + + it('should support moving projected light dom around', () => { + TestBed.configureTestingModule( + {declarations: [Empty, ProjectDirective, ManualViewportDirective]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '
A
' + + 'START(
)END' + } + }); + const main = TestBed.createComponent(MainComp); + + const sourceDirective: ManualViewportDirective = + main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( + ManualViewportDirective); + const projectDirective: ProjectDirective = + main.debugElement.queryAllNodes(By.directive(ProjectDirective))[0].injector.get( + ProjectDirective); + expect(main.nativeElement).toHaveText('SIMPLE()START()END'); + + projectDirective.show(sourceDirective.templateRef); + expect(main.nativeElement).toHaveText('SIMPLE()START(A)END'); + }); + + fixmeIvy('FW-796: Content projection logic is incorrect for in nested templates') + .it('should support moving ng-content around', () => { + TestBed.configureTestingModule({ + declarations: [ConditionalContentComponent, ProjectDirective, ManualViewportDirective] + }); + TestBed.overrideComponent(MainComp, { + set: { + template: '' + + '
A
' + + '
B
' + + '
' + + 'START(
)END' + } + }); + const main = TestBed.createComponent(MainComp); + + const sourceDirective: ManualViewportDirective = + main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( + ManualViewportDirective); + const projectDirective: ProjectDirective = + main.debugElement.queryAllNodes(By.directive(ProjectDirective))[0].injector.get( + ProjectDirective); + expect(main.nativeElement).toHaveText('(, B)START()END'); + + projectDirective.show(sourceDirective.templateRef); + expect(main.nativeElement).toHaveText('(, B)START(A)END'); + + // Stamping ng-content multiple times should not produce the content multiple + // times... + projectDirective.show(sourceDirective.templateRef); + expect(main.nativeElement).toHaveText('(, B)START(A)END'); + }); + + // Note: This does not use a ng-content element, but + // is still important as we are merging proto views independent of + // the presence of ng-content elements! + it('should still allow to implement a recursive trees', () => { + TestBed.configureTestingModule({declarations: [Tree, ManualViewportDirective]}); + TestBed.overrideComponent(MainComp, {set: {template: ''}}); + const main = TestBed.createComponent(MainComp); + + main.detectChanges(); + const manualDirective: ManualViewportDirective = + main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( + ManualViewportDirective); + expect(main.nativeElement).toHaveText('TREE(0:)'); + manualDirective.show(); + main.detectChanges(); + expect(main.nativeElement).toHaveText('TREE(0:TREE(1:))'); + }); + + // Note: This does not use a ng-content element, but + // is still important as we are merging proto views independent of + // the presence of ng-content elements! + it('should still allow to implement a recursive trees via multiple components', () => { + TestBed.configureTestingModule({declarations: [Tree, Tree2, ManualViewportDirective]}); + TestBed.overrideComponent(MainComp, {set: {template: ''}}); + TestBed.overrideComponent( + Tree, {set: {template: 'TREE({{depth}}:)'}}); + const main = TestBed.createComponent(MainComp); + + main.detectChanges(); + + expect(main.nativeElement).toHaveText('TREE(0:)'); + + const tree = main.debugElement.query(By.directive(Tree)); + let manualDirective: ManualViewportDirective = tree.queryAllNodes(By.directive( + ManualViewportDirective))[0].injector.get(ManualViewportDirective); + manualDirective.show(); + main.detectChanges(); + expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:))'); + + const tree2 = main.debugElement.query(By.directive(Tree2)); + manualDirective = tree2.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( + ManualViewportDirective); + manualDirective.show(); + main.detectChanges(); + expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:TREE(2:)))'); + }); + + if (getDOM().supportsNativeShadowDOM()) { + it('should support native content projection and isolate styles per component', () => { + TestBed.configureTestingModule({declarations: [SimpleNative1, SimpleNative2]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '
A
' + + '
B
' + } + }); + const main = TestBed.createComponent(MainComp); + + const childNodes = getDOM().childNodes(main.nativeElement); + expect(childNodes[0]).toHaveText('div {color: red}SIMPLE1(A)'); + expect(childNodes[1]).toHaveText('div {color: blue}SIMPLE2(B)'); + main.destroy(); + }); + } + + if (getDOM().supportsDOMEvents()) { + it('should support non emulated styles', () => { + TestBed.configureTestingModule({declarations: [OtherComp]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '
', + styles: ['.redStyle { color: red}'], + encapsulation: ViewEncapsulation.None, + } + }); + const main = TestBed.createComponent(MainComp); + + const mainEl = main.nativeElement; + const div1 = getDOM().firstChild(mainEl); + const div2 = getDOM().createElement('div'); + getDOM().setAttribute(div2, 'class', 'redStyle'); + getDOM().appendChild(mainEl, div2); + expect(getDOM().getComputedStyle(div1).color).toEqual('rgb(255, 0, 0)'); + expect(getDOM().getComputedStyle(div2).color).toEqual('rgb(255, 0, 0)'); + }); + + it('should support emulated style encapsulation', () => { + TestBed.configureTestingModule({declarations: [OtherComp]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '
', + styles: ['div { color: red}'], + encapsulation: ViewEncapsulation.Emulated, + } + }); + const main = TestBed.createComponent(MainComp); + + const mainEl = main.nativeElement; + const div1 = getDOM().firstChild(mainEl); + const div2 = getDOM().createElement('div'); + getDOM().appendChild(mainEl, div2); + expect(getDOM().getComputedStyle(div1).color).toEqual('rgb(255, 0, 0)'); + expect(getDOM().getComputedStyle(div2).color).toEqual('rgb(0, 0, 0)'); + }); + } + + fixmeIvy('FW-796: Content projection logic is incorrect for in nested templates') + .it('should support nested conditionals that contain ng-contents', () => { + TestBed.configureTestingModule( + {declarations: [ConditionalTextComponent, ManualViewportDirective]}); + TestBed.overrideComponent( + MainComp, {set: {template: `a`}}); + const main = TestBed.createComponent(MainComp); + + expect(main.nativeElement).toHaveText('MAIN()'); + + let viewportElement = + main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0]; + viewportElement.injector.get(ManualViewportDirective).show(); + expect(main.nativeElement).toHaveText('MAIN(FIRST())'); + + viewportElement = main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[1]; + viewportElement.injector.get(ManualViewportDirective).show(); + expect(main.nativeElement).toHaveText('MAIN(FIRST(SECOND(a)))'); + }); + + it('should allow to switch the order of nested components via ng-content', () => { + TestBed.configureTestingModule({declarations: [CmpA, CmpB, CmpD, CmpC]}); + TestBed.overrideComponent(MainComp, {set: {template: ``}}); + const main = TestBed.createComponent(MainComp); + + main.detectChanges(); + expect(getDOM().getInnerHTML(main.nativeElement)) + .toEqual( + 'cmp-d' + + 'cmp-c'); + }); + + it('should create nested components in the right order', () => { + TestBed.configureTestingModule({declarations: [CmpA1, CmpA2, CmpB11, CmpB12, CmpB21, CmpB22]}); + TestBed.overrideComponent(MainComp, {set: {template: ``}}); + const main = TestBed.createComponent(MainComp); + + main.detectChanges(); + expect(getDOM().getInnerHTML(main.nativeElement)) + .toEqual( + 'a1b11b12' + + 'a2b21b22'); + }); + + fixmeIvy('FW-796: Content projection logic is incorrect for in nested templates') + .it('should project filled view containers into a view container', () => { + TestBed.configureTestingModule( + {declarations: [ConditionalContentComponent, ManualViewportDirective]}); + TestBed.overrideComponent(MainComp, { + set: { + template: '' + + '
A
' + + 'B' + + '
C
' + + '
D
' + + '
' + } + }); + const main = TestBed.createComponent(MainComp); + + const conditionalComp = main.debugElement.query(By.directive(ConditionalContentComponent)); + + const viewViewportDir = + conditionalComp.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get( + ManualViewportDirective); + + expect(main.nativeElement).toHaveText('(, D)'); + expect(main.nativeElement).toHaveText('(, D)'); + + viewViewportDir.show(); + main.detectChanges(); + expect(main.nativeElement).toHaveText('(AC, D)'); + + const contentViewportDir = + conditionalComp.queryAllNodes(By.directive(ManualViewportDirective))[1].injector.get( + ManualViewportDirective); + + contentViewportDir.show(); + main.detectChanges(); + expect(main.nativeElement).toHaveText('(ABC, D)'); + + // hide view viewport, and test that it also hides + // the content viewport's views + viewViewportDir.hide(); + main.detectChanges(); + expect(main.nativeElement).toHaveText('(, D)'); + }); +}); @Component({selector: 'main', template: ''}) class MainComp { diff --git a/packages/core/test/linker/query_integration_spec.ts b/packages/core/test/linker/query_integration_spec.ts index 7168b4ce59..06bfb89dd2 100644 --- a/packages/core/test/linker/query_integration_spec.ts +++ b/packages/core/test/linker/query_integration_spec.ts @@ -9,7 +9,7 @@ import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, ContentChild, ContentChildren, Directive, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, asNativeElements} from '@angular/core'; import {ComponentFixture, TestBed, async} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; -import {fixmeIvy, modifiedInIvy} from '@angular/private/testing'; +import {fixmeIvy, ivyEnabled, modifiedInIvy} from '@angular/private/testing'; import {Subject} from 'rxjs'; import {stringify} from '../../src/util'; @@ -637,35 +637,66 @@ describe('Query API', () => { expect(q.query.length).toBe(0); }); - fixmeIvy('unknown').it( - 'should not throw if a content template is queried and created in the view during change detection', - () => { - @Component( - {selector: 'auto-projecting', template: '
'}) - class AutoProjecting { - // TODO(issue/24571): - // remove '!'. - @ContentChild(TemplateRef) - content !: TemplateRef; + modifiedInIvy('https://github.com/angular/angular/issues/15117 fixed in ivy') + .it('should not throw if a content template is queried and created in the view during change detection', + () => { + @Component({ + selector: 'auto-projecting', + template: '
' + }) + class AutoProjecting { + // TODO(issue/24571): + // remove '!'. + @ContentChild(TemplateRef) + content !: TemplateRef; - // TODO(issue/24571): - // remove '!'. - @ContentChildren(TextDirective) - query !: QueryList; - } + // TODO(issue/24571): + // remove '!'. + @ContentChildren(TextDirective) + query !: QueryList; + } - TestBed.configureTestingModule({declarations: [AutoProjecting]}); - const template = - '
'; - const view = createTestCmpAndDetectChanges(MyComp0, template); + TestBed.configureTestingModule({declarations: [AutoProjecting]}); + const template = + '
'; + const view = createTestCmpAndDetectChanges(MyComp0, template); - const q = view.debugElement.children[0].references !['q']; - // This should be 1, but due to - // https://github.com/angular/angular/issues/15117 - // this is 0. - expect(q.query.length).toBe(0); - }); + const q = view.debugElement.children[0].references !['q']; + // This should be 1, but due to + // https://github.com/angular/angular/issues/15117 + // this is 0. + expect(q.query.length).toBe(0); + }); + if (ivyEnabled) { + // The fixed version of the "should not throw if a content template is queried and created in + // the view during change detection" test. This test is a different as ivy fixes + // https://github.com/angular/angular/issues/15117 present in the view engine. + it('should not throw if a content template is queried and created in the view during change detection - fixed in ivy', + () => { + @Component( + {selector: 'auto-projecting', template: '
'}) + class AutoProjecting { + // TODO(issue/24571): + // remove '!'. + @ContentChild(TemplateRef) + content !: TemplateRef; + + // TODO(issue/24571): + // remove '!'. + @ContentChildren(TextDirective) + query !: QueryList; + } + + TestBed.configureTestingModule({declarations: [AutoProjecting]}); + const template = + '
'; + const view = createTestCmpAndDetectChanges(MyComp0, template); + + const q = view.debugElement.children[0].references !['q']; + expect(q.query.length).toBe(1); + }); + } }); }); diff --git a/packages/core/test/linker/regression_integration_spec.ts b/packages/core/test/linker/regression_integration_spec.ts index 9b36d734a5..aee4ea8f01 100644 --- a/packages/core/test/linker/regression_integration_spec.ts +++ b/packages/core/test/linker/regression_integration_spec.ts @@ -32,15 +32,16 @@ function declareTests(config?: {useJit: boolean}) { describe('platform pipes', () => { beforeEach(() => { TestBed.configureCompiler({...config}); }); - fixmeIvy('unknown').it('should overwrite them by custom pipes', () => { - TestBed.configureTestingModule({declarations: [CustomPipe]}); - const template = '{{true | somePipe}}'; - TestBed.overrideComponent(MyComp1, {set: {template}}); - const fixture = TestBed.createComponent(MyComp1); + fixmeIvy('FW-798: Handle pipes with duplicate names') + .it('should overwrite them by custom pipes', () => { + TestBed.configureTestingModule({declarations: [CustomPipe]}); + const template = '{{true | somePipe}}'; + TestBed.overrideComponent(MyComp1, {set: {template}}); + const fixture = TestBed.createComponent(MyComp1); - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('someCustomPipe'); - }); + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('someCustomPipe'); + }); }); describe('expressions', () => { @@ -351,36 +352,37 @@ function declareTests(config?: {useJit: boolean}) { }); }); - fixmeIvy('unknown').it( - 'should support @ContentChild and @Input on the same property for static queries', () => { - @Directive({selector: 'test'}) - class Test { - // TODO(issue/24571): remove '!'. - @Input() @ContentChild(TemplateRef) tpl !: TemplateRef; - } + fixmeIvy('FW-797: @ContentChildren results are assigned after @Input bindings') + .it('should support @ContentChild and @Input on the same property for static queries', + () => { + @Directive({selector: 'test'}) + class Test { + // TODO(issue/24571): remove '!'. + @Input() @ContentChild(TemplateRef) tpl !: TemplateRef; + } - @Component({ - selector: 'my-app', - template: ` + @Component({ + selector: 'my-app', + template: `
Custom as a child
Custom as a binding
` - }) - class App { - } + }) + class App { + } - const fixture = - TestBed.configureTestingModule({declarations: [App, Test]}).createComponent(App); - fixture.detectChanges(); + const fixture = + TestBed.configureTestingModule({declarations: [App, Test]}).createComponent(App); + fixture.detectChanges(); - const testDirs = - fixture.debugElement.queryAll(By.directive(Test)).map(el => el.injector.get(Test)); - expect(testDirs[0].tpl).toBeUndefined(); - expect(testDirs[1].tpl).toBeDefined(); - expect(testDirs[2].tpl).toBeDefined(); - }); + const testDirs = fixture.debugElement.queryAll(By.directive(Test)) + .map(el => el.injector.get(Test)); + expect(testDirs[0].tpl).toBeUndefined(); + expect(testDirs[1].tpl).toBeDefined(); + expect(testDirs[2].tpl).toBeDefined(); + }); it('should not add ng-version for dynamically created components', () => { @Component({template: ''}) diff --git a/packages/core/test/linker/source_map_integration_node_only_spec.ts b/packages/core/test/linker/source_map_integration_node_only_spec.ts index 863f68fc73..2dd4f7b63e 100644 --- a/packages/core/test/linker/source_map_integration_node_only_spec.ts +++ b/packages/core/test/linker/source_map_integration_node_only_spec.ts @@ -102,167 +102,167 @@ import {fixmeIvy} from '@angular/private/testing'; function declareTests( {ngUrl, templateDecorator}: {ngUrl: string, templateDecorator: (template: string) => { [key: string]: any }}) { - fixmeIvy('unknown').it( - 'should use the right source url in html parse errors', fakeAsync(() => { - @Component({...templateDecorator('
\n ')}) - class MyComp { - } - - expect(() => compileAndCreateComponent(MyComp)) - .toThrowError( - new RegExp(`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:2`)); - })); - - fixmeIvy('unknown').it( - 'should use the right source url in template parse errors', fakeAsync(() => { - @Component({...templateDecorator('
\n
')}) - class MyComp { - } - - expect(() => compileAndCreateComponent(MyComp)) - .toThrowError( - new RegExp(`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:7`)); - })); - - fixmeIvy('unknown').it('should create a sourceMap for templates', fakeAsync(() => { - const template = `Hello World!`; - - @Component({...templateDecorator(template)}) - class MyComp { - } - - compileAndCreateComponent(MyComp); - - const sourceMap = - getSourceMap('ng:///DynamicTestModule/MyComp.ngfactory.js'); - expect(sourceMap.sources).toEqual([ - 'ng:///DynamicTestModule/MyComp.ngfactory.js', ngUrl - ]); - expect(sourceMap.sourcesContent).toEqual([' ', template]); - })); - - - fixmeIvy('unknown').it( - 'should report source location for di errors', fakeAsync(() => { - const template = `
\n
`; - - @Component({...templateDecorator(template)}) - class MyComp { - } - - @Directive({selector: '[someDir]'}) - class SomeDir { - constructor() { throw new Error('Test'); } - } - - TestBed.configureTestingModule({declarations: [SomeDir]}); - let error: any; - try { - compileAndCreateComponent(MyComp); - } catch (e) { - error = e; - } - // The error should be logged from the element - expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ - line: 2, - column: 4, - source: ngUrl, - }); - })); - - fixmeIvy('unknown').it( - 'should report di errors with multiple elements and directives', fakeAsync(() => { - const template = `
`; - - @Component({...templateDecorator(template)}) - class MyComp { - } - - @Directive({selector: '[someDir]'}) - class SomeDir { - constructor(@Attribute('someDir') someDir: string) { - if (someDir === 'throw') { - throw new Error('Test'); + fixmeIvy('FW-682: Compiler error handling') + .it('should use the right source url in html parse errors', fakeAsync(() => { + @Component({...templateDecorator('
\n ')}) + class MyComp { } - } - } - TestBed.configureTestingModule({declarations: [SomeDir]}); - let error: any; - try { - compileAndCreateComponent(MyComp); - } catch (e) { - error = e; - } - // The error should be logged from the 2nd-element - expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ - line: 1, - column: 19, - source: ngUrl, - }); - })); + expect(() => compileAndCreateComponent(MyComp)) + .toThrowError(new RegExp( + `Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:2`)); + })); - fixmeIvy('unknown').it( - 'should report source location for binding errors', fakeAsync(() => { - const template = `
\n
`; + fixmeIvy('FW-682: Compiler error handling') + .it('should use the right source url in template parse errors', fakeAsync(() => { + @Component({...templateDecorator('
\n
')}) + class MyComp { + } - @Component({...templateDecorator(template)}) - class MyComp { - createError() { throw new Error('Test'); } - } + expect(() => compileAndCreateComponent(MyComp)) + .toThrowError(new RegExp( + `Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:7`)); + })); - const comp = compileAndCreateComponent(MyComp); + fixmeIvy('FW-223: Generate source maps during template compilation') + .it('should create a sourceMap for templates', fakeAsync(() => { + const template = `Hello World!`; - let error: any; - try { - comp.detectChanges(); - } catch (e) { - error = e; - } - // the stack should point to the binding - expect(getSourcePositionForStack(error.stack)).toEqual({ - line: 2, - column: 12, - source: ngUrl, - }); - // The error should be logged from the element - expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ - line: 2, - column: 4, - source: ngUrl, - }); - })); + @Component({...templateDecorator(template)}) + class MyComp { + } - fixmeIvy('unknown').it( - 'should report source location for event errors', fakeAsync(() => { - const template = `
\n
`; + compileAndCreateComponent(MyComp); - @Component({...templateDecorator(template)}) - class MyComp { - createError() { throw new Error('Test'); } - } + const sourceMap = getSourceMap('ng:///DynamicTestModule/MyComp.ngfactory.js'); + expect(sourceMap.sources).toEqual([ + 'ng:///DynamicTestModule/MyComp.ngfactory.js', ngUrl + ]); + expect(sourceMap.sourcesContent).toEqual([' ', template]); + })); - const comp = compileAndCreateComponent(MyComp); - let error: any; - const errorHandler = TestBed.get(ErrorHandler); - spyOn(errorHandler, 'handleError').and.callFake((e: any) => error = e); - comp.debugElement.children[0].children[0].triggerEventHandler('click', 'EVENT'); - expect(error).toBeTruthy(); - // the stack should point to the binding - expect(getSourcePositionForStack(error.stack)).toEqual({ - line: 2, - column: 12, - source: ngUrl, - }); - // The error should be logged from the element - expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ - line: 2, - column: 4, - source: ngUrl, - }); + fixmeIvy('FW-223: Generate source maps during template compilation') + .it('should report source location for di errors', fakeAsync(() => { + const template = `
\n
`; - })); + @Component({...templateDecorator(template)}) + class MyComp { + } + + @Directive({selector: '[someDir]'}) + class SomeDir { + constructor() { throw new Error('Test'); } + } + + TestBed.configureTestingModule({declarations: [SomeDir]}); + let error: any; + try { + compileAndCreateComponent(MyComp); + } catch (e) { + error = e; + } + // The error should be logged from the element + expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ + line: 2, + column: 4, + source: ngUrl, + }); + })); + + fixmeIvy('FW-223: Generate source maps during template compilation') + .it('should report di errors with multiple elements and directives', fakeAsync(() => { + const template = `
`; + + @Component({...templateDecorator(template)}) + class MyComp { + } + + @Directive({selector: '[someDir]'}) + class SomeDir { + constructor(@Attribute('someDir') someDir: string) { + if (someDir === 'throw') { + throw new Error('Test'); + } + } + } + + TestBed.configureTestingModule({declarations: [SomeDir]}); + let error: any; + try { + compileAndCreateComponent(MyComp); + } catch (e) { + error = e; + } + // The error should be logged from the 2nd-element + expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ + line: 1, + column: 19, + source: ngUrl, + }); + })); + + fixmeIvy('FW-223: Generate source maps during template compilation') + .it('should report source location for binding errors', fakeAsync(() => { + const template = `
\n
`; + + @Component({...templateDecorator(template)}) + class MyComp { + createError() { throw new Error('Test'); } + } + + const comp = compileAndCreateComponent(MyComp); + + let error: any; + try { + comp.detectChanges(); + } catch (e) { + error = e; + } + // the stack should point to the binding + expect(getSourcePositionForStack(error.stack)).toEqual({ + line: 2, + column: 12, + source: ngUrl, + }); + // The error should be logged from the element + expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ + line: 2, + column: 4, + source: ngUrl, + }); + })); + + fixmeIvy('FW-223: Generate source maps during template compilation') + .it('should report source location for event errors', fakeAsync(() => { + const template = `
\n
`; + + @Component({...templateDecorator(template)}) + class MyComp { + createError() { throw new Error('Test'); } + } + + const comp = compileAndCreateComponent(MyComp); + + let error: any; + const errorHandler = TestBed.get(ErrorHandler); + spyOn(errorHandler, 'handleError').and.callFake((e: any) => error = e); + comp.debugElement.children[0].children[0].triggerEventHandler('click', 'EVENT'); + expect(error).toBeTruthy(); + // the stack should point to the binding + expect(getSourcePositionForStack(error.stack)).toEqual({ + line: 2, + column: 12, + source: ngUrl, + }); + // The error should be logged from the element + expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({ + line: 2, + column: 4, + source: ngUrl, + }); + + })); } }); }