Skip to content

Commit

Permalink
fix: removal dependency on decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Dec 27, 2020
1 parent 2778992 commit 92064d4
Show file tree
Hide file tree
Showing 13 changed files with 100 additions and 138 deletions.
20 changes: 20 additions & 0 deletions lib/common/core.helpers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { extendClass } from './core.helpers';
import decorateMock from './decorate.mock';

describe('DebuggableMock', () => {
it('prefixes the class name with MockOf', () => {
class Foo {}
const mock = extendClass(Foo);
decorateMock(mock, Foo);

expect(mock.name).toBe('MockOfFoo');
});

it('adds a mockOf property that is the class being replaced with a mock copy', () => {
class Bar {}
const mock = extendClass(Bar);
decorateMock(mock, Bar);

expect((mock as any).mockOf).toBe(Bar);
});
});
12 changes: 7 additions & 5 deletions lib/common/core.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ const extendClassicClass = <I extends object>(base: Type<I>): Type<I> => {
try {
// tslint:disable-next-line no-eval
eval(`
class child extends window.ngMocksParent {
class MockMiddleware extends window.ngMocksParent {
}
window.ngMocksResult = child
window.ngMocksResult = MockMiddleware
`);
child = (window as any).ngMocksResult;
} catch (e) {
class ClassEs5 extends (window as any).ngMocksParent {}
class MockMiddleware extends (window as any).ngMocksParent {}

child = ClassEs5;
child = MockMiddleware;
}
(window as any).ngMocksParent = undefined;

Expand All @@ -112,7 +112,9 @@ export const extendClass = <I extends object>(base: Type<I>): Type<I> => {

const parameters = jitReflector.parameters(base);
if (parameters.length) {
child.parameters = parameters;
Object.defineProperty(child, 'parameters', {
value: [...parameters],
});
}

return child;
Expand Down
11 changes: 11 additions & 0 deletions lib/common/decorate.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { AnyType } from './core.types';
import { ngMocksMockConfig } from './mock';

export default function (base: AnyType<any>, mockClass: AnyType<any>, config: ngMocksMockConfig = {}): void {
Object.defineProperties(base, {
mockOf: { value: mockClass },
name: { value: `MockOf${mockClass.name}` },
nameConstructor: { value: base.name },
});
base.prototype.__ngMocksConfig = config;
}
19 changes: 0 additions & 19 deletions lib/common/mock-of.spec.ts

This file was deleted.

20 changes: 0 additions & 20 deletions lib/common/mock-of.ts

This file was deleted.

65 changes: 27 additions & 38 deletions lib/common/mock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import { MockDirective } from '../mock-directive/mock-directive';
import { MockModule } from '../mock-module/mock-module';
import { MockPipe } from '../mock-pipe/mock-pipe';

import { Type } from './core.types';
import { extendClass } from './core.helpers';
import decorateMock from './decorate.mock';
import { isMockOf } from './func.is-mock-of';
import { Mock } from './mock';
import { MockOf } from './mock-of';

class ParentClass {
protected parentValue = true;
Expand Down Expand Up @@ -251,62 +251,51 @@ describe('definitions', () => {
it('skips output properties from config', () => {
class TargetComponent {}

@MockOf(TargetComponent, {
const testComponent = extendClass(Mock);
decorateMock(testComponent, TargetComponent, {
outputs: ['__ngMocksConfig', 'test'],
})
class TestComponent extends Mock {}
});

const instance: any = new TestComponent();
const instance: any = new testComponent();
expect(instance.__ngMocksConfig).not.toEqual(
jasmine.any(EventEmitter),
);
expect(instance.test).toEqual(jasmine.any(EventEmitter));
});

it('adds missed properties to the instance', () => {
const customProperty = (constructor: Type<any>) => {
Object.defineProperty(constructor.prototype, 'test', {
get: () => false,
});
};

class TargetComponent {}

@customProperty
class TestMock extends Mock {}

@MockOf(TargetComponent)
class TestComponent extends TestMock {}
class TestMock extends Mock {
public get test(): boolean {
return false;
}
}

const instance: any = new TestComponent();
const testComponent = extendClass(TestMock);
decorateMock(testComponent, TargetComponent);
const instance: any = new testComponent();
expect(
Object.getOwnPropertyDescriptor(instance, 'test'),
).toBeDefined();
});

it('skips existing properties from mockOf', () => {
const customPropertyFalse = (constructor: Type<any>) => {
Object.defineProperty(constructor.prototype, 'test', {
get: () => false,
});
};

const customPropertyTrue = (constructor: Type<any>) => {
Object.defineProperty(constructor.prototype, 'test', {
get: () => true,
});
};

@customPropertyTrue
class TargetComponent {}

@customPropertyFalse
class TestMock extends Mock {}
class TargetComponent {
public get test(): boolean {
return true;
}
}

@MockOf(TargetComponent)
class TestComponent extends TestMock {}
class TestMock extends Mock {
public get test(): boolean {
return false;
}
}

const instance: any = new TestComponent();
const testComponent = extendClass(TestMock);
decorateMock(testComponent, TargetComponent);
const instance: any = new testComponent();
expect(instance.test).toEqual(false);
});

Expand Down
4 changes: 3 additions & 1 deletion lib/common/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,6 @@ export class Mock {
}
}

(Mock as any).parameters = [[Injector, new Optional()]];
Object.defineProperty(Mock, 'parameters', {
value: [[Injector, new Optional()]],
});
20 changes: 6 additions & 14 deletions lib/mock-component/mock-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '@angular/core';
import { getTestBed } from '@angular/core/testing';

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';
Expand Down Expand Up @@ -86,6 +87,10 @@ class ComponentMockBase extends LegacyControlValueAccessor implements AfterConte
}
}

Object.defineProperty(ComponentMockBase, 'parameters', {
value: [[ChangeDetectorRef], [Injector]],
});

const viewChildArgs: any = { read: ViewContainerRef, static: false };
const viewChildTemplate = (selector: string): string =>
`<div *ngIf="mockRender_${selector}" data-key="${selector}"><ng-template #__${selector}></ng-template></div>`;
Expand Down Expand Up @@ -119,19 +124,6 @@ const generateTemplate = (
};
};

const createMockClass = (): Type<any> => {
class ComponentMock extends ComponentMockBase {
// istanbul ignore next
public constructor(changeDetector: ChangeDetectorRef, injector: Injector) {
super(changeDetector, injector);
}
}

(ComponentMock as any).parameters = [ChangeDetectorRef, Injector];

return ComponentMock;
};

const decorateClass = (component: Type<any>, mock: Type<any>): void => {
const meta = coreReflectDirectiveResolve(component);
const { exportAs, inputs, outputs, queries, selector, providers } = meta;
Expand Down Expand Up @@ -161,7 +153,7 @@ export function MockComponent<TComponent>(component: Type<TComponent>): Type<Moc
return ngMocksUniverse.cacheDeclarations.get(component);
}

const mock = createMockClass();
const mock = extendClass(ComponentMockBase);
decorateClass(component, mock);

// istanbul ignore else
Expand Down
12 changes: 6 additions & 6 deletions lib/mock-declaration/mock-declaration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,24 @@ describe('MockDeclaration', () => {
TargetPipe,
);
expect(mocks.length).toEqual(3);
expect(mocks[0].nameConstructor).toEqual('ComponentMock');
expect(mocks[1].nameConstructor).toEqual('DirectiveMock');
expect(mocks[2].nameConstructor).toEqual('PipeMock');
expect(mocks[0].nameConstructor).toEqual('MockMiddleware');
expect(mocks[1].nameConstructor).toEqual('MockMiddleware');
expect(mocks[2].nameConstructor).toEqual('MockMiddleware');
});

