Skip to content

Commit

Permalink
fix(core): respecting transform in mock pipes #4564
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Dec 20, 2022
1 parent a12f089 commit df51240
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 5 deletions.
3 changes: 2 additions & 1 deletion libs/ng-mocks/src/lib/common/mock.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EventEmitter, Injector, Optional, Self } from '@angular/core';
import { EventEmitter, Injector, Optional, PipeTransform, Self } from '@angular/core';

import { IMockBuilderConfig } from '../mock-builder/types';
import mockHelperStub from '../mock-helper/mock-helper.stub';
Expand Down Expand Up @@ -116,6 +116,7 @@ export type ngMocksMockConfig = {
outputs?: string[];
queryScanKeys?: string[];
setControlValueAccessor?: boolean;
transform?: PipeTransform['transform'];
};

const applyOverrides = (instance: any, mockOf: any, injector?: Injector): void => {
Expand Down
11 changes: 7 additions & 4 deletions libs/ng-mocks/src/lib/common/ng-mocks-global-overrides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ const configureTestingModule =
// 0b10 - mock exist
// 0b01 - real exist
let hasMocks = 0;
const mockBuilder: Array<[any, boolean]> = [];
const mockBuilder: Array<[any, any, boolean]> = [];
for (const key of useMockBuilder ? ['imports', 'declarations'] : []) {
for (const declaration of flatten(moduleDef[key as never]) as any[]) {
if (!declaration) {
Expand All @@ -205,19 +205,22 @@ const configureTestingModule =
providers: declaration.providers,
}
: getSourceOfMock(declaration),
isNgModuleDefWithProviders(declaration) ? declaration.ngModule : declaration,
isMockNgDef(funcGetType(declaration)),
]);
if (key === 'imports') {
hasMocks |= mockBuilder[mockBuilder.length - 1][1] ? 0b10 : 0b01;
hasMocks |= mockBuilder[mockBuilder.length - 1][2] ? 0b10 : 0b01;
}
}
}
// We should do magic only then both mock and real exist.
let finalModuleDef = hasMocks === 0b11 ? undefined : moduleDef;
if (!finalModuleDef) {
let builder = MockBuilder(NG_MOCKS_ROOT_PROVIDERS);
for (const [def, isMock] of mockBuilder) {
builder = isMock ? builder.mock(def) : builder.keep(def);
for (const [source, def, isMock] of mockBuilder) {
const transform = def.prototype.__ngMocksConfig?.transform;
builder =
isMock && transform ? builder.mock(source, transform) : isMock ? builder.mock(source) : builder.keep(source);
}
finalModuleDef = builder.build();
finalModuleDef = {
Expand Down
1 change: 1 addition & 0 deletions libs/ng-mocks/src/lib/mock-pipe/mock-pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const getMockClass = (pipe: Type<any>, transform?: PipeTransform['transform']):
helperMockService.mock(instance, 'transform', `${funcGetName(instance)}.transform`);
}
},
transform,
});

return mock;
Expand Down
112 changes: 112 additions & 0 deletions tests/issue-4564/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import {
Component,
CUSTOM_ELEMENTS_SCHEMA,
Inject,
Injectable,
InjectionToken,
NgModule,
Pipe,
PipeTransform,
} from '@angular/core';
import { TestBed } from '@angular/core/testing';

import {
MockModule,
MockPipe,
MockProvider,
MockRender,
ngMocks,
} from 'ng-mocks';

const TOKEN = new InjectionToken('TOKEN');

@Injectable()
class TargetService {
func() {
return 'real';
}
}

@Pipe({
name: 'target',
})
class TargetPipe implements PipeTransform {
transform() {
return 'real';
}
}

@Pipe({
name: 'standard',
})
class StandardPipe implements PipeTransform {
transform() {
return 'standard';
}
}

@NgModule({
declarations: [TargetPipe, StandardPipe],
exports: [TargetPipe, StandardPipe],
providers: [
{
provide: TOKEN,
useValue: 'real',
},
TargetService,
],
})
class PipeModule {}

@Component({
selector: 'target',
template: '{{ null | target }}:{{ token }}:{{ service.func() }}',
})
class TargetComponent {
constructor(
@Inject(TOKEN) public readonly token: string,
public readonly service: TargetService,
) {}
}

@NgModule({
imports: [PipeModule],
declarations: [TargetComponent],
exports: [TargetComponent],
})
class ComponentModule {}

@Component({
selector: 'sut',
template: '<custom-component><target></target></custom-component>',
})
class SubjectUnderTestComponent {}

// @see https://github.com/help-me-mom/ng-mocks/issues/4564
// mixed imports forget pipe customizations.
describe('issue-4564', () => {
ngMocks.throwOnConsole();

beforeEach(() =>
TestBed.configureTestingModule({
imports: [ComponentModule, MockModule(PipeModule)],
declarations: [
SubjectUnderTestComponent,
MockPipe(TargetPipe, () => 'mock'),
MockPipe(StandardPipe),
],
providers: [
MockProvider(TOKEN, 'mock'),
MockProvider(TargetService, {
func: () => 'mock',
}),
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents(),
);

it('customizes pipe', () => {
const fixture = MockRender(SubjectUnderTestComponent);
expect(ngMocks.formatText(fixture)).toEqual('mock:mock:mock');
});
});

0 comments on commit df51240

Please sign in to comment.