diff --git a/libs/ng-mocks/src/lib/common/func.import-exists.ts b/libs/ng-mocks/src/lib/common/func.import-exists.ts new file mode 100644 index 0000000000..eeb37dc9ef --- /dev/null +++ b/libs/ng-mocks/src/lib/common/func.import-exists.ts @@ -0,0 +1,5 @@ +export default (value: any, funcName: string) => { + if (value === undefined || value === null) { + throw new Error(`An empty parameter has been passed into ${funcName}. Please check that its import is correct.`); + } +}; diff --git a/libs/ng-mocks/src/lib/mock-component/mock-component.ts b/libs/ng-mocks/src/lib/mock-component/mock-component.ts index f111f31940..8a9e2ae511 100644 --- a/libs/ng-mocks/src/lib/mock-component/mock-component.ts +++ b/libs/ng-mocks/src/lib/mock-component/mock-component.ts @@ -18,6 +18,7 @@ import { extendClass } from '../common/core.helpers'; import coreReflectDirectiveResolve from '../common/core.reflect.directive-resolve'; import { Type } from '../common/core.types'; import { getMockedNgDefOf } from '../common/func.get-mocked-ng-def-of'; +import funcImportExists from '../common/func.import-exists'; import funcIsMock from '../common/func.is-mock'; import { MockConfig } from '../common/mock'; import { LegacyControlValueAccessor } from '../common/mock-control-value-accessor'; @@ -216,6 +217,8 @@ export function MockComponents(...components: Array>): Array(component: Type): Type> { + funcImportExists(component, 'MockComponent'); + // We are inside of an 'it'. It is fine to to return a mock copy. if ((getTestBed() as any)._instantiated) { try { diff --git a/libs/ng-mocks/src/lib/mock-directive/mock-directive.ts b/libs/ng-mocks/src/lib/mock-directive/mock-directive.ts index 3537679b7f..5f83eb4559 100644 --- a/libs/ng-mocks/src/lib/mock-directive/mock-directive.ts +++ b/libs/ng-mocks/src/lib/mock-directive/mock-directive.ts @@ -17,6 +17,7 @@ import { extendClass } from '../common/core.helpers'; import coreReflectDirectiveResolve from '../common/core.reflect.directive-resolve'; import { Type } from '../common/core.types'; import { getMockedNgDefOf } from '../common/func.get-mocked-ng-def-of'; +import funcImportExists from '../common/func.import-exists'; import { LegacyControlValueAccessor } from '../common/mock-control-value-accessor'; import ngMocksUniverse from '../common/ng-mocks-universe'; import decorateDeclaration from '../mock/decorate-declaration'; @@ -103,6 +104,8 @@ export function MockDirectives(...directives: Array>): Array(directive: Type): Type> { + funcImportExists(directive, 'MockDirective'); + // We are inside of an 'it'. // It is fine to to return a mock copy or to throw an exception if it was not replaced with its mock copy in TestBed. if ((getTestBed() as any)._instantiated) { diff --git a/libs/ng-mocks/src/lib/mock-instance/mock-instance.ts b/libs/ng-mocks/src/lib/mock-instance/mock-instance.ts index 9d54b719cf..7b52352b88 100644 --- a/libs/ng-mocks/src/lib/mock-instance/mock-instance.ts +++ b/libs/ng-mocks/src/lib/mock-instance/mock-instance.ts @@ -1,6 +1,7 @@ import { InjectionToken, Injector } from '@angular/core'; import { AbstractType, Type } from '../common/core.types'; +import funcImportExists from '../common/func.import-exists'; import ngMocksUniverse from '../common/ng-mocks-universe'; const stack: any[][] = [[]]; @@ -187,6 +188,8 @@ export function MockInstance( ): void; export function MockInstance(declaration: Type | AbstractType | InjectionToken, ...args: any[]) { + funcImportExists(declaration, 'MockInstance'); + const { key, value, accessor, data } = parseMockInstanceArgs(args); if (key) { return mockInstanceMember(declaration, key, value, accessor); diff --git a/libs/ng-mocks/src/lib/mock-module/mock-module.ts b/libs/ng-mocks/src/lib/mock-module/mock-module.ts index 3fe11ebde0..8bccfe4733 100644 --- a/libs/ng-mocks/src/lib/mock-module/mock-module.ts +++ b/libs/ng-mocks/src/lib/mock-module/mock-module.ts @@ -7,6 +7,7 @@ import coreReflectModuleResolve from '../common/core.reflect.module-resolve'; import { Type } from '../common/core.types'; import decorateMock from '../common/decorate.mock'; import { getMockedNgDefOf } from '../common/func.get-mocked-ng-def-of'; +import funcImportExists from '../common/func.import-exists'; import { isNgDef } from '../common/func.is-ng-def'; import { isNgModuleDefWithProviders, NgModuleWithProviders } from '../common/func.is-ng-module-def-with-providers'; import { Mock } from '../common/mock'; @@ -153,6 +154,8 @@ export function MockModule(module: Type): Type; export function MockModule(module: NgModuleWithProviders): NgModuleWithProviders; export function MockModule(module: any): any { + funcImportExists(module, 'MockModule'); + const { ngModule, ngModuleProviders } = extractModuleAndProviders(module); // We are inside of an 'it'. It is fine to to return a mock copy. diff --git a/libs/ng-mocks/src/lib/mock-pipe/mock-pipe.ts b/libs/ng-mocks/src/lib/mock-pipe/mock-pipe.ts index 4b6acb4079..112e059e7e 100644 --- a/libs/ng-mocks/src/lib/mock-pipe/mock-pipe.ts +++ b/libs/ng-mocks/src/lib/mock-pipe/mock-pipe.ts @@ -6,6 +6,7 @@ import coreReflectPipeResolve from '../common/core.reflect.pipe-resolve'; import { Type } from '../common/core.types'; import decorateMock from '../common/decorate.mock'; import { getMockedNgDefOf } from '../common/func.get-mocked-ng-def-of'; +import funcImportExists from '../common/func.import-exists'; import { Mock } from '../common/mock'; import ngMocksUniverse from '../common/ng-mocks-universe'; import helperMockService from '../mock-service/helper.mock-service'; @@ -48,6 +49,8 @@ export function MockPipe( pipe: Type, transform?: TPipe['transform'], ): Type> { + funcImportExists(pipe, 'MockPipe'); + // We are inside of an 'it'. It is fine to return a mock copy. if ((getTestBed() as any)._instantiated) { try { diff --git a/libs/ng-mocks/src/lib/mock-provider/mock-provider.ts b/libs/ng-mocks/src/lib/mock-provider/mock-provider.ts index e14a9bae46..903c39c6a2 100644 --- a/libs/ng-mocks/src/lib/mock-provider/mock-provider.ts +++ b/libs/ng-mocks/src/lib/mock-provider/mock-provider.ts @@ -1,6 +1,7 @@ import { FactoryProvider, InjectionToken, Provider } from '@angular/core'; import { AnyType } from '../common/core.types'; +import funcImportExists from '../common/func.import-exists'; import mockHelperStub from '../mock-helper/mock-helper.stub'; import helperUseFactory from '../mock-service/helper.use-factory'; import { MockService } from '../mock-service/mock-service'; @@ -27,6 +28,8 @@ export function MockProvider(provider: InjectionToken | string, useValue?: export function MockProvider(provider: string, useValue?: Partial): FactoryProvider; export function MockProvider(provide: any, overrides: any = defaultValue): Provider { + funcImportExists(provide, 'MockProvider'); + return helperUseFactory( provide, () => MockService(provide), diff --git a/libs/ng-mocks/src/lib/mock-render/mock-render.ts b/libs/ng-mocks/src/lib/mock-render/mock-render.ts index e3da79f6c3..07b7eeb8f5 100644 --- a/libs/ng-mocks/src/lib/mock-render/mock-render.ts +++ b/libs/ng-mocks/src/lib/mock-render/mock-render.ts @@ -4,6 +4,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { extendClass } from '../common/core.helpers'; import coreReflectDirectiveResolve from '../common/core.reflect.directive-resolve'; import { AnyType, Type } from '../common/core.types'; +import funcImportExists from '../common/func.import-exists'; import { isNgDef } from '../common/func.is-ng-def'; import { ngMocks } from '../mock-helper/mock-helper'; import { MockService } from '../mock-service/mock-service'; @@ -220,6 +221,8 @@ function MockRender>( params?: TComponent, flags: boolean | IMockRenderOptions = true, ): any { + funcImportExists(template, 'MockRender'); + const flagsObject: IMockRenderOptions = typeof flags === 'boolean' ? { detectChanges: flags } : flags; const meta: Directive = typeof template === 'string' || isNgDef(template, 't') ? {} : reflectTemplate(template); diff --git a/tests/issue-354/test.spec.ts b/tests/issue-354/test.spec.ts new file mode 100644 index 0000000000..061f5ad24a --- /dev/null +++ b/tests/issue-354/test.spec.ts @@ -0,0 +1,74 @@ +import { + MockComponent, + MockDirective, + MockInstance, + MockModule, + MockPipe, + MockProvider, + MockRender, +} from 'ng-mocks'; + +describe('issue-354', () => { + it('does not accept an empty module', () => { + expect(() => MockModule(undefined as any)).toThrowError( + 'An empty parameter has been passed into MockModule. Please check that its import is correct.', + ); + expect(() => MockModule(null as any)).toThrowError( + 'An empty parameter has been passed into MockModule. Please check that its import is correct.', + ); + }); + + it('does not accept an empty component', () => { + expect(() => MockComponent(undefined as any)).toThrowError( + 'An empty parameter has been passed into MockComponent. Please check that its import is correct.', + ); + expect(() => MockComponent(null as any)).toThrowError( + 'An empty parameter has been passed into MockComponent. Please check that its import is correct.', + ); + }); + + it('does not accept an empty directive', () => { + expect(() => MockDirective(undefined as any)).toThrowError( + 'An empty parameter has been passed into MockDirective. Please check that its import is correct.', + ); + expect(() => MockDirective(null as any)).toThrowError( + 'An empty parameter has been passed into MockDirective. Please check that its import is correct.', + ); + }); + + it('does not accept an empty pipe', () => { + expect(() => MockPipe(undefined as any)).toThrowError( + 'An empty parameter has been passed into MockPipe. Please check that its import is correct.', + ); + expect(() => MockPipe(null as any)).toThrowError( + 'An empty parameter has been passed into MockPipe. Please check that its import is correct.', + ); + }); + + it('does not accept an empty provider', () => { + expect(() => MockProvider(undefined as any)).toThrowError( + 'An empty parameter has been passed into MockProvider. Please check that its import is correct.', + ); + expect(() => MockProvider(null as any)).toThrowError( + 'An empty parameter has been passed into MockProvider. Please check that its import is correct.', + ); + }); + + it('does not accept an empty render', () => { + expect(() => MockRender(undefined as any)).toThrowError( + 'An empty parameter has been passed into MockRender. Please check that its import is correct.', + ); + expect(() => MockRender(null as any)).toThrowError( + 'An empty parameter has been passed into MockRender. Please check that its import is correct.', + ); + }); + + it('does not accept an empty instance', () => { + expect(() => MockInstance(undefined as any)).toThrowError( + 'An empty parameter has been passed into MockInstance. Please check that its import is correct.', + ); + expect(() => MockInstance(null as any)).toThrowError( + 'An empty parameter has been passed into MockInstance. Please check that its import is correct.', + ); + }); +}); diff --git a/tslint.json b/tslint.json index d76b10d500..6cc2d6432c 100644 --- a/tslint.json +++ b/tslint.json @@ -18,7 +18,7 @@ "max-file-line-count": [true, 250], "max-func-body-length": [ true, - 25, + 30, { "ignore-parameters-to-function-regex": "^(([fx]?(describe|it))|beforeEach|fakeAsync|inject)$" }