diff --git a/.gitignore b/.gitignore index 3220058e..bff6dc9a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ npm-debug.log* /lib /coverage /dist +/ts .node-version .vscode diff --git a/package-lock.json b/package-lock.json index d7bcd8cb..a62dce9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,24 @@ } } }, + "@types/jest": { + "version": "22.2.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-22.2.2.tgz", + "integrity": "sha512-Dt7aifQmvMPTLVimzvfQ99qUn4zeSDCQarFNV4otfDLYu0RFdSRBnqSLgksoAnsRL88xJ/UBKbd66iP2XIab0w==", + "dev": true + }, + "@types/lodash": { + "version": "4.14.106", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.106.tgz", + "integrity": "sha512-tOSvCVrvSqFZ4A/qrqqm6p37GZoawsZtoR0SJhlF7EonNZUgrn8FfT+RNQ11h+NUpMt6QVe36033f3qEKBwfWA==", + "dev": true + }, + "@types/node": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.0.tgz", + "integrity": "sha512-h3YZbOq2+ZoDFI1z8Zx0Ck/xRWkOESVaLdgLdd/c25mMQ1Y2CAkILu9ny5A15S5f32gGcQdaUIZ2jzYr8D7IFg==", + "dev": true + }, "JSONStream": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", @@ -1349,9 +1367,9 @@ "dev": true }, "eslint-plugin-typescript": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-typescript/-/eslint-plugin-typescript-0.9.0.tgz", - "integrity": "sha512-zcEAftbsnd1xEyLT4ZOXphbgWO2SFZduU3Mrjj6FA7Up2qOSc2kD++ICxvIgh4/8hKOaq/xJsNNOoDUJSVNyZg==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-typescript/-/eslint-plugin-typescript-0.11.0.tgz", + "integrity": "sha512-U3+fjhwmBZMPMQNC962tHgEBVUwflEuFkKNL0NMAyuUffdd3lIvGqHF+VG27p9qA7yR/mkxwIjiXYi+OB3e4DQ==", "dev": true, "requires": { "requireindex": "1.1.0" @@ -5670,6 +5688,122 @@ "tsconfig": "5.0.3" } }, + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", + "dev": true + }, + "tslint": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", + "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.3.2", + "commander": "2.14.1", + "diff": "3.4.0", + "glob": "7.1.2", + "js-yaml": "3.10.0", + "minimatch": "3.0.4", + "resolve": "1.5.0", + "semver": "5.5.0", + "tslib": "1.9.0", + "tsutils": "2.25.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", + "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.3.0" + } + }, + "supports-color": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", + "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "tslint-config-standard": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tslint-config-standard/-/tslint-config-standard-7.0.0.tgz", + "integrity": "sha512-QCrLt8WwiRgZpRSgRsk6cExy8/Vipa/5fHespm4Q1ly90EB6Lni04Ub8dkEW10bV3fPN3SkxEwj41ZOe/knCZA==", + "dev": true, + "requires": { + "tslint-eslint-rules": "4.1.1" + } + }, + "tslint-eslint-rules": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-4.1.1.tgz", + "integrity": "sha1-fDDniC8mvCdr/5HSOEl1xp2viLo=", + "dev": true, + "requires": { + "doctrine": "0.7.2", + "tslib": "1.9.0", + "tsutils": "1.9.1" + }, + "dependencies": { + "doctrine": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", + "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", + "dev": true, + "requires": { + "esutils": "1.1.6", + "isarray": "0.0.1" + } + }, + "esutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", + "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "tsutils": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz", + "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=", + "dev": true + } + } + }, + "tsutils": { + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.25.1.tgz", + "integrity": "sha512-xHiTER5XIRYlWbylk4vpGZSYo8FUTVn32bAxxM1rJopXs2DYG7lWp40LqNvM3iMNpXa50wmnC9bFXsPTV6xAiQ==", + "dev": true, + "requires": { + "tslib": "1.9.0" + } + }, "tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", diff --git a/package.json b/package.json index 27776da1..81aa12c2 100644 --- a/package.json +++ b/package.json @@ -13,16 +13,16 @@ "src", "lib", "dist", - "index.d.ts" + "ts" ], "config": { "build_file": "dist/testdouble.js" }, "scripts": { - "clean": "rimraf dist lib coverage", + "clean": "rimraf dist lib coverage ts", "postclean": "mkdirp dist", - "compile:browser": "cross-conf-env browserify src/index.js --standalone td --outfile $npm_package_config_build_file -p tsify -p headerify", - "compile:node": "tsc", + "compile:browser": "cross-conf-env browserify src/index.ts --standalone td --outfile $npm_package_config_build_file -p tsify -p headerify", + "compile:node": "tsc --project ./tsconfig.node.json", "precompile": "npm run clean", "compile": "run-s compile:node compile:browser", "cover": "nyc --reporter=lcov --reporter=text-summary --reporter=html npm test", @@ -30,7 +30,7 @@ "cover:ci": "if test \"`node -v | awk '{print substr($1, 2, 1)}'`\" = \"8\" ; then run-s cover cover:report ; fi", "style": "run-p style:js style:ts", "style:js": "standard --fix", - "style:ts": "standard --fix --parser typescript-eslint-parser --plugin typescript \"**/*.ts\"", + "style:ts": "tslint './src/**/*.ts'", "test": "teenytest --helper test/helper.js \"test/**/*.test.{js,ts}\"", "test:all": "run-s test test:example", "test:unit": "teenytest --helper test/helper.js \"test/unit/**/*.test.{js,ts}\"", @@ -51,8 +51,8 @@ "prepare": "npm run compile" }, "browser": { - "./src/replace/module.js": "./src/replace/module.browser.js", - "quibble": "./src/quibble.browser.js" + "./src/replace/module.ts": "./src/replace/module.browser.ts", + "quibble": "./src/quibble.browser.ts" }, "standard": { "globals": [ @@ -86,10 +86,13 @@ "stringify-object-es5": "^2.5.0" }, "devDependencies": { + "@types/jest": "^22.2.2", + "@types/lodash": "^4.14.106", + "@types/node": "^9.6.0", "browserify": "^16.1.0", "codeclimate-test-reporter": "^0.5.0", "cross-conf-env": "^1.1.2", - "eslint-plugin-typescript": "^0.9.0", + "eslint-plugin-typescript": "^0.11.0", "headerify": "^1.0.1", "is-number": "^5.0.0", "mkdirp": "^0.5.1", @@ -102,6 +105,8 @@ "testdouble": "^3.5.0", "ts-node": "^5.0.0", "tsify": "^3.0.4", + "tslint": "^5.9.1", + "tslint-config-standard": "^7.0.0", "typescript": "^2.7.2", "typescript-eslint-parser": "^14.0.0" }, @@ -111,7 +116,7 @@ "lib": "./lib", "src": "./src" }, - "typings": "./index.d.ts", + "typings": "./ts/index.d.ts", "keywords": [ "tdd", "bdd", diff --git a/src/args-match.js b/src/args-match.ts similarity index 70% rename from src/args-match.js rename to src/args-match.ts index 58bb0356..20c4cab9 100644 --- a/src/args-match.js +++ b/src/args-match.ts @@ -1,7 +1,11 @@ import _ from './wrap/lodash' import isMatcher from './matchers/is-matcher' -export default (expectedArgs, actualArgs, config = {}) => { +export interface Config { + allowMatchers?: boolean +} + +export default (expectedArgs, actualArgs, config: Config = {}) => { if (arityMismatch(expectedArgs, actualArgs, config)) { return false } else if (config.allowMatchers !== false) { @@ -11,14 +15,14 @@ export default (expectedArgs, actualArgs, config = {}) => { } } -var arityMismatch = (expectedArgs, actualArgs, config) => +const arityMismatch = (expectedArgs, actualArgs, config) => expectedArgs.length !== actualArgs.length && !config.ignoreExtraArgs -var equalsWithMatchers = (expectedArgs, actualArgs) => +const equalsWithMatchers = (expectedArgs, actualArgs) => _.every(expectedArgs, (expectedArg, key) => argumentMatchesExpectation(expectedArg, actualArgs[key])) -var argumentMatchesExpectation = (expectedArg, actualArg) => { +const argumentMatchesExpectation = (expectedArg, actualArg) => { if (isMatcher(expectedArg)) { return matcherTestFor(expectedArg)(actualArg) } else { @@ -30,5 +34,5 @@ var argumentMatchesExpectation = (expectedArg, actualArg) => { } } -var matcherTestFor = (matcher) => +const matcherTestFor = (matcher) => matcher.__matches diff --git a/src/callback.js b/src/callback.ts similarity index 85% rename from src/callback.js rename to src/callback.ts index bbc926d7..a69cf264 100644 --- a/src/callback.js +++ b/src/callback.ts @@ -1,5 +1,5 @@ import _ from './wrap/lodash' -import create from './matchers/create' +import create, { CreatedResult } from './matchers/create' const callback = create({ name: 'callback', @@ -10,7 +10,7 @@ const callback = create({ matcherInstance.args = matcherArgs matcherInstance.__testdouble_callback = true } -}) +}) as CreatedResult // Make callback itself quack like a matcher for its non-invoked use case. callback.__name = 'callback' diff --git a/src/config.js b/src/config.ts similarity index 72% rename from src/config.js rename to src/config.ts index 846a6ba0..19fa9235 100644 --- a/src/config.js +++ b/src/config.ts @@ -2,20 +2,31 @@ import _ from './wrap/lodash' import log from './log' import stringifyAnything from './stringify/anything' -const DEFAULTS = { +export interface ConfigObject { + ignoreWarnings?: boolean + promiseConstructor?: PromiseConstructor + suppressErrors?: boolean +} + +const DEFAULTS: ConfigObject = { ignoreWarnings: false, - promiseConstructor: global.Promise, + promiseConstructor: global.Promise as PromiseConstructor, suppressErrors: false } const DELETED_OPTIONS = ['extendWhenReplacingConstructors'] let configData = _.extend({}, DEFAULTS) -export default _.tap((overrides) => { +export interface Config { + (overrides?: ConfigObject): ConfigObject + reset: () => void +} + +export default _.tap(((overrides) => { deleteDeletedOptions(overrides) ensureOverridesExist(overrides) return _.extend(configData, overrides) -}, (config) => { +}) as Config, (config) => { config.reset = () => { configData = _.extend({}, DEFAULTS) } @@ -30,7 +41,7 @@ const deleteDeletedOptions = (overrides) => { }) } -var ensureOverridesExist = (overrides) => { +let ensureOverridesExist = (overrides) => { _.each(overrides, (val, key) => { if (!configData.hasOwnProperty(key)) { log.error('td.config', diff --git a/src/constructor.js b/src/constructor.js deleted file mode 100644 index 1c3ab18d..00000000 --- a/src/constructor.js +++ /dev/null @@ -1,19 +0,0 @@ -import _ from './wrap/lodash' -import tdFunction from './function' -import imitate from './imitate' - -export default (typeOrNames) => - _.isFunction(typeOrNames) - ? imitate(typeOrNames) - : fakeConstructorFromNames(typeOrNames) - -var fakeConstructorFromNames = (funcNames) => { - return _.tap(tdFunction('(unnamed constructor)'), (fakeConstructor) => { - fakeConstructor.prototype.toString = () => - '[test double instance of constructor]' - - _.each(funcNames, (funcName) => { - fakeConstructor.prototype[funcName] = tdFunction(`#${String(funcName)}`) - }) - }) -} diff --git a/src/constructor.ts b/src/constructor.ts new file mode 100644 index 00000000..51f53168 --- /dev/null +++ b/src/constructor.ts @@ -0,0 +1,33 @@ + +import { Constructor0, Constructor1, Constructor2, Constructor3, Constructor4 } from './types' + +import _ from './wrap/lodash' +import tdFunction from './function' +import imitate from './imitate' + +export interface ConstructorType { + (typeOrNames: (keyof T)[] | Constructor0): Constructor0 + (typeOrNames: Constructor1): Constructor1 + (typeOrNames: Constructor2): Constructor2 + (typeOrNames: Constructor3): Constructor3 + (typeOrNames: Constructor4): Constructor4 +} + +const constructor = ((typeOrNames: any) => +_.isFunction(typeOrNames) + ? imitate(typeOrNames) + : fakeConstructorFromNames(typeOrNames) +) as ConstructorType + +let fakeConstructorFromNames = (funcNames: (keyof T)[]): Constructor0 => { + return _.tap>(tdFunction('(unnamed constructor)') as any, (fakeConstructor) => { + fakeConstructor.prototype.toString = () => + '[test double instance of constructor]' + + _.each(funcNames, (funcName) => { + fakeConstructor.prototype[funcName] = tdFunction(`#${String(funcName)}`) + }) + }) +} + +export default constructor diff --git a/src/explain.js b/src/explain.ts similarity index 100% rename from src/explain.js rename to src/explain.ts diff --git a/src/function.js b/src/function.js deleted file mode 100644 index 13e1e28e..00000000 --- a/src/function.js +++ /dev/null @@ -1,30 +0,0 @@ -import _ from './wrap/lodash' -import calls from './store/calls' -import store from './store' -import stubbings from './store/stubbings' -import imitate from './imitate' - -export default function func (nameOrFunc, __optionalName) { - return _.isFunction(nameOrFunc) - ? imitate(nameOrFunc) - : createTestDoubleNamed(nameOrFunc || __optionalName) -} - -var createTestDoubleNamed = function (name) { - return _.tap(createTestDoubleFunction(), (testDouble) => { - const entry = store.for(testDouble, true) - if (name != null) { - entry.name = name - testDouble.toString = () => `[test double for "${name}"]` - } else { - testDouble.toString = () => '[test double (unnamed)]' - } - }) -} - -var createTestDoubleFunction = function () { - return function testDouble (...args) { - calls.log(testDouble, args, this) - return stubbings.invoke(testDouble, args, this) - } -} diff --git a/src/function.ts b/src/function.ts new file mode 100644 index 00000000..856ca52f --- /dev/null +++ b/src/function.ts @@ -0,0 +1,44 @@ + +import { Function0, Function1, Function2, Function3, Function4 } from 'lodash' + +import _ from './wrap/lodash' +import calls from './store/calls' +import store from './store' +import stubbings from './store/stubbings' +import imitate from './imitate' + +export interface FuncType { + (name?: string): Function0 + (func: Function0): Function0 + (func: Function1): Function1 + (func: Function2): Function2 + (func: Function3): Function3 + (func: Function4): Function4 +} + +const func: FuncType = (nameOrFunc, optionalName?) => { + return _.isFunction(nameOrFunc) + ? imitate(nameOrFunc) + : createTestDoubleNamed(nameOrFunc || optionalName) +} + +let createTestDoubleNamed = function (name) { + return _.tap(createTestDoubleFunction(), (testDouble) => { + const entry = store.for(testDouble, true) + if (name != null) { + entry.name = name + testDouble.toString = () => `[test double for "${name}"]` + } else { + testDouble.toString = () => '[test double (unnamed)]' + } + }) +} + +let createTestDoubleFunction = function () { + return function testDouble (...args) { + calls.log(testDouble, args, this) + return stubbings.invoke(testDouble, args, this) + } +} + +export default func diff --git a/src/function/create.js b/src/function/create.ts similarity index 75% rename from src/function/create.js rename to src/function/create.ts index 29581abc..f37659d6 100644 --- a/src/function/create.js +++ b/src/function/create.ts @@ -1,6 +1,6 @@ import Double from '../value/double' import generateFakeFunction from './generate-fake-function' -export default function create (name, real, parent) { +export default function create (name, real, parent?) { return Double.create(name, real, parent, generateFakeFunction) } diff --git a/src/function/generate-fake-function.js b/src/function/generate-fake-function.ts similarity index 100% rename from src/function/generate-fake-function.js rename to src/function/generate-fake-function.ts diff --git a/src/function/index.js b/src/function/index.js deleted file mode 100644 index ee12b493..00000000 --- a/src/function/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import _ from '../wrap/lodash' - -import create from './create' - -export default function func (nameOrFunc) { - if (_.isFunction(nameOrFunc)) { - return create(_.isEmpty(nameOrFunc.name) ? null : nameOrFunc.name, nameOrFunc).fake - } else { - return create(nameOrFunc, null).fake - } -} diff --git a/src/function/index.ts b/src/function/index.ts new file mode 100644 index 00000000..551eb4b2 --- /dev/null +++ b/src/function/index.ts @@ -0,0 +1,25 @@ + +import { Function0, Function1, Function2, Function3, Function4 } from 'lodash' + +import _ from '../wrap/lodash' + +import create from './create' + +export interface FuncType { + (name: string): Function0 + (func: Function0): Function0 + (func: Function1): Function1 + (func: Function2): Function2 + (func: Function3): Function3 + (func: Function4): Function4 +} + +const func: FuncType = (nameOrFunc) => { + if (_.isFunction(nameOrFunc)) { + return create(_.isEmpty(nameOrFunc.name) ? null : nameOrFunc.name, nameOrFunc).fake + } else { + return create(nameOrFunc, null).fake + } +} + +export default func diff --git a/src/imitate/create-imitation.js b/src/imitate/create-imitation.ts similarity index 65% rename from src/imitate/create-imitation.js rename to src/imitate/create-imitation.ts index 7a98077a..a7e6dc15 100644 --- a/src/imitate/create-imitation.js +++ b/src/imitate/create-imitation.ts @@ -3,7 +3,13 @@ import _ from '../wrap/lodash' import tdFunction from '../function' import isGenerator from './is-generator' -export default (original, names) => { +export interface CreateImitationType { + (original: IArguments, names: string[]): any[] + (original: T[], names: string[]): T[] + (original: T, names: string[]): T +} + +const createImitation: CreateImitationType = (original, names: string[]) => { if (_.isArray(original) || _.isArguments(original)) { return [] } else if (_.isFunction(original)) { @@ -17,3 +23,5 @@ export default (original, names) => { return _.clone(original) } } + +export default createImitation diff --git a/src/imitate/index.js b/src/imitate/index.ts similarity index 72% rename from src/imitate/index.js rename to src/imitate/index.ts index 3cd90b30..36123ccc 100644 --- a/src/imitate/index.js +++ b/src/imitate/index.ts @@ -1,12 +1,11 @@ -import * as Map from 'es6-map' import initializeNames from './initialize-names' import createImitation from './create-imitation' import overwriteChildren from './overwrite-children' -export default function imitate (original, names, encounteredObjects = new Map()) { +export default function imitate (original: T, originalNames?: string | string[], encounteredObjects = new Map()): T { if (encounteredObjects.has(original)) return encounteredObjects.get(original) - names = initializeNames(original, names) + const names = initializeNames(original, originalNames) const target = createImitation(original, names) encounteredObjects.set(original, target) overwriteChildren(original, target, (originalValue, name) => diff --git a/src/imitate/initialize-names.js b/src/imitate/initialize-names.ts similarity index 74% rename from src/imitate/initialize-names.js rename to src/imitate/initialize-names.ts index 1679b6b5..9858a2ae 100644 --- a/src/imitate/initialize-names.js +++ b/src/imitate/initialize-names.ts @@ -1,6 +1,6 @@ import _ from '../wrap/lodash' -export default (original, names) => { +export default (original: any, names: string | string[]): string[] => { if (_.isString(names)) return [names] if (names != null) return names if (_.isFunction(original) && original.name) { diff --git a/src/imitate/is-generator.js b/src/imitate/is-generator.ts similarity index 69% rename from src/imitate/is-generator.js rename to src/imitate/is-generator.ts index f06520c9..3d66b27c 100644 --- a/src/imitate/is-generator.js +++ b/src/imitate/is-generator.ts @@ -1,6 +1,7 @@ const generatorsAreSupported = (function () { try { - eval('(function* () {})') // eslint-disable-line + // tslint:disable-next-line:no-eval + eval('(function* () {})') return true } catch (e) { return false @@ -9,7 +10,8 @@ const generatorsAreSupported = (function () { const GeneratorFunction = (function () { if (!generatorsAreSupported) return - const func = eval('(function* () {})') // eslint-disable-line + // tslint:disable-next-line:no-eval + const func = eval('(function* () {})') return Object.getPrototypeOf(func).constructor })() diff --git a/src/imitate/overwrite-children/chain-prototype.js b/src/imitate/overwrite-children/chain-prototype.ts similarity index 100% rename from src/imitate/overwrite-children/chain-prototype.js rename to src/imitate/overwrite-children/chain-prototype.ts diff --git a/src/imitate/overwrite-children/copy-props.js b/src/imitate/overwrite-children/copy-props.ts similarity index 93% rename from src/imitate/overwrite-children/copy-props.js rename to src/imitate/overwrite-children/copy-props.ts index f25279c9..5dad20f8 100644 --- a/src/imitate/overwrite-children/copy-props.js +++ b/src/imitate/overwrite-children/copy-props.ts @@ -1,6 +1,6 @@ import _ from '../../wrap/lodash' -export default (target, props, visitor) => { +export default (target, props: PropertyDescriptorMap, visitor) => { Object.defineProperties(target, _.transform(props, (acc, descriptor, name) => { if (propOnTargetAndNotWritable(target, name, descriptor)) { if (name === 'prototype') { diff --git a/src/imitate/overwrite-children/gather-props.js b/src/imitate/overwrite-children/gather-props.ts similarity index 67% rename from src/imitate/overwrite-children/gather-props.js rename to src/imitate/overwrite-children/gather-props.ts index 862b72dd..775ff707 100644 --- a/src/imitate/overwrite-children/gather-props.js +++ b/src/imitate/overwrite-children/gather-props.ts @@ -1,8 +1,12 @@ import isFakeable from './is-fakeable' import isNativePrototype from './is-native-prototype' -export default function gatherProps (thing) { - const props = {} +export type PropertyDescriptors < T > = { + [P in keyof T]: PropertyDescriptor +} + +export default function gatherProps (thing: T): PropertyDescriptors { + const props = {} as PropertyDescriptors while (isFakeable(thing) && !isNativePrototype(thing)) { Object.getOwnPropertyNames(thing).forEach((propName) => { if (!props[propName] && propName !== 'constructor') { diff --git a/src/imitate/overwrite-children/index.js b/src/imitate/overwrite-children/index.js deleted file mode 100644 index abb6ec05..00000000 --- a/src/imitate/overwrite-children/index.js +++ /dev/null @@ -1,21 +0,0 @@ -import _ from '../../wrap/lodash' - -import isFakeable from './is-fakeable' -import gatherProps from './gather-props' -import copyProps from './copy-props' -import chainPrototype from './chain-prototype' - -export default (original, target, overwriteChild) => { - if (!isFakeable(target)) return - - if (_.isArray(target)) { - _.each(original, (item, index) => - target.push(overwriteChild(item, `[${index}]`)) - ) - } else { - copyProps(target, gatherProps(original), (name, originalValue) => - chainPrototype(original, target, name, originalValue, - overwriteChild(originalValue, `.${name}`)) - ) - } -} diff --git a/src/imitate/overwrite-children/index.ts b/src/imitate/overwrite-children/index.ts new file mode 100644 index 00000000..672bfdd5 --- /dev/null +++ b/src/imitate/overwrite-children/index.ts @@ -0,0 +1,30 @@ +import _ from '../../wrap/lodash' + +import isFakeable from './is-fakeable' +import gatherProps from './gather-props' +import copyProps from './copy-props' +import chainPrototype from './chain-prototype' + +export interface OverwriteChildrenType { + (original: T[], target: T[], overwriteChild: (val: T, name: string) => T) + (original: T, target: T, overwriteChild: (val: T[keyof T], name: string) => T[keyof T]) +} + +const overwriteChildren: OverwriteChildrenType = (original: T | T[], target: T | T[], overwriteChild: (val: T | T[keyof T], name: string) => T | T[keyof T]) => { + if (!isFakeable(target)) return + + if (_.isArray(target)) { + const overwrite = overwriteChild as (val: T, name: string) => T + _.each(original as T[], (item, index) => + target.push(overwrite(item, `[${index}]`)) + ) + } else { + const overwrite = overwriteChild as (val: T[keyof T], name: string) => T[keyof T] + copyProps(target as T, gatherProps(original as T), (name, originalValue) => + chainPrototype(original, target, name, originalValue, + overwrite(originalValue, `.${name}`)) + ) + } +} + +export default overwriteChildren diff --git a/src/imitate/overwrite-children/is-fakeable.js b/src/imitate/overwrite-children/is-fakeable.ts similarity index 100% rename from src/imitate/overwrite-children/is-fakeable.js rename to src/imitate/overwrite-children/is-fakeable.ts diff --git a/src/imitate/overwrite-children/is-native-prototype.js b/src/imitate/overwrite-children/is-native-prototype.ts similarity index 100% rename from src/imitate/overwrite-children/is-native-prototype.js rename to src/imitate/overwrite-children/is-native-prototype.ts diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 63af80d0..00000000 --- a/src/index.js +++ /dev/null @@ -1,32 +0,0 @@ -import tdFunction from './function' -import object from './object' -import constructor from './constructor' -import imitate from './imitate' -import when from './when' -import verify from './verify' -import matchers from './matchers' -import replace from './replace' -import explain from './explain' -import reset from './reset' -import config from './config' -import callback from './callback' -import version from './version' -import * as quibble from 'quibble' - -module.exports = { - function: tdFunction, - func: tdFunction, - object, - constructor, - imitate, - when, - verify, - matchers, - replace, - explain, - reset, - config, - callback, - version, - quibble -} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..3db01a57 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,16 @@ +import * as quibble from 'quibble' + +export { default as function, default as func } from './function' +export { default as object } from './object' +export { default as constructor } from './constructor' +export { default as imitate } from './imitate' +export { default as when } from './when' +export { default as verify } from './verify' +export { default as matchers } from './matchers' +export { default as replace } from './replace' +export { default as explain } from './explain' +export { default as reset } from './reset' +export { default as config } from './config' +export { default as callback } from './callback' +export { default as version } from './version' +export { quibble } diff --git a/src/log.js b/src/log.ts similarity index 73% rename from src/log.js rename to src/log.ts index 7eb7eaae..095eac6d 100644 --- a/src/log.js +++ b/src/log.ts @@ -1,24 +1,24 @@ import config from './config' export default { - warn (func, msg, url) { + warn (func: string, msg: string, url?: string) { if (!config().ignoreWarnings && typeof console === 'object' && console.warn) { console.warn(`Warning: testdouble.js - ${func} - ${msg}${withUrl(url)}`) } }, - error (func, msg, url) { + error (func: string, msg: string, url?: string) { if (!config().suppressErrors) { throw new Error(`Error: testdouble.js - ${func} - ${msg}${withUrl(url)}`) } }, - fail (msg) { + fail (msg: string) { throw new Error(msg) } } -var withUrl = (url) => +let withUrl = (url?: string) => url != null ? ` (see: ${url} )` : '' diff --git a/src/log/ensure-promise.js b/src/log/ensure-promise.ts similarity index 100% rename from src/log/ensure-promise.js rename to src/log/ensure-promise.ts diff --git a/src/matchers/builtin/anything.js b/src/matchers/builtin/anything.ts similarity index 63% rename from src/matchers/builtin/anything.js rename to src/matchers/builtin/anything.ts index 33f29cfa..e99b94a2 100644 --- a/src/matchers/builtin/anything.js +++ b/src/matchers/builtin/anything.ts @@ -1,4 +1,4 @@ -import create from '../create' +import create, { Created } from '../create' export default create({ name: 'anything', diff --git a/src/matchers/builtin/arg-that.js b/src/matchers/builtin/arg-that.ts similarity index 77% rename from src/matchers/builtin/arg-that.js rename to src/matchers/builtin/arg-that.ts index 2af0ef46..aba0d958 100644 --- a/src/matchers/builtin/arg-that.js +++ b/src/matchers/builtin/arg-that.ts @@ -1,4 +1,4 @@ -import create from '../create' +import create, { Created } from '../create' export default create({ name: 'argThat', diff --git a/src/matchers/builtin/captor.js b/src/matchers/builtin/captor.ts similarity index 63% rename from src/matchers/builtin/captor.js rename to src/matchers/builtin/captor.ts index 1ccdf732..c0122fab 100644 --- a/src/matchers/builtin/captor.js +++ b/src/matchers/builtin/captor.ts @@ -1,7 +1,13 @@ -import create from '../create' +import create, { Created } from '../create' + +export interface Captor { + capture: Created + values: any[] + value: any +} export default () => { - const captor = { + const captor: Captor = { capture: create({ name: 'captor.capture', matches (matcherArgs, actual) { @@ -12,7 +18,9 @@ export default () => { captor.values.push(actual) captor.value = actual } - }) + }), + values: [], + value: undefined } return captor } diff --git a/src/matchers/builtin/contains.js b/src/matchers/builtin/contains.ts similarity index 97% rename from src/matchers/builtin/contains.js rename to src/matchers/builtin/contains.ts index 1ae8828a..d0e623ff 100644 --- a/src/matchers/builtin/contains.js +++ b/src/matchers/builtin/contains.ts @@ -1,5 +1,5 @@ import _ from '../../wrap/lodash' -import create from '../create' +import create, { Created } from '../create' import isMatcher from '../is-matcher' export default create({ diff --git a/src/matchers/builtin/is-a.js b/src/matchers/builtin/is-a.ts similarity index 92% rename from src/matchers/builtin/is-a.js rename to src/matchers/builtin/is-a.ts index d3fea231..fd6f2dde 100644 --- a/src/matchers/builtin/is-a.js +++ b/src/matchers/builtin/is-a.ts @@ -1,5 +1,5 @@ import _ from '../../wrap/lodash' -import create from '../create' +import create, { Created } from '../create' import stringifyArguments from '../../stringify/arguments' export default create({ diff --git a/src/matchers/builtin/not.js b/src/matchers/builtin/not.ts similarity index 81% rename from src/matchers/builtin/not.js rename to src/matchers/builtin/not.ts index fbc799e8..761de747 100644 --- a/src/matchers/builtin/not.js +++ b/src/matchers/builtin/not.ts @@ -1,5 +1,5 @@ import _ from '../../wrap/lodash' -import create from '../create' +import create, { Created } from '../create' export default create({ name: 'not', diff --git a/src/matchers/create.js b/src/matchers/create.ts similarity index 68% rename from src/matchers/create.js rename to src/matchers/create.ts index 3e736338..a0e9c4dd 100644 --- a/src/matchers/create.js +++ b/src/matchers/create.ts @@ -1,21 +1,33 @@ import _ from '../wrap/lodash' import stringifyArguments from '../stringify/arguments' -export default config => +export interface CreatedResult { + __name?: string + __matches?: { + (value: any): boolean + afterSatisfaction?: Function + } +} + +export interface Created { + (...matcherArgs): CreatedResult +} + +export default (config): Created => (...matcherArgs) => - _.tap({ + _.tap(({ __name: nameFor(config, matcherArgs), __matches (actualArg) { return config.matches(matcherArgs, actualArg) } - }, (matcherInstance) => { + }) as CreatedResult, (matcherInstance) => { matcherInstance.__matches.afterSatisfaction = (actualArg) => { _.invoke(config, 'afterSatisfaction', matcherArgs, actualArg) } _.invoke(config, 'onCreate', matcherInstance, matcherArgs) }) -var nameFor = (config, matcherArgs) => { +let nameFor = (config, matcherArgs) => { if (_.isFunction(config.name)) { return config.name(matcherArgs) } else if (config.name != null) { diff --git a/src/matchers/index.js b/src/matchers/index.ts similarity index 61% rename from src/matchers/index.js rename to src/matchers/index.ts index 03d24c73..6f3618ac 100644 --- a/src/matchers/index.js +++ b/src/matchers/index.ts @@ -1,11 +1,14 @@ -import create from './create' -import captor from './builtin/captor' +import create, { Created } from './create' +import captor, { Captor } from './builtin/captor' import isA from './builtin/is-a' import contains from './builtin/contains' import anything from './builtin/anything' import argThat from './builtin/arg-that' import not from './builtin/not' +export { Created } from './create' +export { Captor } from './builtin/captor' + export default { create, captor, diff --git a/src/matchers/is-callback.js b/src/matchers/is-callback.ts similarity index 100% rename from src/matchers/is-callback.js rename to src/matchers/is-callback.ts diff --git a/src/matchers/is-matcher.js b/src/matchers/is-matcher.ts similarity index 100% rename from src/matchers/is-matcher.js rename to src/matchers/is-matcher.ts diff --git a/src/matchers/notify-after-satisfaction.js b/src/matchers/notify-after-satisfaction.ts similarity index 100% rename from src/matchers/notify-after-satisfaction.js rename to src/matchers/notify-after-satisfaction.ts diff --git a/src/object.js b/src/object.ts similarity index 69% rename from src/object.js rename to src/object.ts index fc183ee1..74f426fe 100644 --- a/src/object.js +++ b/src/object.ts @@ -5,19 +5,30 @@ import imitate from './imitate' const DEFAULT_OPTIONS = {excludeMethods: ['then']} -export default function object (nameOrType, config) { +export interface ObjectProxyConfig { + excludeMethods: string[] +} + +export interface ObjectType { + (originalOrNames: T | (keyof T)[]): T + (names: string, config?: ObjectProxyConfig): any +} + +type a = ProxyConstructor + +const object: ObjectType = function (nameOrType: T | (keyof T)[] | string, config?: ObjectProxyConfig): T { return _.tap(fakeObject(nameOrType, config, arguments.length), (obj) => { addToStringToDouble(obj, nameOrType) }) } -var fakeObject = function (nameOrType, config, argCount) { +let fakeObject = function (nameOrType: T | (keyof T)[] | string, config, argCount: number): T { if (_.isArray(nameOrType)) { return createTestDoublesForFunctionNames(nameOrType) - } else if (_.isObjectLike(nameOrType)) { - return imitate(nameOrType) } else if (_.isString(nameOrType) || argCount === 0) { return createTestDoubleViaProxy(nameOrType, withDefaults(config)) + } else if (_.isObjectLike(nameOrType)) { + return imitate(nameOrType) } else if (_.isFunction(nameOrType)) { ensureFunctionIsNotPassed() } else { @@ -25,15 +36,15 @@ var fakeObject = function (nameOrType, config, argCount) { } } -var createTestDoublesForFunctionNames = (names) => - _.transform(names, (acc, funcName) => { +let createTestDoublesForFunctionNames = (names: (keyof T)[]): T => + _.transform(names, (acc: any, funcName) => { acc[funcName] = tdFunction(`.${String(funcName)}`) - }) + }, {}) as { [P in keyof T] } -var createTestDoubleViaProxy = (name, config) => { +let createTestDoubleViaProxy = (name, config): T => { ensureProxySupport(name) - const obj = {} - return new Proxy(obj, { + const obj = {} as T & object + return new Proxy(obj, { get (target, propKey, receiver) { if (!obj.hasOwnProperty(propKey) && !_.includes(config.excludeMethods, propKey)) { obj[propKey] = tdFunction(`${nameOf(name)}.${String(propKey)}`) @@ -43,7 +54,7 @@ var createTestDoubleViaProxy = (name, config) => { }) } -var ensureProxySupport = (name) => { +let ensureProxySupport = (name) => { if (typeof Proxy === 'undefined') { log.error('td.object', `\ The current runtime does not have Proxy support, which is what @@ -57,10 +68,10 @@ Did you mean \`td.object(['${name}'])\`?\ } } -var ensureFunctionIsNotPassed = () => +let ensureFunctionIsNotPassed = () => log.error('td.object', `Functions are not valid arguments to \`td.object\` (as of testdouble@2.0.0). Please use \`td.function()\` or \`td.constructor()\` instead for creating fake functions.`) -var ensureOtherGarbageIsNotPassed = () => +let ensureOtherGarbageIsNotPassed = () => log.error('td.object', `\ To create a fake object with td.object(), pass it a plain object that contains functions, an array of function names, or (if your runtime supports ES Proxy @@ -70,15 +81,17 @@ If you passed td.object an instance of a custom type, consider passing the type's constructor to \`td.constructor()\` instead. `) -var withDefaults = (config) => +let withDefaults = (config) => _.extend({}, DEFAULT_OPTIONS, config) -var addToStringToDouble = (fakeObject, nameOrType) => { +let addToStringToDouble = (fakeObject, nameOrType) => { const name = nameOf(nameOrType) fakeObject.toString = () => `[test double object${name ? ` for "${name}"` : ''}]` } -var nameOf = (nameOrType) => +let nameOf = (nameOrType) => _.isString(nameOrType) ? nameOrType : '' + +export default object diff --git a/src/quibble.browser.js b/src/quibble.browser.ts similarity index 100% rename from src/quibble.browser.js rename to src/quibble.browser.ts diff --git a/src/replace/index.js b/src/replace/index.ts similarity index 60% rename from src/replace/index.js rename to src/replace/index.ts index 761d639c..636e33d5 100644 --- a/src/replace/index.js +++ b/src/replace/index.ts @@ -5,10 +5,10 @@ import replaceProperty from './property' quibble.ignoreCallsFromThisFile() -export default function (target) { +export default function (target, ...rest) { if (_.isString(target)) { - return replaceModule(...arguments) + return replaceModule(target, ...rest) } else { - return replaceProperty(...arguments) + return replaceProperty(target, rest[0], ...rest.slice(1)) } } diff --git a/src/replace/is-constructor.js b/src/replace/is-constructor.ts similarity index 100% rename from src/replace/is-constructor.js rename to src/replace/is-constructor.ts diff --git a/src/replace/jest-module.js b/src/replace/jest-module.ts similarity index 94% rename from src/replace/jest-module.js rename to src/replace/jest-module.ts index 09c95b5d..77ea8e58 100644 --- a/src/replace/jest-module.js +++ b/src/replace/jest-module.ts @@ -3,7 +3,7 @@ import log from '../log' quibble.ignoreCallsFromThisFile() -export default function jestModule (path, stub) { +export default function jestModule (path, stub, ...rest) { const tdMock = require('../index').mock if (!tdMock) { log.error('td.replace', 'It appears the test is being run by Jest, but the testdouble-jest module has not been initialized, so testdouble.js cannot replace modules. For setup instructions, visit: https://github.com/testdouble/testdouble-jest') diff --git a/src/replace/module.browser.js b/src/replace/module.browser.ts similarity index 100% rename from src/replace/module.browser.js rename to src/replace/module.browser.ts diff --git a/src/replace/module.js b/src/replace/module.ts similarity index 76% rename from src/replace/module.js rename to src/replace/module.ts index f5c33621..d900c478 100644 --- a/src/replace/module.js +++ b/src/replace/module.ts @@ -1,5 +1,5 @@ import _ from '../wrap/lodash' -import path from 'path' +import * as path from 'path' import * as quibble from 'quibble' import jestModule from './jest-module' @@ -7,8 +7,8 @@ import imitate from '../imitate' quibble.ignoreCallsFromThisFile() -export default function (path, stub) { - if (typeof jest === 'object') return jestModule(...arguments) +export default function (path: string, stub?, ...rest) { + if (typeof jest === 'object') return jestModule(path, stub, ...rest) if (arguments.length > 1) { return quibble(path, stub) } const realThing = requireAt(path) const fakeThing = imitate(realThing, `${path}: ${nameFor(realThing)}`) @@ -16,12 +16,12 @@ export default function (path, stub) { return fakeThing } -const nameFor = (realThing) => { +const nameFor = (realThing: Function) => { if (!_.isFunction(realThing)) return '' return realThing.name ? realThing.name : '(anonymous function)' } -var requireAt = (modulePath) => { +const requireAt = (modulePath) => { try { // 1. Try just following quibble's inferred path return require(quibble.absolutify(modulePath)) diff --git a/src/replace/property.js b/src/replace/property.ts similarity index 81% rename from src/replace/property.js rename to src/replace/property.ts index 923f9d68..77d2b272 100644 --- a/src/replace/property.js +++ b/src/replace/property.ts @@ -4,7 +4,7 @@ import log from '../log' import reset from '../reset' import stringifyAnything from '../stringify/anything' -export default function (object, property, manualReplacement) { +export default function (object: T, property: P, manualReplacement?: T[P]): T[P] { const isManual = arguments.length > 2 const realThingExists = object[property] || object.hasOwnProperty(property) @@ -25,7 +25,7 @@ export default function (object, property, manualReplacement) { } } -var getFake = (isManual, property, manualReplacement, realThing) => { +let getFake = (isManual: boolean, property: string, manualReplacement: T, realThing: T): T => { if (isManual) { warnIfTypeMismatch(property, manualReplacement, realThing) return manualReplacement @@ -34,7 +34,7 @@ var getFake = (isManual, property, manualReplacement, realThing) => { } } -var warnIfTypeMismatch = (property, fakeThing, realThing) => { +let warnIfTypeMismatch = (property: string, fakeThing: T, realThing: T) => { const fakeType = typeof fakeThing const realType = typeof realThing if (realThing !== undefined && fakeType !== realType) { diff --git a/src/reset.js b/src/reset.ts similarity index 68% rename from src/reset.js rename to src/reset.ts index e42e984c..3701d4c2 100644 --- a/src/reset.js +++ b/src/reset.ts @@ -4,13 +4,18 @@ import store from './store' let resetHandlers = [] -export default _.tap(() => { +export interface Reset { + (): void + onNextReset: (cb: () => void) => void +} + +export default _.tap((() => { store.reset() quibble.reset() _.each(resetHandlers, (resetHandler) => resetHandler()) resetHandlers = [] -}, (reset) => { +}) as Reset, (reset) => { reset.onNextReset = (func) => resetHandlers.push(func) }) diff --git a/src/satisfy/deliver-outcome.js b/src/satisfy/deliver-outcome.ts similarity index 100% rename from src/satisfy/deliver-outcome.js rename to src/satisfy/deliver-outcome.ts diff --git a/src/satisfy/find-last-stubbing-match.js b/src/satisfy/find-last-stubbing-match.ts similarity index 100% rename from src/satisfy/find-last-stubbing-match.js rename to src/satisfy/find-last-stubbing-match.ts diff --git a/src/satisfy/index.js b/src/satisfy/index.ts similarity index 100% rename from src/satisfy/index.js rename to src/satisfy/index.ts diff --git a/src/satisfy/invoke-callbacks.js b/src/satisfy/invoke-callbacks.ts similarity index 100% rename from src/satisfy/invoke-callbacks.js rename to src/satisfy/invoke-callbacks.ts diff --git a/src/share/call-later.js b/src/share/call-later.ts similarity index 100% rename from src/share/call-later.js rename to src/share/call-later.ts diff --git a/src/share/create-promise.js b/src/share/create-promise.ts similarity index 100% rename from src/share/create-promise.js rename to src/share/create-promise.ts diff --git a/src/store/calls.js b/src/store/calls.ts similarity index 100% rename from src/store/calls.js rename to src/store/calls.ts diff --git a/src/store/index.js b/src/store/index.ts similarity index 100% rename from src/store/index.js rename to src/store/index.ts diff --git a/src/store/stubbings.js b/src/store/stubbings.ts similarity index 86% rename from src/store/stubbings.js rename to src/store/stubbings.ts index 0727c607..cbf6b2c2 100644 --- a/src/store/stubbings.js +++ b/src/store/stubbings.ts @@ -29,11 +29,11 @@ export default { } } -var stubbingFor = (testDouble, actualArgs) => +let stubbingFor = (testDouble, actualArgs) => _.findLast(store.for(testDouble).stubbings, stubbing => isSatisfied(stubbing, actualArgs)) -var executePlan = (stubbing, actualArgs, actualContext) => { +let executePlan = (stubbing, actualArgs, actualContext) => { const value = stubbedValueFor(stubbing) stubbing.callCount += 1 invokeCallbackFor(stubbing, actualArgs) @@ -46,7 +46,7 @@ var executePlan = (stubbing, actualArgs, actualContext) => { } } -var invokeCallbackFor = (stubbing, actualArgs) => { +let invokeCallbackFor = (stubbing, actualArgs) => { if (_.some(stubbing.args, isCallback)) { _.each(stubbing.args, (expectedArg, i) => { if (isCallback(expectedArg)) { @@ -56,7 +56,7 @@ var invokeCallbackFor = (stubbing, actualArgs) => { } } -var callbackArgs = (stubbing, expectedArg) => { +let callbackArgs = (stubbing, expectedArg) => { if (expectedArg.args != null) { return expectedArg.args } else if (stubbing.config.plan === 'thenCallback') { @@ -66,7 +66,7 @@ var callbackArgs = (stubbing, expectedArg) => { } } -var callCallback = (stubbing, callback, args) => { +let callCallback = (stubbing, callback, args) => { if (stubbing.config.delay) { _.delay(callback, stubbing.config.delay, ...args) } else if (stubbing.config.defer) { @@ -76,7 +76,7 @@ var callCallback = (stubbing, callback, args) => { } } -var createPromise = (stubbing, value, willResolve) => { +let createPromise = (stubbing, value, willResolve) => { const Promise = config().promiseConstructor ensurePromise(Promise) return new Promise((resolve, reject) => { @@ -86,21 +86,21 @@ var createPromise = (stubbing, value, willResolve) => { }) } -var stubbedValueFor = (stubbing) => +let stubbedValueFor = (stubbing) => stubbing.callCount < stubbing.stubbedValues.length ? stubbing.stubbedValues[stubbing.callCount] : _.last(stubbing.stubbedValues) -var isSatisfied = (stubbing, actualArgs) => +let isSatisfied = (stubbing, actualArgs) => argsMatch(stubbing.args, actualArgs, stubbing.config) && hasTimesRemaining(stubbing) -var hasTimesRemaining = (stubbing) => +let hasTimesRemaining = (stubbing) => stubbing.config.times == null ? true : stubbing.callCount < stubbing.config.times -var ensurePromise = (Promise) => { +let ensurePromise = (Promise) => { if (Promise == null) { return log.error('td.when', `\ no promise constructor is set (perhaps this runtime lacks a native Promise diff --git a/src/stringify/anything.js b/src/stringify/anything.ts similarity index 80% rename from src/stringify/anything.js rename to src/stringify/anything.ts index 57e492d4..1f2a379d 100644 --- a/src/stringify/anything.js +++ b/src/stringify/anything.ts @@ -23,7 +23,7 @@ export default (anything) => { } } -var stringifyString = (string) => - _.includes(string, '\n') - ? `"""\n${string}\n"""` - : `"${string.replace(new RegExp('"', 'g'), '\\"')}"` +const stringifyString = (str: string) => + _.includes(str, '\n') + ? `"""\n${str}\n"""` + : `"${str.replace(new RegExp('"', 'g'), '\\"')}"` diff --git a/src/stringify/arguments.js b/src/stringify/arguments.ts similarity index 100% rename from src/stringify/arguments.js rename to src/stringify/arguments.ts diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000..03b52adb --- /dev/null +++ b/src/types.ts @@ -0,0 +1,20 @@ + +export interface Constructor0 { + new (): T & object +} + +export interface Constructor1 { + new (a1: A1): T & object +} + +export interface Constructor2 { + new (a1: A1, a2: A2): T & object +} + +export interface Constructor3 { + new (a1: A1, a2: A2, a3: A3): T & object +} + +export interface Constructor4 { + new (a1: A1, a2: A2, a3: A3, a4: A4): T & object +} diff --git a/src/value/call-log.js b/src/value/call-log.ts similarity index 95% rename from src/value/call-log.js rename to src/value/call-log.ts index 71a413e4..45701ca8 100644 --- a/src/value/call-log.js +++ b/src/value/call-log.ts @@ -3,6 +3,14 @@ import * as Map from 'es6-map' let instance = null export default class CallLog { + calls: Map + callHistory: any[] + + constructor () { + this.calls = new Map() + this.callHistory = [] + } + static get instance () { if (instance) return instance instance = new CallLog() @@ -13,11 +21,6 @@ export default class CallLog { instance = null } - constructor () { - this.calls = new Map() - this.callHistory = [] - } - log (double, call) { this.callHistory.push({double, call}) if (this.calls.has(double)) { diff --git a/src/value/call.js b/src/value/call.ts similarity index 80% rename from src/value/call.js rename to src/value/call.ts index 4f541930..ee5b8076 100644 --- a/src/value/call.js +++ b/src/value/call.ts @@ -1,4 +1,7 @@ export default class Call { + context: any + args: any + constructor (context, args) { this.context = context this.args = args diff --git a/src/value/double.js b/src/value/double.ts similarity index 92% rename from src/value/double.js rename to src/value/double.ts index 54f0fb4d..b9e650e1 100644 --- a/src/value/double.js +++ b/src/value/double.ts @@ -1,11 +1,11 @@ import _ from '../wrap/lodash' export default class Double { - static create (name, real, parent, fakeCreator) { - const double = new Double(name, real, parent) - if (fakeCreator) double.fake = fakeCreator(double) - return double - } + fake?: any + name?: any + real?: any + children?: Set + parent?: any constructor (name, real, parent) { this.name = name @@ -17,6 +17,12 @@ export default class Double { } } + static create (name, real, parent, fakeCreator) { + const double = new Double(name, real, parent) + if (fakeCreator) double.fake = fakeCreator(double) + return double + } + addChild (child) { this.children.add(child) child.parent = this diff --git a/src/value/stubbing-register.js b/src/value/stubbing-register.ts similarity index 96% rename from src/value/stubbing-register.js rename to src/value/stubbing-register.ts index 279e6827..9c8bf665 100644 --- a/src/value/stubbing-register.js +++ b/src/value/stubbing-register.ts @@ -3,6 +3,12 @@ import * as Map from 'es6-map' let instance = null export default class StubbingRegister { + stubbings: Map + + constructor () { + this.stubbings = new Map() + } + static get instance () { if (instance) return instance instance = new StubbingRegister() @@ -13,10 +19,6 @@ export default class StubbingRegister { instance = null } - constructor () { - this.stubbings = new Map() - } - add (double, stubbing) { if (this.stubbings.has(double)) { this.stubbings.get(double).push(stubbing) diff --git a/src/value/stubbing.js b/src/value/stubbing.ts similarity index 89% rename from src/value/stubbing.js rename to src/value/stubbing.ts index a0394dc9..8e35fd41 100644 --- a/src/value/stubbing.js +++ b/src/value/stubbing.ts @@ -1,6 +1,12 @@ import _ from '../wrap/lodash' export default class Stubbing { + type: any + args: any + outcomes: any + options: any + satisfyingCalls: Set + constructor (type, args, outcomes, options = {}) { this.type = type this.args = args diff --git a/src/verify.js b/src/verify.ts similarity index 86% rename from src/verify.js rename to src/verify.ts index d31e1e17..5301a50e 100644 --- a/src/verify.js +++ b/src/verify.ts @@ -7,6 +7,7 @@ import stringifyArgs from './stringify/arguments' import stubbingsStore from './store/stubbings' import notifyAfterSatisfaction from './matchers/notify-after-satisfaction' +// tslint:disable-next-line:variable-name export default (__userDoesRehearsalInvocationHere__, config = {}) => { const last = callsStore.pop() ensureRehearsalOccurred(last) @@ -18,7 +19,7 @@ export default (__userDoesRehearsalInvocationHere__, config = {}) => { } } -var ensureRehearsalOccurred = (last) => { +let ensureRehearsalOccurred = (last) => { if (!last) { log.error('td.verify', `\ No test double invocation detected for \`verify()\`. @@ -35,7 +36,7 @@ const notifyMatchers = (testDouble, expectedArgs, config) => { }) } -var warnIfStubbed = (testDouble, actualArgs) => { +let warnIfStubbed = (testDouble, actualArgs) => { if (_.some(stubbingsStore.for(testDouble), (stubbing) => argsMatch(stubbing.args, actualArgs, stubbing.config)) ) { @@ -46,17 +47,17 @@ var warnIfStubbed = (testDouble, actualArgs) => { } } -var unsatisfiedErrorMessage = (testDouble, args, config) => +let unsatisfiedErrorMessage = (testDouble, args, config) => baseSummary(testDouble, args, config) + matchedInvocationSummary(testDouble, args, config) + invocationSummary(testDouble, args, config) -var stringifyName = (testDouble) => { +let stringifyName = (testDouble) => { const name = store.for(testDouble).name return name ? ` \`${name}\`` : '' } -var baseSummary = (testDouble, args, config) => +let baseSummary = (testDouble, args, config) => `\ Unsatisfied verification on test double${stringifyName(testDouble)}. @@ -64,7 +65,7 @@ Unsatisfied verification on test double${stringifyName(testDouble)}. - called with \`(${stringifyArgs(args)})\`${timesMessage(config)}${ignoreMessage(config)}.\ ` -var invocationSummary = (testDouble, args, config) => { +let invocationSummary = (testDouble, args, config) => { const calls = callsStore.for(testDouble) if (calls.length === 0) { return '\n\n But there were no invocations of the test double.' @@ -75,7 +76,7 @@ var invocationSummary = (testDouble, args, config) => { } } -var matchedInvocationSummary = (testDouble, args, config) => { +let matchedInvocationSummary = (testDouble, args, config) => { const calls = callsStore.where(testDouble, args, config) const expectedCalls = config.times || 0 @@ -88,15 +89,15 @@ var matchedInvocationSummary = (testDouble, args, config) => { } } -var pluralize = (x, msg) => +let pluralize = (x, msg) => `${x} ${msg}${x === 1 ? '' : 's'}` -var timesMessage = (config) => +let timesMessage = (config) => config.times != null ? ` ${pluralize(config.times, 'time')}` : '' -var ignoreMessage = (config) => +let ignoreMessage = (config) => config.ignoreExtraArgs != null ? ', ignoring any additional arguments' : '' diff --git a/src/verify/did-call-occur.js b/src/verify/did-call-occur.ts similarity index 100% rename from src/verify/did-call-occur.js rename to src/verify/did-call-occur.ts diff --git a/src/verify/fail.js b/src/verify/fail.ts similarity index 100% rename from src/verify/fail.js rename to src/verify/fail.ts diff --git a/src/verify/index.js b/src/verify/index.ts similarity index 85% rename from src/verify/index.js rename to src/verify/index.ts index 8828167d..b9a8608b 100644 --- a/src/verify/index.js +++ b/src/verify/index.ts @@ -4,8 +4,9 @@ import notifySatisfiedMatchers from './notify-satisfied-matchers' import warnIfAlsoStubbed from './warn-if-also-stubbed' import fail from './fail' +// tslint:disable-next-line:variable-name export default function verify (__userInvokesDemonstrationHere__, config) { - const {double, call} = popDemonstration() + const { double, call } = popDemonstration() if (didCallOccur(double, call, config)) { notifySatisfiedMatchers(double, call, config) warnIfAlsoStubbed(double, call, config) diff --git a/src/verify/notify-satisfied-matchers.js b/src/verify/notify-satisfied-matchers.ts similarity index 100% rename from src/verify/notify-satisfied-matchers.js rename to src/verify/notify-satisfied-matchers.ts diff --git a/src/verify/pop-demonstration.js b/src/verify/pop-demonstration.js deleted file mode 100644 index cba26dba..00000000 --- a/src/verify/pop-demonstration.js +++ /dev/null @@ -1,2 +0,0 @@ -export default function popDemonstration () { -} diff --git a/src/verify/pop-demonstration.ts b/src/verify/pop-demonstration.ts new file mode 100644 index 00000000..f81de7e5 --- /dev/null +++ b/src/verify/pop-demonstration.ts @@ -0,0 +1,2 @@ +export default function popDemonstration (): any { +} diff --git a/src/verify/warn-if-also-stubbed.js b/src/verify/warn-if-also-stubbed.ts similarity index 100% rename from src/verify/warn-if-also-stubbed.js rename to src/verify/warn-if-also-stubbed.ts diff --git a/src/version.js b/src/version.ts similarity index 100% rename from src/version.js rename to src/version.ts diff --git a/src/when.js b/src/when.ts similarity index 98% rename from src/when.js rename to src/when.ts index b149dd97..f375abc0 100644 --- a/src/when.js +++ b/src/when.ts @@ -6,6 +6,7 @@ import log from './log' import stubbings from './store/stubbings' import tdConfig from './config' +// tslint:disable-next-line:variable-name export default function when (__userDoesRehearsalInvocationHere__, config = {}) { return ({ thenReturn (...stubbedValues) { diff --git a/src/when/add-implied-callback-arg-if-necessary.js b/src/when/add-implied-callback-arg-if-necessary.ts similarity index 100% rename from src/when/add-implied-callback-arg-if-necessary.js rename to src/when/add-implied-callback-arg-if-necessary.ts diff --git a/src/when/chain-stubbing.js b/src/when/chain-stubbing.ts similarity index 100% rename from src/when/chain-stubbing.js rename to src/when/chain-stubbing.ts diff --git a/src/when/ensure-rehearsal.js b/src/when/ensure-rehearsal.ts similarity index 100% rename from src/when/ensure-rehearsal.js rename to src/when/ensure-rehearsal.ts diff --git a/src/when/index.js b/src/when/index.ts similarity index 94% rename from src/when/index.js rename to src/when/index.ts index f9c44dd2..1c0d8078 100644 --- a/src/when/index.js +++ b/src/when/index.ts @@ -5,6 +5,7 @@ import CallLog from '../value/call-log' import StubbingRegister from '../value/stubbing-register' import Stubbing from '../value/stubbing' +// tslint:disable-next-line:variable-name export default (__rehearseInvocationHere__, options) => { const rehearsal = CallLog.instance.pop() ensureRehearsal(rehearsal) diff --git a/src/wrap/lodash.js b/src/wrap/lodash.js deleted file mode 100644 index 4c589bd2..00000000 --- a/src/wrap/lodash.js +++ /dev/null @@ -1,77 +0,0 @@ -import * as assign from 'lodash/assign' -import * as capitalize from 'lodash/capitalize' -import * as clone from 'lodash/clone' -import * as compact from 'lodash/compact' -import * as defer from 'lodash/defer' -import * as delay from 'lodash/delay' -import * as each from 'lodash/each' -import * as every from 'lodash/every' -import * as extend from 'lodash/extend' -import * as filter from 'lodash/filter' -import * as find from 'lodash/find' -import * as findLast from 'lodash/findLast' -import * as get from 'lodash/get' -import * as groupBy from 'lodash/groupBy' -import * as includes from 'lodash/includes' -import * as invoke from 'lodash/invoke' -import * as isArguments from 'lodash/isArguments' -import * as isArray from 'lodash/isArray' -import * as isBoolean from 'lodash/isBoolean' -import * as isEmpty from 'lodash/isEmpty' -import * as isEqual from 'lodash/isEqual' -import * as isEqualWith from 'lodash/isEqualWith' -import * as isFunction from 'lodash/isFunction' -import * as isNumber from 'lodash/isNumber' -import * as isObject from 'lodash/isObject' -import * as isObjectLike from 'lodash/isObjectLike' -import * as isRegExp from 'lodash/isRegExp' -import * as isString from 'lodash/isString' -import * as keys from 'lodash/keys' -import * as last from 'lodash/last' -import * as map from 'lodash/map' -import * as reduce from 'lodash/reduce' -import * as reject from 'lodash/reject' -import * as some from 'lodash/some' -import * as tap from 'lodash/tap' -import * as toArray from 'lodash/toArray' -import * as transform from 'lodash/transform' - -export default { - assign, - capitalize, - clone, - compact, - defer, - delay, - each, - every, - extend, - filter, - find, - findLast, - get, - groupBy, - includes, - invoke, - isArguments, - isArray, - isBoolean, - isEmpty, - isEqual, - isEqualWith, - isFunction, - isNumber, - isObject, - isObjectLike, - isRegExp, - isString, - keys, - last, - map, - reduce, - reject, - some, - tap, - toArray, - transform -} diff --git a/src/wrap/lodash.ts b/src/wrap/lodash.ts new file mode 100644 index 00000000..5697db0b --- /dev/null +++ b/src/wrap/lodash.ts @@ -0,0 +1,80 @@ + +import { Dictionary, NumericDictionary, PartialDeep } from 'lodash' + +import assign = require('lodash/assign') +import capitalize = require('lodash/capitalize') +import clone = require('lodash/clone') +import compact = require('lodash/compact') +import defer = require('lodash/defer') +import delay = require('lodash/delay') +import each = require('lodash/each') +import every = require('lodash/every') +import extend = require('lodash/extend') +import filter = require('lodash/filter') +import find = require('lodash/find') +import findLast = require('lodash/findLast') +import get = require('lodash/get') +import groupBy = require('lodash/groupBy') +import includes = require('lodash/includes') +import invoke = require('lodash/invoke') +import isArguments = require('lodash/isArguments') +import isArray = require('lodash/isArray') +import isBoolean = require('lodash/isBoolean') +import isEmpty = require('lodash/isEmpty') +import isEqual = require('lodash/isEqual') +import isEqualWith = require('lodash/isEqualWith') +import isFunction = require('lodash/isFunction') +import isNumber = require('lodash/isNumber') +import isObject = require('lodash/isObject') +import isObjectLike = require('lodash/isObjectLike') +import isRegExp = require('lodash/isRegExp') +import isString = require('lodash/isString') +import keys = require('lodash/keys') +import last = require('lodash/last') +import map = require('lodash/map') +import reduce = require('lodash/reduce') +import reject = require('lodash/reject') +import some = require('lodash/some') +import tap = require('lodash/tap') +import toArray = require('lodash/toArray') +import transform = require('lodash/transform') + +export default { + assign, + capitalize, + clone, + compact, + defer, + delay, + each, + every, + extend, + filter, + find, + findLast, + get, + groupBy, + includes, + invoke, + isArguments, + isArray, + isBoolean, + isEmpty, + isEqual, + isEqualWith, + isFunction, + isNumber, + isObject, + isObjectLike, + isRegExp, + isString, + keys, + last, + map, + reduce, + reject, + some, + tap, + toArray, + transform +} diff --git a/test/helper.js b/test/helper.js index 89c9b5f5..70e21236 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,4 +1,6 @@ -require('ts-node/register') +require('ts-node').register({ + compilerOptions: { allowJs: true } +}) global.assert = require('assert') global.ES_SUPPORT = require('./support/es-support') diff --git a/tsconfig.json b/tsconfig.json index 24ef1745..f0364e36 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "allowJs": true, "target": "es5", "lib": ["es2015"], "noImplicitAny": false, @@ -8,7 +7,6 @@ "moduleResolution": "node", "outDir": "lib" }, - "include": [ - "src/**/*" - ] + "include": ["src/**/*"], + "exclude": ["ts/**/*"] } diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 00000000..d9048e00 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,8 @@ +{ + "$schema": "http://json.schemastore.org/tslint", + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "ts" + } +} \ No newline at end of file diff --git a/tslint.json b/tslint.json new file mode 100644 index 00000000..c7806473 --- /dev/null +++ b/tslint.json @@ -0,0 +1,10 @@ +{ + "extends": "tslint-config-standard", + "rules": { + "no-empty": false, + "quotemark": { + "options":"single" + }, + "object-curly-spacing": false + } +} \ No newline at end of file