Skip to content

Commit

Permalink
fix: export types (#402)
Browse files Browse the repository at this point in the history
* fix: export types

* build: move steps into webpack conf

* chore: move types to module namespace
  • Loading branch information
iamogbz authored Sep 23, 2020
1 parent 56c50b5 commit ea2615b
Show file tree
Hide file tree
Showing 14 changed files with 1,062 additions and 205 deletions.
8 changes: 8 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.circleci/
artifacts/
dist/
docs/
internals/
lib/
node_modules/
server/
49 changes: 49 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"prettier/@typescript-eslint"
],
"env": {
"browser": true,
"node": true,
"jest": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"globals": {
"browser": false
},
"rules": {
"@typescript-eslint/explicit-function-return-type": 0,
"class-methods-use-this": 0,
"no-console": 1,
"no-param-reassign": ["error", { "props": false }],
"no-trailing-spaces": 2,
"no-use-before-define": [2, {
"functions": false
}],
"object-curly-newline": [2, {
"multiline": true,
"consistent": true
}],
"object-property-newline": [2, {
"allowAllPropertiesOnSameLine": true
}],
"prettier/prettier": 2
},
"settings": {
"import/resolver": {
"node": {
"paths": ["src"]
}
}
}
}
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"bracketSpacing": true,
"jsxBracketSameLine": false,
"semi": true,
"singleQuote": false,
"tabWidth": 4,
"trailingComma": "all"
}
933 changes: 853 additions & 80 deletions package-lock.json

Large diffs are not rendered by default.

39 changes: 21 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@
"name": "jest-mock-props",
"version": "0.0.0",
"description": "Mock module and object properties",
"main": "main.js",
"types": "main.d.ts",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib",
"typings"
],
"scripts": {
"build": "webpack --mode=production",
"build-watch": "webpack --mode=development --watch",
"build-watch": "webpack --mode=development --watch",
"clean": "rm -rf ./lib",
"commit": "git-cz",
"commitlint": "commitlint-travis",
"lint": "eslint",
"compile-types": "tsc --emitDeclarationOnly --project tsconfig.prod.json",
"lint": "eslint . --ext .js,.ts",
"release": "semantic-release",
"test": "jest",
"test-watch": "jest --watch"
Expand All @@ -30,8 +36,7 @@
"jest": {
"preset": "ts-jest",
"moduleDirectories": [
"./src",
"./tests",
".",
"./node_modules"
],
"setupFilesAfterEnv": [
Expand All @@ -45,9 +50,6 @@
"coverageDirectory": "./artifacts/coverage"
},
"commitlint": {
"rules": {
"header-max-length": [0, "always", 0]
},
"extends": [
"@commitlint/config-conventional"
]
Expand All @@ -58,14 +60,9 @@
}
},
"lint-staged": {
"*.js": [
"eslint"
],
"*.ts": [
"tslint"
],
"*.{js,ts}": [
"jest --bail --findRelatedTests"
"npm run lint",
"npm test -- --bail --findRelatedTests"
]
},
"husky": {
Expand Down Expand Up @@ -114,6 +111,8 @@
"@types/node": "^14.11.2",
"@types/source-map": "^0.5.2",
"@types/webpack": "^4.41.22",
"@typescript-eslint/eslint-plugin": "^2.34.0",
"@typescript-eslint/parser": "^2.34.0",
"acorn": "^8.0.1",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
Expand All @@ -122,6 +121,10 @@
"coveralls": "^3.1.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.9.0",
"eslint-config-airbnb": "^18.2.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^3.1.4",
"husky": "^4.3.0",
"jest": "^24.9.0",
"lint-staged": "^10.4.0",
Expand All @@ -130,9 +133,9 @@
"semantic-release": "^17.1.2",
"ts-jest": "^24.3.0",
"ts-node": "^9.0.0",
"tslint": "^6.1.3",
"typescript": "^4.0.3",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12"
"webpack-cli": "^3.3.12",
"webpack-compiler-plugin": "^1.1.1"
}
}
21 changes: 0 additions & 21 deletions src/@types/index.d.ts

This file was deleted.

9 changes: 9 additions & 0 deletions src/globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { IsMockProp, SpyOnProp } from "jest-mock-props";

declare global {
namespace jest {
const isMockProp: IsMockProp;
const spyOnProp: SpyOnProp;
}
}
85 changes: 47 additions & 38 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import { IsMockProp, Obj, Spyable, SpyOnProp, ValueOf } from "jest-mock-props";

