diff --git a/packages/core/test/acceptance/pipe_spec.ts b/packages/core/test/acceptance/pipe_spec.ts index c264fd27b68..51f1260a26d 100644 --- a/packages/core/test/acceptance/pipe_spec.ts +++ b/packages/core/test/acceptance/pipe_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Directive, Inject, Injectable, InjectionToken, Input, NgModule, OnChanges, OnDestroy, Pipe, PipeTransform, SimpleChanges, ViewChild} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Directive, Inject, Injectable, InjectionToken, Input, NgModule, OnChanges, OnDestroy, Pipe, PipeTransform, SimpleChanges, ViewChild, ɵɵdefineInjectable, ɵɵdefinePipe, ɵɵgetInheritedFactory, ɵɵinject} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -271,6 +271,54 @@ describe('pipe', () => { expect(fixture.nativeElement).toHaveText('a'); }); + // This test uses AOT-generated code, because we can't capture the same behavior that we want + // when going through `TestBed`. Here we're testing the behavior of AOT-compiled code which + // differs from the JIT code in `TestBed`, because it includes a `ɵɵgetInheritedFactory` call + // when the pipe is using inheritance. + it('should be able to use DI in a Pipe that extends an Injectable', () => { + @Injectable({providedIn: 'root'}) + class SayHelloService { + getHello() { + return 'Hello there'; + } + } + + // The generated code corresponds to the following decorator: + // @Injectable() + class ParentPipe { + constructor(protected sayHelloService: SayHelloService) {} + + static ɵfac = (t?: any) => new(t || ParentPipe)(ɵɵinject(SayHelloService)); + static ɵprov = ɵɵdefineInjectable({token: ParentPipe, factory: ParentPipe.ɵfac}); + } + + // The generated code corresponds to the following decorator: + // @Pipe({name: 'sayHello', pure: true, standalone: true}) + class SayHelloPipe extends ParentPipe implements PipeTransform { + transform() { + return this.sayHelloService.getHello(); + } + + static override ɵfac = (t?: any) => ɵɵgetInheritedFactory(t || SayHelloPipe)(SayHelloPipe); + static ɵpipe = + ɵɵdefinePipe({name: 'sayHello', type: SayHelloPipe, pure: true, standalone: true}); + } + + @Component({ + standalone: true, + selector: 'app', + template: '{{ value | sayHello }}', + imports: [SayHelloPipe] + }) + class AppComponent { + value = 'test'; + } + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + + expect(fixture.nativeElement.textContent).toBe('Hello there'); + }); + describe('pure', () => { it('should call pure pipes only if the arguments change', () => { @Component({ diff --git a/packages/core/test/render3/pipe_spec.ts b/packages/core/test/render3/pipe_spec.ts deleted file mode 100644 index 0226ebf0092..00000000000 --- a/packages/core/test/render3/pipe_spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @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 {Injectable as _Injectable, Pipe as _Pipe, PipeTransform, ɵɵdefineInjectable, ɵɵdefinePipe, ɵɵgetInheritedFactory, ɵɵinject} from '@angular/core'; -import {expect} from '@angular/platform-browser/testing/src/matchers'; - -import {ɵɵtext, ɵɵtextInterpolate1} from '../../src/render3/instructions/all'; -import {ɵɵpipe, ɵɵpipeBind1} from '../../src/render3/pipe'; - -import {TemplateFixture} from './render_util'; - -const Pipe: typeof _Pipe = function(...args: any[]): any { - // In test we use @Pipe for documentation only so it's safe to mock out the implementation. - return () => undefined; -} as any; - -const Injectable: typeof _Injectable = function(...args: any[]): any { - // In test we use @Injectable for documentation only so it's safe to mock out the implementation. - return () => undefined; -} as any; - - -describe('pipe', () => { - // This test isn't in `acceptance`, because we can't capture the same behavior that we want - // when going through `TestBed`. Here we're testing the behavior of AOT-compiled code which - // differs from the JIT code in `TestBed`, because it includes a `ɵɵgetInheritedFactory` call - // when the pipe is using inheritance. - it('should be able to use DI in a Pipe that extends an Injectable', () => { - @Injectable({providedIn: 'root'}) - class SayHelloService { - getHello() { - return 'Hello there'; - } - static ɵfac = () => new SayHelloService(); - static ɵprov = ɵɵdefineInjectable( - {token: SayHelloService, factory: SayHelloService.ɵfac, providedIn: 'root'}); - } - - @Injectable() - class ParentPipe { - constructor(protected sayHelloService: SayHelloService) {} - static ɵfac = (t?: any) => new(t || ParentPipe)(ɵɵinject(SayHelloService)); - static ɵprov = ɵɵdefineInjectable({token: ParentPipe, factory: ParentPipe.ɵfac}); - } - - @Pipe({name: 'sayHello', pure: true}) - class SayHelloPipe extends ParentPipe implements PipeTransform { - transform() { - return this.sayHelloService.getHello(); - } - static override ɵfac = (t?: any) => ɵɵgetInheritedFactory(t || SayHelloPipe)(SayHelloPipe); - static ɵpipe = ɵɵdefinePipe({name: 'sayHello', type: SayHelloPipe, pure: true}); - } - - const fixture = new TemplateFixture({ - create: () => { - ɵɵtext(0); - ɵɵpipe(1, 'sayHello'); - }, - update: () => { - ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), ''); - }, - decls: 2, - vars: 3, - pipes: [SayHelloPipe] - }); - - expect(fixture.html).toBe('Hello there'); - }); -});