Skip to content

Commit

Permalink
fix: supporting pipes in providers
Browse files Browse the repository at this point in the history
closes #218
  • Loading branch information
satanTime committed Oct 30, 2020
1 parent ea332f8 commit 6e252e8
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 13 deletions.
46 changes: 33 additions & 13 deletions lib/mock-helper/mock-helper.guts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,39 @@ import { MockDirective } from '../mock-directive/mock-directive';
import { MockModule, MockProvider } from '../mock-module/mock-module';
import { MockPipe } from '../mock-pipe/mock-pipe';

export default (keep: any, mock?: any): TestModuleMetadata => {
export default (keep: any, mock: any = null): TestModuleMetadata => {
const declarations: any[] = [];
const imports: any[] = [];
const providers: any[] = [];

const keepFlat: any[] = flatten(keep);
const mockFlat: any[] = mock ? flatten(mock) : [];
const keepFlat: any[] = [];
for (const def of flatten(keep)) {
if (keepFlat.indexOf(def) === -1) {
keepFlat.push(def);
}
}

const mockFlat: any[] = [];
for (const def of mock ? flatten(mock) : []) {
if (mockFlat.indexOf(def) === -1) {
mockFlat.push(def);
}
}

const skip: any[] = [];

const resolveProvider = (def: any): void => {
const provider = typeof def === 'object' && def.provide ? def.provide : def;
if (skip.indexOf(provider) === -1) {
skip.push(provider);
}

const providerDef = keepFlat.indexOf(provider) === -1 ? MockProvider(def) : def;
if (providerDef) {
providers.push(providerDef);
}
};

const resolve = (def: any, skipDestruction = true): void => {
if (!def) {
return;
Expand Down Expand Up @@ -68,9 +92,12 @@ export default (keep: any, mock?: any): TestModuleMetadata => {
throw new Error('ng-mocks is not in JIT mode and cannot resolve declarations');
}

for (const toMock of flatten([meta.declarations, meta.imports, meta.providers])) {
for (const toMock of flatten([meta.declarations, meta.imports])) {
resolve(toMock);
}
for (const toMock of flatten(meta.providers)) {
resolveProvider(toMock);
}
return;
}

Expand Down Expand Up @@ -104,15 +131,7 @@ export default (keep: any, mock?: any): TestModuleMetadata => {
return;
}

const provider = typeof def === 'object' && def.provide ? def.provide : def;
if (!isNgInjectionToken(provider) && skip.indexOf(provider) !== -1) {
return;
}
skip.push(provider);
const providerDef = keepFlat.indexOf(provider) === -1 ? MockProvider(def) : def;
if (providerDef) {
providers.push(providerDef);
}
resolveProvider(def);
};

for (const def of mockFlat) {
Expand Down Expand Up @@ -141,6 +160,7 @@ export default (keep: any, mock?: any): TestModuleMetadata => {

if (isNgDef(def, 'p')) {
declarations.push(def);
providers.push(def);
continue;
}

Expand Down
7 changes: 7 additions & 0 deletions lib/mock-service/mock-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,13 @@ const mockServiceHelperPrototype = {
let mockedDef: typeof def;
if (resolutions.has(provider)) {
mockedDef = resolutions.get(provider);
// A case when a provider is actually a component, directive, pipe.
if (typeof mockedDef === 'function') {
mockedDef = {
provide: provider,
useClass: mockedDef,
};
}
return multi && typeof mockedDef === 'object' ? { ...mockedDef, multi } : mockedDef;
}

Expand Down
119 changes: 119 additions & 0 deletions tests/issue-218/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import {
Component,
Inject,
Injectable,
InjectionToken,
Input,
NgModule,
OnInit,
Pipe,
PipeTransform,
} from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { MockBuilder, MockRender, ngMocks } from 'ng-mocks';

const TOKEN_PREFIX = new InjectionToken('PREFIX');

@Injectable()
class PrefixService {
public readonly prefix: string;

constructor(@Inject(TOKEN_PREFIX) prefix: string) {
this.prefix = prefix;
}
}

@Pipe({
name: 'target',
})
class TargetPipe implements PipeTransform {
protected prefix: string;

constructor(service: PrefixService) {
this.prefix = service.prefix;
}

transform(value: string): string {
return `${this.prefix}:${value}`;
}
}

@Component({
selector: 'target',
template: `{{ value | target }} - {{ piped }}`,
})
class TargetComponent implements OnInit {
public piped: string;
@Input() value: string;

protected pipe: TargetPipe;

constructor(pipe: TargetPipe) {
this.pipe = pipe;
}

ngOnInit(): void {
this.piped = this.pipe.transform(this.value);
}
}

@NgModule({
declarations: [TargetComponent, TargetPipe],
providers: [
{
provide: TOKEN_PREFIX,
useValue: 'real',
},
PrefixService,
TargetPipe,
],
})
class TargetModule {}

describe('issue-218:real', () => {
beforeEach(() => MockBuilder(TargetComponent).keep(TargetModule));

it('renders transformed strings', () => {
const fixture = MockRender(TargetComponent, {
value: 'test',
});

expect(fixture.nativeElement.innerHTML).toContain('>real:test - real:test<');
});
});

describe('issue-218:mock-all', () => {
beforeEach(() => MockBuilder(TargetComponent, TargetModule));

it('renders emptiness', () => {
const fixture = MockRender(TargetComponent, {
value: 'test',
});

expect(fixture.nativeElement.innerHTML).toContain('> - <');
});
});

describe('issue-218:guts', () => {
beforeEach(() => TestBed.configureTestingModule(ngMocks.guts(TargetComponent, TargetModule)).compileComponents());

it('renders emptiness', () => {
const fixture = MockRender(TargetComponent, {
value: 'test',
});

expect(fixture.nativeElement.innerHTML).toContain('> - <');
});
});

describe('issue-218:keep-pipe', () => {
beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(TargetPipe));

it('renders how we mocked it', () => {
const fixture = MockRender(TargetComponent, {
value: 'test',
});

expect(fixture.nativeElement.innerHTML).toContain('>undefined:test - undefined:test<');
});
});

0 comments on commit 6e252e8

Please sign in to comment.