Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(createMockList): add createMockList functionality, add typings to framework test #34

Merged
merged 5 commits into from
Apr 20, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ A Transformer needs to be provided at compile time. There are different ways to
[Please read the following guide to find your configuration](docs/TRANSFORMER.md)

## Usage
#### Create mock
```ts
import { createMock } from 'ts-auto-mock';

Expand All @@ -33,6 +34,18 @@ mock.getName() // ""
mock.details // "{phone: 0} "
```

#### Create mock list
createMock list it will create a list of mocks automatically
```ts
import { createMockList } from 'ts-auto-mock';

interface Person {
id: string;
}
const mockList = createMockList<Person>(2);
mockList.length // 2
```

## Type Examples
The library try to convert the type given to createMock so you dont need to create concrete mock manually.
[Open this link to see more examples](docs/DETAILS.md)
Expand Down
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export { MockMarker } from './src/mockMarker/mockMarker';
export { MockMethod } from './src/mockMethod/mockMethod';
export { On, AutoMockExtensionHandler } from './src/framework/framework';
export { createMock } from './src/transformer/create-mock';
export { createMockList } from './src/transformer/create-mock-list';
export { method } from './src/framework/methodExtension';
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"src/mockMarker/*.d.ts",
"src/transformer/transformer.d.ts",
"src/transformer/create-mock.ts",
"src/transformer/create-mock-list.ts",
"index.d.ts",
"index.js",
"transformer.js"
Expand All @@ -36,7 +37,7 @@
"type": "git",
"url": "https://github.com/uittorio/ts-auto-mock.git"
},
"author": "Vittorio Guerriero",
"author": "Vittorio Guerriero & Giulio Caprino",
"license": "ISC",
"devDependencies": {
"@commitlint/cli": "7.5.2",
Expand Down
1 change: 1 addition & 0 deletions src/transformer/create-mock-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export declare function createMockList<T extends object>(quantity?: number): T[];
Pmyl marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions src/transformer/create-mock.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@

export declare function createMock<T extends object>(): T;
34 changes: 34 additions & 0 deletions src/transformer/matcher/matcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as path from 'path';
import * as ts from 'typescript';

export function isCreateMock(declaration: ts.FunctionDeclaration): boolean {
return declaration.name && declaration.name.getText() === 'createMock';
}

export function isCreateMockList(declaration: ts.FunctionDeclaration): boolean {
return declaration.name && declaration.name.getText() === 'createMockList';
}

export function isFromTsAutoMock(signature: ts.Signature): boolean {
if (!isDeclarationDefined(signature) || !isFunctionDeclaration(signature.declaration)) {
return false;
}

if (!isFunctionDeclaration(signature.declaration)) {
Pmyl marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

const createMockTs: string = path.join(__dirname, `src/transformer/create-mock.ts`);
const createMockListTs: string = path.join(__dirname, `src/transformer/create-mock-list.ts`);
const fileName: string = signature.declaration.getSourceFile().fileName;

return fileName === createMockTs || fileName === createMockListTs;
}

function isFunctionDeclaration(declaration: ts.Declaration): declaration is ts.FunctionDeclaration {
return ts.isFunctionDeclaration(declaration);
}

function isDeclarationDefined(signature: ts.Signature): boolean {
return signature && !!signature.declaration;
}
63 changes: 42 additions & 21 deletions src/transformer/transformer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as path from 'path';
import * as ts from 'typescript';
import { GetDescriptor } from './descriptor/descriptor';
import { TypeReferenceCache } from './descriptor/typeReference/cache';
import { isCreateMock, isCreateMockList, isFromTsAutoMock, } from './matcher/matcher';
import { MockDefiner } from './mockDefiner/mockDefiner';
import { GetMockFactoryCall } from './mockFactoryCall/mockFactoryCall';
import { SetTypeChecker, TypeChecker } from './typeChecker/typeChecker';
Expand Down Expand Up @@ -30,40 +30,61 @@ function visitNodeAndChildren(node: ts.Node, context: ts.TransformationContext):
}

function visitNode(node: ts.Node): ts.Node {
if (!isCreateMockCallExpression(node)) {
if (!ts.isCallExpression(node)) {
return node;
}

const signature: ts.Signature = getSignature(node);

if (!isFromTsAutoMock(signature)) {
return node;
}

const nodeToMock: ts.TypeNode = node.typeArguments[0];

TypeReferenceCache.instance.clear();
MockDefiner.instance.setFileNameFromNode(nodeToMock);
MockDefiner.instance.setTsAutoMockImportIdentifier();

if (isTypeReusable(nodeToMock)) {
return GetMockFactoryCall(nodeToMock);
} else {
return GetDescriptor(nodeToMock);
const declaration: ts.FunctionDeclaration = signature.declaration as ts.FunctionDeclaration;

if (isCreateMock(declaration)) {
return getMockExpression(nodeToMock);
}

if (isCreateMockList(declaration)) {
const lengthLiteral: ts.NumericLiteral = node.arguments[0] as ts.NumericLiteral;
const mocks: ts.Expression[] = getListOfMockExpression(nodeToMock, lengthLiteral);

return ts.createArrayLiteral(mocks);
}

return node;
}

function isCreateMockCallExpression(node: ts.Node): node is ts.CallExpression {
const indexTs: string = path.join(__dirname, 'src/transformer/create-mock.ts');
function getListOfMockExpression(nodeToMock: ts.TypeNode, lengthLiteral: ts.NumericLiteral): ts.Expression[] {
const defaultLengthOfArray: number = 1;
const length: number = lengthLiteral ? parseInt(lengthLiteral.text, 10) : defaultLengthOfArray;
const mocks: ts.Expression[] = [];
const mock: ts.Expression = getMockExpression(nodeToMock);

if (node.kind !== ts.SyntaxKind.CallExpression) {
return false;
for (let i: number = 0; i <= length - 1; i++) {
mocks.push(mock);
}

const typeChecker: ts.TypeChecker = TypeChecker();
const signature: ts.Signature = typeChecker.getResolvedSignature(node as ts.CallExpression);
if (typeof signature === 'undefined') {
return false;
return mocks;
}

function getMockExpression(nodeToMock: ts.TypeNode): ts.Expression {
if (isTypeReusable(nodeToMock)) {
return GetMockFactoryCall(nodeToMock);
}

const { declaration }: ts.Signature = signature;
return !!declaration
&& (path.join(declaration.getSourceFile().fileName) === indexTs)
// tslint:disable-next-line:no-string-literal
&& !!declaration['name']
// tslint:disable-next-line:no-string-literal
&& (declaration['name'].getText() === 'createMock');
return GetDescriptor(nodeToMock);
}

function getSignature(node: ts.CallExpression): ts.Signature {
const typeChecker: ts.TypeChecker = TypeChecker();

return typeChecker.getResolvedSignature(node);
}
1 change: 1 addition & 0 deletions test/framework/context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { MockFactory } from 'ts-auto-mock';

// tslint:disable:no-any
MockFactory.instance.registerFactory((name: string, value: any) => {
return jasmine.createSpy(name).and.returnValue(value);
});
Expand Down
15 changes: 15 additions & 0 deletions test/framework/create-mock-list.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createMockList } from 'ts-auto-mock';

describe('create-mock-list', () => {
interface Interface {
property: string;
method(): void;
}

it('should have different mocks for each item', () => {
const properties: Interface[] = createMockList<Interface>(2);
properties[0].method();
expect(properties[0].method).toHaveBeenCalled();
expect(properties[1].method).not.toHaveBeenCalled();
});
});
14 changes: 7 additions & 7 deletions test/framework/functions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ describe('functions', () => {
});

it('should work as a spy', () => {
function hello(a: a) {
a();
function hello(myMethod: a): void {
myMethod();
}

hello(mock);
Expand All @@ -19,18 +19,18 @@ describe('functions', () => {

it('should not be able to get the mock', () => {
expect(() => {
On(mock).get(method((x) => x.apply));
On(mock).get(method((x: a) => x.apply));
}).toThrow();
});

it('should create different factories for different functions mock', () => {
interface Mock {
interface AMock {
first: Function;
second: Function;
}
const mock: Mock = createMock<Mock>();
const anotherMock: AMock = createMock<AMock>();

expect((mock.first as jasmine.Spy).and.identity).toBe('first');
expect((mock.second as jasmine.Spy).and.identity).toBe('second');
expect((anotherMock.first as jasmine.Spy).and.identity).toBe('first');
expect((anotherMock.second as jasmine.Spy).and.identity).toBe('second');
});
});
12 changes: 6 additions & 6 deletions test/framework/methodExtension.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {createMock, method, MockMethod, On} from "ts-auto-mock";
import {createMock, method, On} from 'ts-auto-mock';

describe('Extension: method', () => {
interface Interface {
m(): string;
}

it('should be able to retrieve the method using arrow function', () => {
const mock: Interface = createMock<Interface>();

expect(On(mock).get(method(x => x.m))).toBe(mock.m);
expect(On(mock).get(method((x) => x.m))).toBe(mock.m);
});

it('should be able to retrieve the method using a string', () => {
const mock: Interface = createMock<Interface>();

expect(On(mock).get(method("m"))).toBe(mock.m);
expect(On(mock).get(method('m'))).toBe(mock.m);
});
});
});
10 changes: 5 additions & 5 deletions test/framework/name.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createMock, method, On } from "ts-auto-mock";
import { createMock, method, On } from 'ts-auto-mock';

describe('names', () => {
it('should give a name to the spy', () => {
Expand All @@ -8,10 +8,10 @@ describe('names', () => {
}

const mock: Interface = createMock<Interface>();
const spyA: jasmine.Spy = On(mock).get(method(x => x.a));
const spyB: jasmine.Spy = On(mock).get(method(x => x.b));
const spyA: jasmine.Spy = On(mock).get(method((x: Interface) => x.a));
const spyB: jasmine.Spy = On(mock).get(method((x: Interface) => x.b));

expect(spyA.and.identity).toBe("a");
expect(spyB.and.identity).toBe("b");
expect(spyA.and.identity).toBe('a');
expect(spyB.and.identity).toBe('b');
});
});
7 changes: 4 additions & 3 deletions test/framework/on.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {AutoMockExtensionHandler, createMock, On} from "ts-auto-mock";
import {AutoMockExtensionHandler, createMock, On} from 'ts-auto-mock';

describe('On', () => {
it('should throw when is used without a mock', () => {
// tslint:disable
expect(() => On({ prop: () => {} })).toThrow();
});

it('should return an AutoMockExtensionHandler when used with a mock', () => {
expect(On(createMock<{prop: () => void}>())).toEqual(jasmine.any(AutoMockExtensionHandler));
});
});
});
3 changes: 1 addition & 2 deletions test/framework/primtive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ describe('primitives', () => {

it('should not be able to get the mock', () => {
expect(() => {
// tslint:disable
// @ts-ignore
On(mock).get(method((x) => x.apply));
On(mock).get(method((x: string) => x.apply));
}).toThrow();
});
});
14 changes: 7 additions & 7 deletions test/framework/spy.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createMock } from "ts-auto-mock";
import { createMock } from 'ts-auto-mock';

describe('when testing with the mock factory jasmine', () => {
it('should create a spy for methods with the return value', () => {
Expand All @@ -8,7 +8,7 @@ describe('when testing with the mock factory jasmine', () => {

const mock: Interface = createMock<Interface>();

expect(mock.a()).toBe("");
expect(mock.a()).toBe('');
expect(mock.a).toHaveBeenCalledWith();
});

Expand All @@ -18,7 +18,7 @@ describe('when testing with the mock factory jasmine', () => {
}

const mock: Interface = createMock<Interface>();
let callTimes = (mock.a as jasmine.Spy).calls.count();
let callTimes: number = (mock.a as jasmine.Spy).calls.count();
expect(callTimes).toBe(0);
mock.a();
callTimes = (mock.a as jasmine.Spy).calls.count();
Expand All @@ -37,14 +37,14 @@ describe('when testing with the mock factory jasmine', () => {
it('should create a spy for nested object', () => {
interface Interface {
b: {
c: () => string
c: () => string,
};
}

const mock: Interface = createMock<Interface>();
expect(mock.b.c()).toBe("");
expect(mock.b.c()).toBe('');
expect(mock.b.c).toHaveBeenCalledWith();
const callTimes = (mock.b.c as jasmine.Spy).calls.count();
const callTimes: number = (mock.b.c as jasmine.Spy).calls.count();
expect(callTimes).toBe(1);
});
});
});
26 changes: 13 additions & 13 deletions test/framework/tsLibs/tsLibs.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { createMock, method, On } from "ts-auto-mock";
import { createMock, method, On } from 'ts-auto-mock';

describe('tsLib', () => {
it('should return a spy with a name', () => {
interface Interface {
a: Function;
}

const mock: Interface = createMock<Interface>();
const spy = On(mock).get(method(x => x.a));
it('should return a spy with a name', () => {
interface Interface {
a: Function;
}

expect(spy.and.identity).toBe("a");

mock.a();
expect(mock.a).toHaveBeenCalledWith();
});
const mock: Interface = createMock<Interface>();
const spy: jasmine.Spy = On(mock).get(method((x: Interface) => x.a));

expect(spy.and.identity).toBe('a');

mock.a();
expect(mock.a).toHaveBeenCalledWith();
});
});
3 changes: 1 addition & 2 deletions test/framework/typings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ describe('when creating a mock', () => {
}

const mock: Interface = createMock<Interface>();
//tslint:disable
const spy: jasmine.Spy = On(mock).get(method((x) => x.methodExpectJasmineExtension));
const spy: jasmine.Spy = On(mock).get(method((x: Interface) => x.methodExpectJasmineExtension));
spy.and.returnValue('');
// mock.arrayExpectGenerateMethod.generateList(3); //TODO Implements functionality
// mock.methodReturnMockedType().a.and.returnValue(2);
Expand Down
Loading