it('should process components with an empty template correctly', () => {
const mock: any = MockDeclaration(TargetComponent);
expect(mock.nameConstructor).toEqual('ComponentMock');
expect(mock.nameConstructor).toEqual('MockMiddleware');
});

it('should process directives correctly', () => {
const mock: any = MockDeclaration(TargetDirective);
expect(mock.nameConstructor).toEqual('DirectiveMock');
expect(mock.nameConstructor).toEqual('MockMiddleware');
});

it('should process pipes correctly', () => {
const mock: any = MockDeclaration(TargetPipe);
expect(mock.nameConstructor).toEqual('PipeMock');
expect(mock.nameConstructor).toEqual('MockMiddleware');
});

it('should skip unknown types', () => {
Expand Down
27 changes: 5 additions & 22 deletions lib/mock-directive/mock-directive.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Directive, ElementRef, Injector, OnInit, Optional, TemplateRef, ViewContainerRef } from '@angular/core';
import { getTestBed } from '@angular/core/testing';

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';
Expand Down Expand Up @@ -54,27 +55,9 @@ class DirectiveMockBase extends LegacyControlValueAccessor implements OnInit {
}
}

const createMockClass = (): Type<any> => {
class DirectiveMock extends DirectiveMockBase {
// istanbul ignore next
public constructor(
injector: Injector,
element?: ElementRef,
template?: TemplateRef<any>,
viewContainer?: ViewContainerRef,
) {
super(injector, element, template, viewContainer);
}
}
(DirectiveMock as any).parameters = [
Injector,
[ElementRef, new Optional()],
[TemplateRef, new Optional()],
[ViewContainerRef, new Optional()],
];

return DirectiveMock;
};
Object.defineProperty(DirectiveMockBase, 'parameters', {
value: [[Injector], [ElementRef, new Optional()], [TemplateRef, new Optional()], [ViewContainerRef, new Optional()]],
});

const decorateClass = (directive: Type<any>, mock: Type<any>): void => {
const meta = coreReflectDirectiveResolve(directive);
Expand Down Expand Up @@ -106,7 +89,7 @@ export function MockDirective<TDirective>(directive: Type<TDirective>): Type<Moc
return ngMocksUniverse.cacheDeclarations.get(directive);
}

const mock = createMockClass();
const mock = extendClass(DirectiveMockBase);
decorateClass(directive, mock);

// istanbul ignore else
Expand Down
4 changes: 2 additions & 2 deletions lib/mock-module/mock-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import coreConfig from '../common/core.config';
import { extendClass } from '../common/core.helpers';
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 { isNgDef } from '../common/func.is-ng-def';
import { isNgModuleDefWithProviders, NgModuleWithProviders } from '../common/func.is-ng-module-def-with-providers';
import { Mock } from '../common/mock';
import { MockOf } from '../common/mock-of';
import ngMocksUniverse from '../common/ng-mocks-universe';

import mockNgDef from './mock-ng-def';
Expand Down Expand Up @@ -113,7 +113,7 @@ const detectMockModule = (ngModule: Type<any>, mockModule?: Type<any>): Type<any

// the last thing is to apply decorators.
NgModule(mockModuleDef)(mock);
MockOf(ngModule)(mock);
decorateMock(mock, ngModule);

return mock;
}
Expand Down
Loading

0 comments on commit 92064d4

Please sign in to comment.