Skip to content

Commit

Permalink
Merge branch 'master' into enhancement/add-factory-property-on-mock
Browse files Browse the repository at this point in the history
  • Loading branch information
martinjlowm authored May 9, 2020
2 parents 38a3d04 + 7480aad commit a363ca0
Show file tree
Hide file tree
Showing 54 changed files with 467 additions and 133 deletions.
43 changes: 33 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,36 @@ jobs:
node-version: [10.x]

steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: npm install, build, and test
run: |
npm install
env:
CI: true
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/
- name: commit configuration
run: |
git config --global user.name 'typescripttdd'
git config --global user.email '[email protected]'
- name: npm install and version
run: |
npm install
npm version ${GITHUB_REF##*/}
- name: Push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN_TYPESCRIPTTDD }}
- name: Register Token
run: |
echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" > /home/runner/work/_temp/.npmrc
echo "_auth=$NODE_AUTH_TOKEN" >> /home/runner/work/_temp/.npmrc
echo "email=<[email protected]>" >> /home/runner/work/_temp/.npmrc
echo "always-auth=true" >> /home/runner/work/_temp/.npmrc
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish
run: |
npm run preparePublish
cd dist
npm publish
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Test

on: [push]
on: [push, pull_request]

jobs:
test:
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
<a name="1.6.0"></a>
# [1.6.0](https://github.com/Typescript-TDD/ts-auto-mock/compare/v1.5.891...v1.6.0) (2020-04-11)



<a name="1.5.89"></a>
## [1.5.89](https://github.com/Typescript-TDD/ts-auto-mock/compare/v1.5.88...v1.5.89) (2020-04-11)



