Skip to content

Commit

Permalink
perf: cc duplicates
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Nov 23, 2020
1 parent 6bebb6b commit 73f607a
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 121 deletions.
3 changes: 3 additions & 0 deletions lib/common/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import { EventEmitter, Injector, Optional } from '@angular/core';
import { NgControl } from '@angular/forms';

import { IMockBuilderConfig } from '../mock-builder/types';
import mockServiceHelper from '../mock-service/helper';

import ngMocksUniverse from './ng-mocks-universe';

export type ngMocksMockConfig = {
config?: IMockBuilderConfig;
outputs?: string[];
setNgValueAccessor?: boolean;
viewChildRefs?: Map<string, string>;
};

export class Mock {
Expand Down
24 changes: 14 additions & 10 deletions lib/mock-builder/mock-builder-performance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ import ngMocksUniverse from '../common/ng-mocks-universe';
import { MockBuilderPromise } from './mock-builder-promise';
import { IMockBuilderResult } from './types';

const requiredMetadata = (
ngModule: TestModuleMetadata,
): {
declarations: any[];
imports: any[];
providers: any[];
} => ({
declarations: [...(ngModule.declarations || /* istanbul ignore next */ [])],
imports: [...(ngModule.imports || /* istanbul ignore next */ [])],
providers: [...(ngModule.providers || /* istanbul ignore next */ [])],
});

export class MockBuilderPerformance extends MockBuilderPromise {
public build(): NgModule {
let ngModule: TestModuleMetadata;
Expand All @@ -19,11 +31,7 @@ export class MockBuilderPerformance extends MockBuilderPromise {
ngModule = ngMocksUniverse.global.get('builder:module');

// avoiding influences on cache when users extend the testing module.
return {
declarations: [...(ngModule.declarations || /* istanbul ignore next */ [])],
imports: [...(ngModule.imports || /* istanbul ignore next */ [])],
providers: [...(ngModule.providers || /* istanbul ignore next */ [])],
};
return requiredMetadata(ngModule);
}

// removal of cached promise in case of mismatch
Expand Down Expand Up @@ -75,11 +83,7 @@ export class MockBuilderPerformance extends MockBuilderPromise {
ngMocksUniverse.global.set('builder:module', ngModule);

// avoiding influences on cache when users extend the testing module.
return {
declarations: [...(ngModule.declarations || /* istanbul ignore next */ [])],
imports: [...(ngModule.imports || /* istanbul ignore next */ [])],
providers: [...(ngModule.providers || /* istanbul ignore next */ [])],
};
return requiredMetadata(ngModule);
}

public async then<TResult1 = IMockBuilderResult>(
Expand Down
121 changes: 60 additions & 61 deletions lib/mock-component/mock-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,62 @@ import decorateDeclaration from '../mock/decorate-declaration';

import { MockedComponent } from './types';

class ComponentMockBase extends MockControlValueAccessor implements AfterContentInit {
/* istanbul ignore next */
public constructor(changeDetector: ChangeDetectorRef, injector: Injector) {
super(injector);
this.__ngMocksInstall(changeDetector);
}

public ngAfterContentInit(): void {
const config: any = this.__ngMocksConfig?.config;
if (!(this as any).__rendered && config && config.render) {
for (const block of Object.keys(config.render)) {
const { $implicit, variables } =
config.render[block] !== true
? config.render[block]
: {
$implicit: undefined,
variables: {},
};
(this as any).__render(block, $implicit, variables);
}
(this as any).__rendered = true;
}
}

private __ngMocksInstall(changeDetector: ChangeDetectorRef): void {
const refs: any = this.__ngMocksConfig?.viewChildRefs;

// Providing method to hide any @ContentChild based on its selector.
(this as any).__hide = (contentChildSelector: string) => {
const key = refs?.get(contentChildSelector);
if (key) {
(this as any)[`mockRender_${contentChildSelector}`] = false;
changeDetector.detectChanges();
}
};

// Providing a method to render any @ContentChild based on its selector.
(this as any).__render = (contentChildSelector: string, $implicit?: any, variables?: Record<keyof any, any>) => {
const key = refs?.get(contentChildSelector);
let templateRef: TemplateRef<any>;
let viewContainer: ViewContainerRef;
if (key) {
(this as any)[`mockRender_${contentChildSelector}`] = true;
changeDetector.detectChanges();
viewContainer = (this as any)[`__mockView_${key}`];
templateRef = (this as any)[key];
if (viewContainer && templateRef) {
viewContainer.clear();
viewContainer.createEmbeddedView(templateRef, { ...variables, $implicit } as any);
changeDetector.detectChanges();
}
}
};
}
}

export function MockComponents(...components: Array<Type<any>>): Array<Type<MockedComponent<any>>> {
return components.map(MockComponent);
}
Expand Down Expand Up @@ -81,73 +137,16 @@ export function MockComponent<TComponent>(component: Type<TComponent>): Type<Moc
}
}

const config = ngMocksUniverse.config.get(component);

class ComponentMock extends MockControlValueAccessor implements AfterContentInit {
class ComponentMock extends ComponentMockBase {
/* istanbul ignore next */
public constructor(changeDetector: ChangeDetectorRef, injector: Injector) {
super(injector);
this.__ngMocksInstall(changeDetector);
}

public ngAfterContentInit(): void {
if (!(this as any).__rendered && config && config.render) {
for (const block of Object.keys(config.render)) {
const { $implicit, variables } =
config.render[block] !== true
? config.render[block]
: {
$implicit: undefined,
variables: {},
};
(this as any).__render(block, $implicit, variables);
}
(this as any).__rendered = true;
}
}

private __ngMocksInstall(changeDetector: ChangeDetectorRef): void {
// Providing method to hide any @ContentChild based on its selector.
(this as any).__hide = (contentChildSelector: string) => {
const key = viewChildRefs.get(contentChildSelector);
if (key) {
(this as any)[`mockRender_${contentChildSelector}`] = false;
changeDetector.detectChanges();
}
};

// Providing a method to render any @ContentChild based on its selector.
(this as any).__render = (contentChildSelector: string, $implicit?: any, variables?: Record<keyof any, any>) => {
const key = viewChildRefs.get(contentChildSelector);
let templateRef: TemplateRef<any>;
let viewContainer: ViewContainerRef;
if (key) {
(this as any)[`mockRender_${contentChildSelector}`] = true;
changeDetector.detectChanges();
viewContainer = (this as any)[`__mockView_${key}`];
templateRef = (this as any)[key];
if (viewContainer && templateRef) {
viewContainer.clear();
viewContainer.createEmbeddedView(templateRef, { ...variables, $implicit } as any);
changeDetector.detectChanges();
}
}
};
super(changeDetector, injector);
}
}
(ComponentMock as any).parameters = [ChangeDetectorRef, Injector];

const mockMeta = {
inputs,
outputs,
providers,
queries,
};
const mockParams = {
exportAs,
selector,
template,
};
const mockMeta = { inputs, outputs, providers, queries, viewChildRefs };
const mockParams = { exportAs, selector, template };
const options = decorateDeclaration(component, ComponentMock, mockMeta, mockParams);
Component(options)(ComponentMock);

Expand Down
99 changes: 49 additions & 50 deletions lib/mock-directive/mock-directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,50 @@ import decorateDeclaration from '../mock/decorate-declaration';

import { MockedDirective } from './types';

class DirectiveMockBase extends MockControlValueAccessor implements OnInit {
/* istanbul ignore next */
public constructor(
injector: Injector,
element?: ElementRef,
template?: TemplateRef<any>,
viewContainer?: ViewContainerRef,
) {
super(injector);
this.__ngMocksInstall(element, template, viewContainer);
}

public ngOnInit(): void {
const config: any = this.__ngMocksConfig?.config;
if (config?.render) {
const { $implicit, variables } =
config.render !== true
? config.render
: {
$implicit: undefined,
variables: {},
};
(this as any).__render($implicit, variables);
}
}

private __ngMocksInstall(element?: ElementRef, template?: TemplateRef<any>, viewContainer?: ViewContainerRef): void {
// Basically any directive on ng-template is treated as structural, even it doesn't control render process.
// In our case we don't if we should render it or not and due to this we do nothing.
(this as any).__element = element;
(this as any).__template = template;
(this as any).__viewContainer = viewContainer;
(this as any).__isStructural = template && viewContainer;

// Providing method to render mock values.
(this as any).__render = ($implicit?: any, variables?: Record<keyof any, any>) => {
if (viewContainer && template) {
viewContainer.clear();
viewContainer.createEmbeddedView(template, { ...variables, $implicit });
}
};
}
}

export function MockDirectives(...directives: Array<Type<any>>): Array<Type<MockedDirective<any>>> {
return directives.map(MockDirective);
}
Expand Down Expand Up @@ -41,52 +85,15 @@ export function MockDirective<TDirective>(directive: Type<TDirective>): Type<Moc
}
const { selector, exportAs, inputs, outputs, queries, providers } = meta;

const config = ngMocksUniverse.config.get(directive);

class DirectiveMock extends MockControlValueAccessor implements OnInit {
class DirectiveMock extends DirectiveMockBase {
/* istanbul ignore next */
public constructor(
injector: Injector,
@Optional() element?: ElementRef,
@Optional() template?: TemplateRef<any>,
@Optional() viewContainer?: ViewContainerRef,
) {
super(injector);
this.__ngMocksInstall(element, template, viewContainer);
}

public ngOnInit(): void {
if (config && config.render) {
const { $implicit, variables } =
config.render !== true
? config.render
: {
$implicit: undefined,
variables: {},
};
(this as any).__render($implicit, variables);
}
}

private __ngMocksInstall(
element?: ElementRef,
template?: TemplateRef<any>,
viewContainer?: ViewContainerRef,
): void {
// Basically any directive on ng-template is treated as structural, even it doesn't control render process.
// In our case we don't if we should render it or not and due to this we do nothing.
(this as any).__element = element;
(this as any).__template = template;
(this as any).__viewContainer = viewContainer;
(this as any).__isStructural = template && viewContainer;

// Providing method to render mock values.
(this as any).__render = ($implicit?: any, variables?: Record<keyof any, any>) => {
if (viewContainer && template) {
viewContainer.clear();
viewContainer.createEmbeddedView(template, { ...variables, $implicit });
}
};
) {
super(injector, element, template, viewContainer);
}
}
(DirectiveMock as any).parameters = [
Expand All @@ -96,16 +103,8 @@ export function MockDirective<TDirective>(directive: Type<TDirective>): Type<Moc
[ViewContainerRef, new Optional()],
];

const mockMeta = {
inputs,
outputs,
providers,
queries,
};
const mockParams = {
exportAs,
selector,
};
const mockMeta = { inputs, outputs, providers, queries };
const mockParams = { exportAs, selector };
const options = decorateDeclaration(directive, DirectiveMock, mockMeta, mockParams);
Directive(options)(DirectiveMock);

Expand Down
4 changes: 4 additions & 0 deletions lib/mock/decorate-declaration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, Directive, Provider, ViewChild } from '@angular/core';
import ngMocksUniverse from 'ng-mocks/dist/lib/common/ng-mocks-universe';

import { AnyType } from '../common/core.types';
import decorateInputs from '../common/decorate.inputs';
Expand All @@ -17,6 +18,7 @@ export default <T extends Component | Directive>(
outputs?: string[];
providers?: Provider[];
queries?: Record<string, ViewChild>;
viewChildRefs?: Map<string, string>;
},
params: T,
): T => {
Expand All @@ -32,8 +34,10 @@ export default <T extends Component | Directive>(
mockServiceHelper.extractMethodsFromPrototype(source.prototype).indexOf('writeValue') !== -1;
}
MockOf(source, {
config: ngMocksUniverse.config.get(source),
outputs: meta.outputs,
setNgValueAccessor: data.setNgValueAccessor,
viewChildRefs: meta.viewChildRefs,
})(mock);

/* istanbul ignore else */
Expand Down

0 comments on commit 73f607a

Please sign in to comment.