export const messages = {
error: {
invalidSpy: (o: any) => {
invalidSpy: (o: object): string => {
const helpfulValue = `${o ? typeof o : ""}'${o}'`;
return `Cannot spyOn on a primitive value; ${helpfulValue} given.`;
},
noMethodSpy: (p: string) =>
noMethodSpy: (p: string): string =>
`Cannot spy on the property '${p}' because it is a function. Please use \`jest.spyOn\`.`,
noMockClear: "Cannot `mockClear` on property spy.",
noUnconfigurableSpy: (p: string) =>
noUnconfigurableSpy: (p: string): string =>
`Cannot spy on the property '${p}' because it is not configurable`,
},
warn: {
noUndefinedSpy: (p: string) =>
noUndefinedSpy: (p: string): string =>
`Spying on an undefined property '${p}'.`,
},
};

export const log = (...args: any[]) => log.default(...args);
log.default = log.warn = (...args: any[]) => console.warn(...args); // tslint:disable-line
export const log = (...args: unknown[]): void => log.default(...args);
// eslint-disable-next-line no-console
log.default = log.warn = (...args: unknown[]): void => console.warn(...args);

const spiedOn: Map<object, Map<string, MockProp>> = new Map();
const getAllSpies = (): Set<MockProp> => {
const spies: Set<MockProp> = new Set();
const spiedOn: Map<
Spyable,
Map<string, MockProp<ValueOf<Spyable>>>
> = new Map();
const getAllSpies = () => {
const spies: Set<MockProp<ValueOf<Spyable>>> = new Set();
for (const spiedProps of spiedOn.values()) {
for (const spy of spiedProps.values()) {
spies.add(spy);
Expand All @@ -30,15 +36,15 @@ const getAllSpies = (): Set<MockProp> => {
return spies;
};

class MockProp implements MockProp {
class MockProp<T> implements MockProp<T> {
private initialPropDescriptor: PropertyDescriptor;
private initialPropValue: any;
private object: AnyObject;
private initialPropValue: T;
private object: Obj<T>;
private propName: string;
private propValue: any;
private propValues: any[] = [];
private propValue: T;
private propValues: T[] = [];

constructor({ object, propName }: { object: AnyObject; propName: string }) {
constructor({ object, propName }: { object: Obj<T>; propName: string }) {
this.initialPropDescriptor = this.validate({ object, propName });
this.object = object;
this.propName = propName;
Expand All @@ -50,11 +56,11 @@ class MockProp implements MockProp {

public mockClear = (): void => {
throw new Error(messages.error.noMockClear);
}
};

public mockReset = (): void => {
this.mockValue(this.initialPropValue);
}
};

public mockRestore = (): void => {
if (this.initialPropDescriptor) {
Expand All @@ -71,24 +77,24 @@ class MockProp implements MockProp {
delete this.object[this.propName];
}
this.deregister();
}
};

/**
* Set the value of the mocked property
*/
public mockValue = (value: any): MockProp => {
public mockValue = (value: T): MockProp<T> => {
this.propValues = [];
this.propValue = value;
return this;
}
};

/**
* Next value returned when the property is accessed
*/
public mockValueOnce = (value: any): MockProp => {
public mockValueOnce = (value: T): MockProp<T> => {
this.propValues.push(value);
return this;
}
};

/**
* Determine if the object property can and should be mocked
Expand All @@ -97,7 +103,7 @@ class MockProp implements MockProp {
object,
propName,
}: {
object: AnyObject;
object: Obj<T>;
propName: string;
}): PropertyDescriptor => {
const acceptedTypes: Set<string> = new Set(["function", "object"]);
Expand All @@ -120,18 +126,18 @@ class MockProp implements MockProp {
throw new Error(messages.error.noMethodSpy(propName));
}
return descriptor;
}
};

/**
* Attach spy to object property
*/
private attach = () => {
private attach = (): void => {
Object.defineProperty(this.object, this.propName, {
configurable: true,
get: this.nextValue,
set: this.mockValue,
});
}
};

/**
* Track spy
Expand All @@ -141,43 +147,46 @@ class MockProp implements MockProp {
spiedOn.set(this.object, new Map());
}
spiedOn.get(this.object).set(this.propName, this);
}
};

/**
* Stop tracking spy
*/
private deregister = (): void => {
spiedOn.get(this.object).delete(this.propName);
}
};

/**
* Shift and return the first next, defaulting to the mocked value
*/
private nextValue = (): any => this.propValues.shift() || this.propValue;
private nextValue = (): T => this.propValues.shift() || this.propValue;
}

const isMockProp = (object: any, propName: string): boolean => {
export const isMockProp: IsMockProp = (object, propName) => {
const spiedOnProps = spiedOn.get(object);
return Boolean(spiedOnProps && spiedOnProps.has(propName));
};

const resetAll = (): void => getAllSpies().forEach(spy => spy.mockReset());
const restoreAll = (): void => getAllSpies().forEach(spy => spy.mockRestore());
export const resetAllMocks = () =>
getAllSpies().forEach((spy) => spy.mockReset());

export const restoreAllMocks = () =>
getAllSpies().forEach((spy) => spy.mockRestore());

const spyOnProp = (object: AnyObject, propName: string): MockProp => {
export const spyOnProp: SpyOnProp = (object, propName) => {
if (isMockProp(object, propName)) {
return spiedOn.get(object).get(propName);
}
return new MockProp({ object, propName });
};

export const extend = (jestInstance: typeof jest) => {
const resetAllMocks = jestInstance.resetAllMocks;
const restoreAllMocks = jestInstance.restoreAllMocks;
export const extend = (jestInstance: typeof jest): void => {
const jestResetAll = jestInstance.resetAllMocks;
const jestRestoreAll = jestInstance.restoreAllMocks;
Object.assign(jestInstance, {
isMockProp,
resetAllMocks: (): void => resetAllMocks() && resetAll(),
restoreAllMocks: (): void => restoreAllMocks() && restoreAll(),
resetAllMocks: () => jestResetAll() && resetAllMocks(),
restoreAllMocks: () => jestRestoreAll() && restoreAllMocks(),
spyOnProp,
});
};
Loading

0 comments on commit ea2615b

Please sign in to comment.