<a name="1.5.8"></a>
## [1.5.8](https://github.com/Typescript-TDD/ts-auto-mock/compare/v1.5.6...v1.5.8) (2020-03-29)

Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
[![npm version](https://badge.fury.io/js/ts-auto-mock.svg)](https://badge.fury.io/js/ts-auto-mock)
[![Downloads](https://img.shields.io/npm/dw/ts-auto-mock.svg)](https://www.npmjs.com/package/ts-auto-mock)


![slack](docs/slack_small.png) Need help? Join us on [Slack](https://join.slack.com/t/typescripttdd/shared_invite/enQtODk3MjQwNzUwNTk2LTMzNjdlZTNmMmY3Nzg2NDNiZDA1YzJmZjk2NjcwZjQwODQ3YzE5NGZjM2Q4MzZjYWNiMWE4MGU0NjEzM2E5YzE)

A Typescript transformer that will allow you to create mock for any types (Interfaces, Classes, ...) without need to create manual fakes/mocks.
Expand Down Expand Up @@ -36,6 +35,10 @@ mock.details // "{phone: 0} "
## [Changelog](CHANGELOG.md)

## [Roadmap](https://github.com/Typescript-TDD/ts-auto-mock/wiki/Roadmap)

## Do you want to contribute?
* [Check how to make changes to the code base](https://typescript-tdd.github.io/ts-auto-mock/local-development)

## Authors

* [**Vittorio Guerriero**](https://github.com/uittorio)
Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion data/definitelyTyped/list.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"id":"756c5f8d-be26-43fc-8831-0a12df4c972e.json","initialDate":"2020-01-11T17:17:58.333Z","lastUpdatedDate":"2020-01-16T00:31:39.270Z","typesProcessed":9999},{"id":"9749238a-cba9-4abd-9fac-f9d0d4ef90e3.json","initialDate":"2020-01-17T20:34:24.904Z","lastUpdatedDate":"2020-02-01T00:40:31.064Z","typesProcessed":9999},{"id":"45e2213b-fa9c-40d1-b066-89413f063ee3.json","initialDate":"2020-02-01T17:11:06.944Z","lastUpdatedDate":"2020-03-07T00:20:59.201Z","typesProcessed":6549},{"id":"532444dd-7414-42ad-8d30-3da2d2e6c9af.json","initialDate":"2020-03-08T08:08:21.638Z","lastUpdatedDate":"2020-03-17T00:55:23.522Z","typesProcessed":9999},{"id":"59fcd50b-ad93-43d9-8777-45cf4cb83abe.json","initialDate":"2020-03-29T00:47:04.041Z","lastUpdatedDate":"2020-04-10T00:53:38.538Z","typesProcessed":5500}]
[{"id":"756c5f8d-be26-43fc-8831-0a12df4c972e.json","initialDate":"2020-01-11T17:17:58.333Z","lastUpdatedDate":"2020-01-16T00:31:39.270Z","typesProcessed":9999},{"id":"9749238a-cba9-4abd-9fac-f9d0d4ef90e3.json","initialDate":"2020-01-17T20:34:24.904Z","lastUpdatedDate":"2020-02-01T00:40:31.064Z","typesProcessed":9999},{"id":"45e2213b-fa9c-40d1-b066-89413f063ee3.json","initialDate":"2020-02-01T17:11:06.944Z","lastUpdatedDate":"2020-03-07T00:20:59.201Z","typesProcessed":6549},{"id":"532444dd-7414-42ad-8d30-3da2d2e6c9af.json","initialDate":"2020-03-08T08:08:21.638Z","lastUpdatedDate":"2020-03-17T00:55:23.522Z","typesProcessed":9999},{"id":"59fcd50b-ad93-43d9-8777-45cf4cb83abe.json","initialDate":"2020-03-29T00:47:04.041Z","lastUpdatedDate":"2020-04-14T00:29:04.679Z","typesProcessed":6680}]
2 changes: 1 addition & 1 deletion data/performance.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions definitelyTypedTests/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion definitelyTypedTests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"license": "ISC",
"dependencies": {
"dotenv": "^8.2.0",
"ttypescript": "^1.5.8",
"ttypescript": "^1.5.10",
"typescript": "^3.8.3"
}
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ts-auto-mock",
"version": "1.5.8",
"version": "1.6.0",
"description": "Typescript transformer to unlock automatic mock creation for interfaces and classes",
"scripts": {
"build:transformer": "webpack --config config/modules/transformer/webpack.js",
Expand Down
8 changes: 7 additions & 1 deletion src/extension/extensionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ export class ExtensionHandler<TMock> {
extension: Extension<TMock, TMockedPropertyHandler>,
): TMockedPropertyHandler;
public get<TPropName extends keyof TMock, TMockedPropertyHandler>(
extensionOrPropertyName: Function | TPropName,
extensionOrPropertyName: Extension<TMock, TMockedPropertyHandler> | TPropName,
maybePropertyHandler?: AsMockedPropertyHandler<TMockedPropertyHandler, TMock, TPropName>,
): TMockedPropertyHandler {
if (isFunction(extensionOrPropertyName)) {
return extensionOrPropertyName(this._mock);
}

if (!maybePropertyHandler) {
throw new Error(
`It looks like you are trying to get an extension for ${extensionOrPropertyName} without specifying the handler.`,
);
}

return maybePropertyHandler(this._mock[extensionOrPropertyName], this._mock, extensionOrPropertyName);
}
}
15 changes: 7 additions & 8 deletions src/options/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ export interface TsAutoMockOptions {
cacheBetweenTests: TsAutoMockCacheOptions;
}

let options: TsAutoMockOptions = null;
let tsAutoMockOptions: TsAutoMockOptions = defaultOptions;

export function SetTsAutoMockOptions(_options: TsAutoMockOptions): void {
options = _options;
export function SetTsAutoMockOptions(options: TsAutoMockOptions): void {
tsAutoMockOptions = {
...defaultOptions,
...options,
};
}

export function GetOptionByKey<T extends keyof TsAutoMockOptions>(optionKey: T): TsAutoMockOptions[T] {
if (options) {
return options.hasOwnProperty(optionKey) ? options[optionKey] : defaultOptions[optionKey];
}

return defaultOptions[optionKey];
return tsAutoMockOptions[optionKey];
}
30 changes: 25 additions & 5 deletions src/transformer/base/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import {
isFunctionFromThisLibrary,
} from '../matcher/matcher';

export type Visitor = (node: ts.CallExpression, declaration: ts.FunctionDeclaration) => ts.Node;
export type Visitor = (node: ts.CallExpression & { typeArguments: ts.NodeArray<ts.TypeNode> }, declaration: ts.FunctionDeclaration) => ts.Node;

export function baseTransformer(visitor: Visitor, customFunctions: CustomFunction[]): (program: ts.Program, options?: TsAutoMockOptions) => ts.TransformerFactory<ts.SourceFile> {
return (program: ts.Program, options?: TsAutoMockOptions): ts.TransformerFactory<ts.SourceFile> => {
SetTsAutoMockOptions(options);
if (options) {
SetTsAutoMockOptions(options);
}

SetTypeChecker(program.getTypeChecker());
SetProgram(program);

Expand All @@ -37,18 +40,35 @@ function visitNodeAndChildren(node: ts.Node, context: ts.TransformationContext,
return ts.visitEachChild(visitNode(node, visitor, customFunctions), (childNode: ts.Node) => visitNodeAndChildren(childNode, context, visitor, customFunctions), context);
}

function isObjectWithProperty<T extends {}, K extends keyof T>(
obj: T,
key: K,
): obj is T & Required<{ [key in K]: T[K] }> {
return typeof obj[key] !== 'undefined';
}

function visitNode(node: ts.Node, visitor: Visitor, customFunctions: CustomFunction[]): ts.Node {
if (!ts.isCallExpression(node)) {
return node;
}

const signature: ts.Signature = TypescriptHelper.getSignatureOfCallExpression(node);
const signature: ts.Signature | undefined = TypescriptHelper.getSignatureOfCallExpression(node);

if (!isFunctionFromThisLibrary(signature, customFunctions)) {
if (!signature || !isFunctionFromThisLibrary(signature, customFunctions)) {
return node;
}

const nodeToMock: ts.TypeNode = node.typeArguments[0];
if (!isObjectWithProperty(node, 'typeArguments') || !node.typeArguments?.length) {
const mockFunction: string = node.getText();

throw new Error(
`It seems you've called \`${mockFunction}' without specifying a type argument to mock. ` +
`Please refer to the documentation on how to use \`${mockFunction}': ` +
'https://github.com/Typescript-TDD/ts-auto-mock#quick-overview'
);
}

const [nodeToMock]: ts.NodeArray<ts.TypeNode> = node.typeArguments;

MockDefiner.instance.setFileNameFromNode(nodeToMock);
MockDefiner.instance.setTsAutoMockImportIdentifier();
Expand Down
6 changes: 6 additions & 0 deletions src/transformer/descriptor/descriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ export function GetDescriptor(node: ts.Node, scope: Scope): ts.Expression {
case ts.SyntaxKind.Identifier:
return GetIdentifierDescriptor(node as ts.Identifier, scope);
case ts.SyntaxKind.ThisType:
if (!scope.currentMockKey) {
throw new Error(
`The transformer attempted to look up a mock factory call for \`${node.getText()}' without a mock key.`,
);
}

return GetMockFactoryCallForThis(scope.currentMockKey);
case ts.SyntaxKind.ImportSpecifier:
return GetImportDescriptor(node as ts.ImportSpecifier, scope);
Expand Down
34 changes: 25 additions & 9 deletions src/transformer/descriptor/helper/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ export namespace TypescriptHelper {

export function GetDeclarationFromNode(node: ts.Node): ts.Declaration {
const typeChecker: ts.TypeChecker = TypeChecker();
const symbol: ts.Symbol = typeChecker.getSymbolAtLocation(node);
const symbol: ts.Symbol | undefined = typeChecker.getSymbolAtLocation(node);

if (!symbol) {
throw new Error(
`The type checker failed to look up a symbol for \`${node.getText()}'. ` +
'Perhaps, the checker was searching an outdated source.',
);
}

return GetDeclarationFromSymbol(symbol);
}
Expand Down Expand Up @@ -56,11 +63,13 @@ export namespace TypescriptHelper {
export function GetParameterOfNode(node: ts.EntityName): ts.NodeArray<ts.TypeParameterDeclaration> {
const declaration: ts.Declaration = GetDeclarationFromNode(node);

return (declaration as Declaration).typeParameters;
const { typeParameters = ts.createNodeArray([]) }: Declaration = (declaration as Declaration);

return typeParameters;
}

export function GetTypeParameterOwnerMock(declaration: ts.Declaration): ts.Declaration {
const typeDeclaration: ts.Declaration = ts.getTypeParameterOwner(declaration);
export function GetTypeParameterOwnerMock(declaration: ts.Declaration): ts.Declaration | undefined {
const typeDeclaration: ts.Declaration | undefined = ts.getTypeParameterOwner(declaration);

// THIS IS TO FIX A MISSING IMPLEMENTATION IN TYPESCRIPT https://github.com/microsoft/TypeScript/blob/ba5e86f1406f39e89d56d4b32fd6ff8de09a0bf3/src/compiler/utilities.ts#L5138
if (typeDeclaration && (typeDeclaration as Declaration).typeParameters) {
Expand All @@ -79,7 +88,14 @@ export namespace TypescriptHelper {
return propertyName.text;
}

const symbol: ts.Symbol = TypeChecker().getSymbolAtLocation(propertyName);
const symbol: ts.Symbol | undefined = TypeChecker().getSymbolAtLocation(propertyName);

if (!symbol) {
throw new Error(
`The type checker failed to look up symbol for property: ${propertyName.getText()}.`,
);
}

return symbol.escapedName.toString();
}

Expand All @@ -88,7 +104,7 @@ export namespace TypescriptHelper {
}


export function getSignatureOfCallExpression(node: ts.CallExpression): ts.Signature {
export function getSignatureOfCallExpression(node: ts.CallExpression): ts.Signature | undefined {
const typeChecker: ts.TypeChecker = TypeChecker();

return typeChecker.getResolvedSignature(node);
Expand All @@ -109,9 +125,9 @@ export namespace TypescriptHelper {

function GetDeclarationsForImport(node: ImportDeclaration): ts.Declaration[] {
const typeChecker: ts.TypeChecker = TypeChecker();
const symbol: ts.Symbol = typeChecker.getSymbolAtLocation(node.name);
const originalSymbol: ts.Symbol = typeChecker.getAliasedSymbol(symbol);
const symbol: ts.Symbol | undefined = node.name && typeChecker.getSymbolAtLocation(node.name);
const originalSymbol: ts.Symbol | undefined = symbol && typeChecker.getAliasedSymbol(symbol);

return originalSymbol.declarations;
return originalSymbol?.declarations ?? [];
}
}
9 changes: 8 additions & 1 deletion src/transformer/descriptor/indexedAccess/indexedAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ export function GetIndexedAccessTypeDescriptor(node: ts.IndexedAccessTypeNode, s
}

if (propertyName !== null) {
const propertySymbol: ts.Symbol = typeChecker.getPropertyOfType(typeChecker.getTypeFromTypeNode(node.objectType), propertyName);
const propertySymbol: ts.Symbol | undefined = typeChecker.getPropertyOfType(typeChecker.getTypeFromTypeNode(node.objectType), propertyName);

if (!propertySymbol) {
throw new Error(
`The type checker failed to look up symbol for property: \`${propertyName}' of \`${node.getText()}'.`,
);
}

return GetDescriptor(TypescriptHelper.GetDeclarationFromSymbol(propertySymbol), scope);
}

Expand Down
10 changes: 8 additions & 2 deletions src/transformer/descriptor/mapped/mapped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ import { GetMockPropertiesFromDeclarations } from '../mock/mockProperties';
import { GetTypes } from '../type/type';

export function GetMappedDescriptor(node: ts.MappedTypeNode, scope: Scope): ts.Expression {
const typeParameter: ts.TypeNode = node.typeParameter.constraint;
const typeParameter: ts.TypeNode | undefined = node.typeParameter.constraint;
const typeChecker: ts.TypeChecker = TypeChecker();
const types: ts.Node[] = GetTypes(ts.createNodeArray([typeParameter]), scope);

const parameters: ts.TypeNode[] = [];
if (typeParameter) {
parameters.push(typeParameter);
}

const types: ts.Node[] = GetTypes(ts.createNodeArray(parameters), scope);

const properties: ts.PropertyDeclaration[] = types.reduce((acc: ts.PropertyDeclaration[], possibleType: ts.Node) => {
if (ts.isLiteralTypeNode(possibleType)) {
Expand Down
10 changes: 7 additions & 3 deletions src/transformer/descriptor/method/bodyReturnType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export function GetReturnTypeFromBodyDescriptor(node: ts.ArrowFunction | ts.Func
}

export function GetReturnNodeFromBody(node: ts.FunctionLikeDeclaration): ts.Node {
let returnValue: ts.Node;
let returnValue: ts.Node | undefined;

const functionBody: ts.ConciseBody = node.body;
const functionBody: ts.ConciseBody | undefined = node.body;

if (ts.isBlock(functionBody)) {
if (functionBody && ts.isBlock(functionBody)) {
const returnStatement: ts.ReturnStatement = GetReturnStatement(functionBody);

if (returnStatement) {
Expand All @@ -24,6 +24,10 @@ export function GetReturnNodeFromBody(node: ts.FunctionLikeDeclaration): ts.Node
returnValue = node.body;
}

if (!returnValue) {
throw new Error(`Failed to determine the return value of ${node.getText()}.`);
}

return returnValue;
}

Expand Down
4 changes: 4 additions & 0 deletions src/transformer/descriptor/method/functionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { GetMethodDescriptor } from './method';
export function GetFunctionTypeDescriptor(node: ts.FunctionTypeNode | ts.CallSignatureDeclaration | ts.ConstructSignatureDeclaration, scope: Scope): ts.Expression {
const property: ts.PropertyName = PropertySignatureCache.instance.get();

if (!node.type) {
throw new Error(`No type was declared for ${node.getText()}.`);
}

const returnValue: ts.Expression = GetDescriptor(node.type, scope);

return GetMethodDescriptor(property, returnValue);
Expand Down
Loading

0 comments on commit a363ca0

Please sign in to comment.