diff --git a/.eslintrc b/.eslintrc index c799fe5..9bcdb46 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,6 @@ { - "extends": "eslint-config-egg" + "extends": [ + "eslint-config-egg/typescript", + "eslint-config-egg/lib/rules/enforce-node-prefix" + ] } diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 4b0c095..9ed9cf2 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -3,7 +3,6 @@ name: CI on: push: branches: [ master ] - pull_request: branches: [ master ] @@ -12,5 +11,6 @@ jobs: name: Node.js uses: node-modules/github-actions/.github/workflows/node-test.yml@master with: - os: 'ubuntu-latest, macos-latest, windows-latest' - version: '14, 16, 18, 20' + version: '18.19.0, 18, 20, 22, 23' + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml new file mode 100644 index 0000000..bac3fac --- /dev/null +++ b/.github/workflows/pkg.pr.new.yml @@ -0,0 +1,23 @@ +name: Publish Any Commit +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - run: corepack enable + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: npm install + + - name: Build + run: npm run prepublishOnly --if-present + + - run: npx pkg-pr-new publish diff --git a/.gitignore b/.gitignore index 86e3a6f..2f5d06e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ results node_modules npm-debug.log coverage +package-lock.json +.tshy* +.eslintcache +dist diff --git a/README.md b/README.md index dd09cc2..37c4973 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Node.js CI](https://github.com/node-modules/mm/actions/workflows/nodejs.yml/badge.svg)](https://github.com/node-modules/mm/actions/workflows/nodejs.yml) [![Test coverage][codecov-image]][codecov-url] [![npm download][download-image]][download-url] +[![Node.js Version](https://img.shields.io/node/v/mm.svg?style=flat)](https://nodejs.org/en/download/) [npm-image]: https://img.shields.io/npm/v/mm.svg?style=flat-square [npm-url]: https://npmjs.org/package/mm @@ -22,9 +23,9 @@ npm install mm --save-dev ## Usage -```js -var mm = require('mm'); -var fs = require('fs'); +```ts +import fs from 'node:fs'; +import mm from 'mm'; mm(fs, 'readFileSync', function(filename) { return filename + ' content'; @@ -33,8 +34,7 @@ mm(fs, 'readFileSync', function(filename) { console.log(fs.readFileSync('《九评 Java》')); // => 《九评 Java》 content -mm.restore(); - +restore(); console.log(fs.readFileSync('《九评 Java》')); // => throw `Error: ENOENT, no such file or directory '《九评 Java》` ``` @@ -43,7 +43,9 @@ console.log(fs.readFileSync('《九评 Java》')); If mocked property is a function, it will be spied, every time it called, mm will modify `.called`, `.calledArguments` and `.lastCalledArguments`. For example: -```js +```ts +import mm from 'mm'; + const target = { async add(a, b) { return a + b; @@ -65,7 +67,9 @@ assert.deepEqual(target.add.lastCalledArguments, [ 2, 2 ]); If you only need spy and don't need mock, you can use `mm.spy` method directly: -```js +```ts +import mm from 'mm'; + const target = { async add(a, b) { await this.foo(); @@ -90,9 +94,9 @@ assert.deepEqual(target.add.lastCalledArguments, [ 2, 2 ]); ### .error(module, propertyName, errerMessage, errorProperties) -```js -var mm = require('mm'); -var fs = require('fs'); +```ts +import fs from 'node:fs'; +import mm from 'mm'; mm.error(fs, 'readFile', 'mock fs.readFile return error'); @@ -114,9 +118,9 @@ fs.readFile('/etc/hosts', 'utf8', function (err, content) { Just like `mm.error()`, but only mock error once. -```js -const mm = require('mm'); -const fs = require('fs'); +```ts +import fs from 'node:fs'; +import mm from 'mm'; mm.errorOnce(fs, 'readFile', 'mock fs.readFile return error'); @@ -135,12 +139,12 @@ fs.readFile('/etc/hosts', 'utf8', function (err, content) { ### .data(module, propertyName, secondCallbackArg) ```js -mm.data(fs, 'readFile', new Buffer('some content')); +mm.data(fs, 'readFile', Buffer.from('some content')); // equals fs.readFile = function (...args, callback) { - callback(null, new Buffer('some content')) + callback(null, Buffer.from('some content')) }; ``` @@ -191,12 +195,12 @@ mysql.query = function (...args, callback) { ### .datas(module, propertyName, argsArray) ```js -mm.datas(urllib, 'request', [new Buffer('data'), {headers: { foo: 'bar' }}]); +mm.datas(urllib, 'request', [Buffer.from('data'), {headers: { foo: 'bar' }}]); // equals urllib.request = function (...args, callback) { - callback(null, new Buffer('data'), {headers: { foo: 'bar' }}); + callback(null, Buffer.from('data'), {headers: { foo: 'bar' }}); } ``` @@ -221,12 +225,12 @@ fs.readFileSync = function (...args) { ### .syncData(module, propertyName, value) ```js -mm.syncData(fs, 'readFileSync', new Buffer('some content')); +mm.syncData(fs, 'readFileSync', Buffer.from('some content')); // equals fs.readFileSync = function (...args) { - return new Buffer('some content'); + return Buffer.from('some content'); }; ``` @@ -337,15 +341,8 @@ assert(await foo1.fetch() === 3); [MIT](LICENSE) - - ## Contributors -|[
fengmk2](https://github.com/fengmk2)
|[
dead-horse](https://github.com/dead-horse)
|[
alsotang](https://github.com/alsotang)
|[
popomore](https://github.com/popomore)
|[
semantic-release-bot](https://github.com/semantic-release-bot)
|[
gemwuu](https://github.com/gemwuu)
| -| :---: | :---: | :---: | :---: | :---: | :---: | -|[
paranoidjk](https://github.com/paranoidjk)
|[
nightink](https://github.com/nightink)
|[
killagu](https://github.com/killagu)
|[
gxkl](https://github.com/gxkl)
|[
iyuq](https://github.com/iyuq)
|[
atian25](https://github.com/atian25)
| -[
xavierchow](https://github.com/xavierchow)
|[
whxaxes](https://github.com/whxaxes)
- -This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Sat Dec 09 2023 11:34:46 GMT+0800`. +[![Contributors](https://contrib.rocks/image?repo=node-modules/mm)](https://github.com/node-modules/mm/graphs/contributors) - +Made with [contributors-img](https://contrib.rocks). diff --git a/index.js b/index.js deleted file mode 100644 index 33756d5..0000000 --- a/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = require('./lib/mm'); - -require('./lib/es6'); diff --git a/lib/es6.js b/lib/es6.js deleted file mode 100644 index dfa7dbb..0000000 --- a/lib/es6.js +++ /dev/null @@ -1,98 +0,0 @@ -const is = require('is-type-of'); -const mm = require('./mm'); - -function sleep(ms) { - return new Promise(resolve => { - setTimeout(resolve, ms); - }); -} - -const mockDatas = mm.datas; -// support generator -mm.datas = function(mod, method, datas, timeout) { - const isGeneratorFunction = is.generatorFunction(mod[method]); - const isAsyncFunction = is.asyncFunction(mod[method]); - if (!isGeneratorFunction && !isAsyncFunction) { - return mockDatas.call(mm, mod, method, datas, timeout); - } - - if (timeout) { - timeout = parseInt(timeout, 10); - } - timeout = timeout || 0; - if (isGeneratorFunction) { - mm(mod, method, function* () { - yield sleep(timeout); - return datas; - }); - } else { - mm(mod, method, async function() { - await sleep(timeout); - return datas; - }); - } - return this; -}; - -const mockData = mm.data; -mm.data = function(mod, method, data, timeout) { - if (!is.generatorFunction(mod[method]) && !is.asyncFunction(mod[method])) { - return mockData.call(mm, mod, method, data, timeout); - } - - return mm.datas(mod, method, data, timeout); -}; - -mm.dataWithAsyncDispose = function(mod, method, data, timeout) { - data = { - ...data, - async [Symbol.asyncDispose]() { - // do nothing - }, - }; - return mm.data(mod, method, data, timeout); -}; - -const mockError = mm.error; -mm.error = function(mod, method, error, props, timeout) { - if (!is.generatorFunction(mod[method])) { - return mockError.call(mm, mod, method, error, props, timeout); - } - - error = mm._createError(error, props); - - if (timeout) { - timeout = parseInt(timeout, 10); - } - timeout = timeout || 0; - mm(mod, method, function* () { - yield sleep(timeout); - throw error; - }); - return this; -}; - -const mockErrorOnce = mm.errorOnce; -mm.errorOnce = function(mod, method, error, props, timeout) { - if (!is.generatorFunction(mod[method])) { - return mockErrorOnce.call(mm, mod, method, error, props, timeout); - } - - error = mm._createError(error, props); - - if (timeout) { - timeout = parseInt(timeout, 10); - } - timeout = timeout || 0; - mm(mod, method, function* () { - yield sleep(timeout); - mm.restore(); - throw error; - }); - return this; -}; - -// mock class method from instance -mm.classMethod = function(instance, property, value) { - return mm(instance.constructor.prototype, property, value); -}; diff --git a/package.json b/package.json index 66cd1c3..04fa0ff 100644 --- a/package.json +++ b/package.json @@ -2,41 +2,6 @@ "name": "mm", "version": "3.4.0", "description": "mock mate, mock http request, fs access and so on.", - "main": "index.js", - "files": [ - "index.js", - "index.d.ts", - "lib" - ], - "scripts": { - "test": "npm run lint && egg-bin test --ts false", - "test-ts": "egg-bin test --ts true", - "ci": "npm run lint && egg-bin cov --ts false && npm run test-ts", - "lint": "eslint --fix lib test index.js", - "contributor": "git-contributor" - }, - "dependencies": { - "is-type-of": "^1.2.1", - "muk-prop": "^1.2.1", - "thenify": "^3.3.0" - }, - "devDependencies": { - "@eggjs/tsconfig": "^1.3.3", - "@hazae41/symbol-dispose-polyfill": "^1.0.2", - "@types/mocha": "^10.0.6", - "@types/node": "^20.10.4", - "chunkstream": "^0.0.1", - "co": "^4.6.0", - "egg-bin": "^6.4.0", - "eslint": "^8.55.0", - "eslint-config-egg": "^12.0.0", - "git-contributor": "^2.1.5", - "pedding": "^1.1.0", - "should": "^13.2.3", - "thunkify-wrap": "^1.0.4", - "typescript": "^5.3.3", - "urllib": "^3.5.1" - }, "homepage": "http://github.com/node-modules/mm", "repository": { "type": "git", @@ -48,9 +13,63 @@ "mock", "test" ], + "author": "fengmk2 (https://github.com/fengmk2)", + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">= 18.19.0" }, - "author": "fengmk2 (https://github.com/fengmk2)", - "license": "MIT" + "dependencies": { + "@cnpmjs/muk-prop": "^1.0.0", + "is-type-of": "^2.2.0", + "thenify": "^3.3.1" + }, + "devDependencies": { + "@arethetypeswrong/cli": "^0.17.1", + "@eggjs/tsconfig": "1", + "@types/mocha": "10", + "@types/node": "22", + "egg-bin": "6", + "eslint": "8", + "eslint-config-egg": "14", + "pedding": "^1.1.0", + "should": "^13.2.3", + "tshy": "3", + "tshy-after": "1", + "typescript": "5" + }, + "scripts": { + "lint": "eslint --cache src test --ext .ts", + "pretest": "npm run lint -- --fix && npm run prepublishOnly", + "test": "egg-bin test", + "preci": "npm run lint && npm run prepublishOnly && attw --pack", + "ci": "egg-bin cov", + "prepublishOnly": "tshy && tshy-after" + }, + "type": "module", + "tshy": { + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + } + }, + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + }, + "./package.json": "./package.json" + }, + "files": [ + "dist", + "src" + ], + "types": "./dist/commonjs/index.d.ts", + "main": "./dist/commonjs/index.js", + "module": "./dist/esm/index.js" } diff --git a/index.d.ts b/src/index.d.ts similarity index 100% rename from index.d.ts rename to src/index.d.ts diff --git a/lib/mm.js b/src/index.ts similarity index 57% rename from lib/mm.js rename to src/index.ts index f14ec3d..53884d5 100644 --- a/lib/mm.js +++ b/src/index.ts @@ -1,19 +1,21 @@ -const EventEmitter = require('events'); -const is = require('is-type-of'); -const muk = require('muk-prop'); -const http = require('http'); -const https = require('https'); -const cp = require('child_process'); -const thenify = require('thenify').withCallback; -const Readable = require('stream').Readable; -const Duplex = require('stream').Duplex; - -const mock = module.exports = function mock(target, property, value) { +import { EventEmitter } from 'node:events'; +import http from 'node:http'; +import https from 'node:https'; +import cp from 'node:child_process'; +import { scheduler } from 'node:timers/promises'; +import { Readable, Duplex } from 'node:stream'; +import { muk, isMocked, restore } from '@cnpmjs/muk-prop'; +import is from 'is-type-of'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import thenify from 'thenify'; + +function mock(target: any, property: string, value?: any) { value = spyFunction(target, property, value); return muk(target, property, value); -}; +} -function spyFunction(target, property, fn) { +function spyFunction(target: any, property: string, fn: any) { if (!is.function(fn)) return fn; // support mock with jest.fn() if (fn._isMockFunction && fn.mock) return fn; @@ -37,16 +39,14 @@ function spyFunction(target, property, fn) { }); } -function isAsyncLikeFunction(target, property) { +function isAsyncLikeFunction(target: any, property: string) { // don't call getter // Object.getOwnPropertyDescriptor can't find getter in prototypes - if (target.__lookupGetter__(property)) return false; + if (typeof target.__lookupGetter__ === 'function' && target.__lookupGetter__(property)) return false; return is.asyncFunction(target[property]) || is.generatorFunction(target[property]); } -exports = mock; - -function getCallback(args) { +function getCallback(args: any[]) { let index = args.length - 1; let callback = args[index]; while (typeof callback !== 'function') { @@ -68,7 +68,7 @@ function getCallback(args) { return callback; } -exports.isMocked = muk.isMocked; +export type MockError = Error | string; /** * create an error instance @@ -77,7 +77,7 @@ exports.isMocked = muk.isMocked; * @param {Object} props - props * @return {Error} error - error */ -exports._createError = function(error, props) { +function _createError(error?: MockError, props?: Record): Error { if (!error) { error = new Error('mm mock error'); error.name = 'MockError'; @@ -86,36 +86,51 @@ exports._createError = function(error, props) { error = new Error(error); error.name = 'MockError'; } - props = props || {}; - for (const key in props) { - error[key] = props[key]; - } - + Object.assign(error, props); return error; -}; +} -exports._mockError = function(mod, method, error, props, timeout, once) { +function _mockError(mod: any, method: string, error?: MockError, props?: Record | number, + timeout?: number | string, once?: boolean) { if (typeof props === 'number') { timeout = props; props = {}; } - error = exports._createError(error, props); + error = _createError(error, props); - if (timeout) { - timeout = parseInt(timeout, 10); + if (typeof timeout !== 'number') { + timeout = parseInt(String(timeout || '0'), 10); } - timeout = timeout || 0; - mock(mod, method, thenify(function() { - const callback = getCallback(arguments); - setTimeout(function() { + const isGeneratorFunction = is.generatorFunction(mod[method]); + const isAsyncFunction = is.asyncFunction(mod[method]); + if (isGeneratorFunction) { + mock(mod, method, function* () { + yield scheduler.wait(timeout); if (once) { - exports.restore(); + restore(); + } + throw error; + }); + } else if (isAsyncFunction) { + mock(mod, method, async function() { + await scheduler.wait(timeout); + if (once) { + restore(); + } + throw error; + }); + } + + mock(mod, method, thenify.withCallback((...args: any[]) => { + const callback = getCallback(args); + setTimeout(() => { + if (once) { + restore(); } callback(error); }, timeout); })); - return this; -}; +} /** * Mock async function error. @@ -124,16 +139,12 @@ exports._mockError = function(mod, method, error, props, timeout, once) { * @param {String|Error} error, error string message or error instance. * @param {Object} props, error properties * @param {Number} timeout, mock async callback timeout, default is 0. - * @param mod - * @param method - * @param error - * @param props - * @param timeout - * @return {mm} this - mm */ -exports.error = function(mod, method, error, props, timeout) { - return exports._mockError(mod, method, error, props, timeout); -}; +function mockError(mod: any, method: string, error?: MockError, + props?: Record | number, + timeout?: number) { + return _mockError(mod, method, error, props, timeout); +} /** * Mock async function error once. @@ -142,16 +153,12 @@ exports.error = function(mod, method, error, props, timeout) { * @param {String|Error} error, error string message or error instance. * @param {Object} props, error properties * @param {Number} timeout, mock async callback timeout, default is 0. - * @param mod - * @param method - * @param error - * @param props - * @param timeout - * @return {mm} this - mm */ -exports.errorOnce = function(mod, method, error, props, timeout) { - return exports._mockError(mod, method, error, props, timeout, true); -}; +function errorOnce(mod: any, method: string, error?: MockError, + props?: Record | number, + timeout?: number) { + return _mockError(mod, method, error, props, timeout, true); +} /** * mock return callback(null, data1, data2). @@ -160,28 +167,38 @@ exports.errorOnce = function(mod, method, error, props, timeout) { * @param {String} method, mock module object method name. * @param {Array} datas, return datas array. * @param {Number} timeout, mock async callback timeout, default is 10. - * @param mod - * @param method - * @param datas - * @param timeout - * @return {mm} this - mm */ -exports.datas = function(mod, method, datas, timeout) { +function mockDatas(mod: any, method: string, datas: any[] | any, timeout?: number) { if (timeout) { - timeout = parseInt(timeout, 10); + timeout = parseInt(String(timeout), 10); } timeout = timeout || 0; + const isGeneratorFunction = is.generatorFunction(mod[method]); + const isAsyncFunction = is.asyncFunction(mod[method]); + if (isGeneratorFunction) { + mock(mod, method, function* () { + yield scheduler.wait(timeout); + return datas; + }); + return; + } else if (isAsyncFunction) { + mock(mod, method, async function() { + await scheduler.wait(timeout); + return datas; + }); + return; + } + if (!Array.isArray(datas)) { datas = [ datas ]; } - mock(mod, method, thenify(function() { - const callback = getCallback(arguments); - setTimeout(function() { + mock(mod, method, thenify.withCallback((...args: any[]) => { + const callback = getCallback(args); + setTimeout(() => { callback.apply(mod, [ null ].concat(datas)); }, timeout); })); - return this; -}; +} /** * mock return callback(null, data). @@ -190,15 +207,25 @@ exports.datas = function(mod, method, datas, timeout) { * @param {String} method, mock module object method name. * @param {Object} data, return data. * @param {Number} timeout, mock async callback timeout, default is 0. - * @param mod - * @param method - * @param data - * @param timeout - * @return {mm} this - mm */ -exports.data = function(mod, method, data, timeout) { - return exports.datas(mod, method, [ data ], timeout); -}; +function mockData(mod: any, method: string, data: any, timeout?: number) { + const isGeneratorFunction = is.generatorFunction(mod[method]); + const isAsyncFunction = is.asyncFunction(mod[method]); + if (isGeneratorFunction || isAsyncFunction) { + return mockDatas(mod, method, data, timeout); + } + return mockDatas(mod, method, [ data ], timeout); +} + +function dataWithAsyncDispose(mod: any, method: string, data: any, timeout?: number) { + data = { + ...data, + async [Symbol.asyncDispose]() { + // do nothing + }, + }; + return mockData(mod, method, data, timeout); +} /** * mock return callback(null, null). @@ -206,30 +233,26 @@ exports.data = function(mod, method, data, timeout) { * @param {Object} mod, module object * @param {String} method, mock module object method name. * @param {Number} [timeout], mock async callback timeout, default is 0. - * @param mod - * @param method - * @param timeout - * @return {mm} this - mm */ -exports.empty = function(mod, method, timeout) { - return exports.datas(mod, method, null, timeout); -}; +function mockEmpty(mod: any, method: string, timeout?: number) { + return mockDatas(mod, method, [ null ], timeout); +} /** * spy a function * @param {Object} mod, module object * @param {String} method, mock module object method name. - * @param mod - * @param method */ -exports.spy = function(mod, method) { - if (typeof mod[method] !== 'function') throw new Error(`spy target ${method} is not a function`); +function spy(mod: any, method: string) { + if (typeof mod[method] !== 'function') { + throw new Error(`spy target ${method} is not a function`); + } const originalFn = mod[method]; - const wrap = function proxy() { - return originalFn.apply(this, arguments); + const wrap = function proxy(this: any, ...args: any[]) { + return originalFn.apply(this, args); }; mock(mod, method, wrap); -}; +} /** * mock function sync throw error @@ -238,17 +261,13 @@ exports.spy = function(mod, method) { * @param {String} method, mock module object method name. * @param {String|Error} error, error string message or error instance. * @param {Object} [props], error properties - * @param mod - * @param method - * @param error - * @param props */ -exports.syncError = function(mod, method, error, props) { - error = exports._createError(error, props); - mock(mod, method, function() { +function syncError(mod: any, method: string, error?: MockError, props?: Record) { + error = _createError(error, props); + mock(mod, method, () => { throw error; }); -}; +} /** * mock function sync return data @@ -256,32 +275,24 @@ exports.syncError = function(mod, method, error, props) { * @param {Object} mod, module object * @param {String} method, mock module object method name. * @param {Object} data, return data. - * @param mod - * @param method - * @param data */ -exports.syncData = function(mod, method, data) { - mock(mod, method, function() { +function syncData(mod: any, method: string, data?: any) { + mock(mod, method, () => { return data; }); -}; +} /** * mock function sync return nothing * * @param {Object} mod, module object * @param {String} method, mock module object method name. - * @param mod - * @param method */ -exports.syncEmpty = function(mod, method) { - exports.syncData(mod, method); -}; - -exports.http = {}; -exports.https = {}; +function syncEmpty(mod: any, method: string) { + return syncData(mod, method); +} -function matchURL(options, params) { +function matchURL(options: any, params: any) { const url = params && params.url || params; const host = params && params.host; @@ -308,19 +319,22 @@ function mockRequest() { const req = new Duplex({ write() {}, read() {}, - }); - req.abort = function() { + }) as any; + req.abort = () => { req._aborted = true; - process.nextTick(function() { + process.nextTick(() => { const err = new Error('socket hang up'); - err.code = 'ECONNRESET'; + Reflect.set(err, 'code', 'ECONNRESET'); req.emit('error', err); }); }; - req.socket = {}; + req.socket = { remoteAddress: '127.0.0.1' }; return req; } +export type RequestURL = string | RegExp | URL | object; +export type ResponseData = string | Buffer | Readable; + /** * Mock http.request(). * @param {String|RegExp|Object} url, request url path. @@ -329,16 +343,11 @@ function mockRequest() { * If data is Array, then res will emit `data` event many times. * @param {Object} headers, mock response headers. * @param {Number} [delay], response delay time, default is 10. - * @param url - * @param data - * @param headers - * @param delay - * @return {mm} this - mm */ -exports.http.request = function(url, data, headers, delay) { +function mockHttpRequest(url: RequestURL, data: ResponseData, headers?: Record, delay?: number) { backupOriginalRequest(http); - return _request.call(this, http, url, data, headers, delay); -}; + return _request(http, url, data, headers, delay); +} /** * Mock https.request(). @@ -348,18 +357,13 @@ exports.http.request = function(url, data, headers, delay) { * If data is Array, then res will emit `data` event many times. * @param {Object} headers, mock response headers. * @param {Number} [delay], response delay time, default is 0. - * @param url - * @param data - * @param headers - * @param delay - * @return {mm} this - mm */ -exports.https.request = function(url, data, headers, delay) { +function mockHttpsRequest(url: RequestURL, data: ResponseData, headers?: Record, delay?: number) { backupOriginalRequest(https); - return _request.call(this, https, url, data, headers, delay); -}; + return _request(https, url, data, headers, delay); +} -function backupOriginalRequest(mod) { +function backupOriginalRequest(mod: any) { if (!mod.__sourceRequest) { mod.__sourceRequest = mod.request; } @@ -368,20 +372,25 @@ function backupOriginalRequest(mod) { } } -function _request(mod, url, data, headers, delay) { +function _request(mod: any, url: any, data: any, headers?: any, delay?: number) { headers = headers || {}; if (delay) { - delay = parseInt(delay, 10); + delay = parseInt(String(delay), 10); } delay = delay || 0; - mod.get = function(options, callback) { + // mod.get = function(options: any, callback: any) { + // const req = mod.request(options, callback); + // req.end(); + // return req; + // }; + mock(mod, 'get', function(options: any, callback: any) { const req = mod.request(options, callback); req.end(); return req; - }; + }); - mod.request = function(options, callback) { + mock(mod, 'request', function(options: any, callback: any) { let datas = []; let stream = null; // read stream if (typeof data.read === 'function') { @@ -405,7 +414,7 @@ function _request(mod, url, data, headers, delay) { req.on('response', callback); } - let res; + let res: any; if (stream) { res = stream; } else { @@ -421,16 +430,16 @@ function _request(mod, url, data, headers, delay) { if (!req._aborted) { if (typeof chunk === 'string') { - chunk = Buffer.from ? Buffer.from(chunk) : new Buffer(chunk); + chunk = Buffer.from(chunk); } - if (this.charset) { + if ('charset' in this && this.charset) { chunk = chunk.toString(this.charset); } this.push(chunk); } }, }); - res.setEncoding = function(charset) { + res.setEncoding = function(charset: string) { res.charset = charset; }; } @@ -452,8 +461,7 @@ function _request(mod, url, data, headers, delay) { } return req; - }; - return this; + }); } /** @@ -462,15 +470,11 @@ function _request(mod, url, data, headers, delay) { * @param {String|Error} reqError, request error. * @param {String|Error} resError, response error. * @param {Number} [delay], request error delay time, default is 0. - * @param url - * @param reqError - * @param resError - * @param delay */ -exports.http.requestError = function(url, reqError, resError, delay) { +function mockHttpRequestError(url: RequestURL, reqError?: MockError, resError?: MockError, delay?: number) { backupOriginalRequest(http); - _requestError.call(this, http, url, reqError, resError, delay); -}; + _requestError(http, url, reqError, resError, delay); +} /** * Mock https.request() error. @@ -478,19 +482,15 @@ exports.http.requestError = function(url, reqError, resError, delay) { * @param {String|Error} reqError, request error. * @param {String|Error} resError, response error. * @param {Number} [delay], request error delay time, default is 0. - * @param url - * @param reqError - * @param resError - * @param delay */ -exports.https.requestError = function(url, reqError, resError, delay) { +function mockHttpsRequestError(url: RequestURL, reqError?: MockError, resError?: MockError, delay?: number) { backupOriginalRequest(https); - _requestError.call(this, https, url, reqError, resError, delay); -}; + _requestError(https, url, reqError, resError, delay); +} -function _requestError(mod, url, reqError, resError, delay) { +function _requestError(mod: any, url: any, reqError?: MockError, resError?: MockError, delay?: number) { if (delay) { - delay = parseInt(delay, 10); + delay = parseInt(String(delay), 10); } delay = delay || 0; if (reqError && typeof reqError === 'string') { @@ -502,13 +502,13 @@ function _requestError(mod, url, reqError, resError, delay) { resError.name = 'MockHttpResponseError'; } - mod.get = function(options, callback) { + mock(mod, 'get', function(options: any, callback: any) { const req = mod.request(options, callback); req.end(); return req; - }; + }); - mod.request = function(options, callback) { + mock(mod, 'request', function(options: any, callback: any) { const match = matchURL(options, url); if (!match) { return mod.__sourceRequest(options, callback); @@ -528,13 +528,13 @@ function _requestError(mod, url, reqError, resError, delay) { const res = new Duplex({ read() {}, write() {}, - }); + }) as any; res.socket = req.socket; res.statusCode = 200; res.headers = { server: 'MockMateServer', }; - process.nextTick(function() { + process.nextTick(() => { if (!req._aborted) { req.emit('error', resError); } @@ -545,8 +545,7 @@ function _requestError(mod, url, reqError, resError, delay) { }, delay); return req; - }; - return this; + }); } /** @@ -556,44 +555,21 @@ function _requestError(mod, url, reqError, resError, delay) { * @param {String} stderr stderr * @param {Integer} timeout stdout/stderr/close event emit timeout */ -exports.spawn = function(code, stdout, stderr, timeout) { +function spawn(code: number, stdout: string, stderr: string, timeout: number = 0) { const evt = new EventEmitter(); - mock(cp, 'spawn', function() { + mock(cp, 'spawn', () => { return evt; }); - setTimeout(function() { + setTimeout(() => { stdout && evt.emit('stdout', stdout); stderr && evt.emit('stderr', stderr); evt.emit('close', code); evt.emit('exit', code); }, timeout); -}; - -/** - * remove all mock effects. - * @return {mm} this - mm - */ -exports.restore = function() { - if (http.__sourceRequest) { - http.request = http.__sourceRequest; - http.__sourceRequest = null; - } - if (http.__sourceGet) { - http.get = http.__sourceGet; - http.__sourceGet = null; - } - - if (https.__sourceRequest) { - https.request = https.__sourceRequest; - https.__sourceRequest = null; - } - - muk.restore(); - return this; -}; +} -function omit(obj, key) { - const newObj = {}; +function omit(obj: Record, key: string) { + const newObj: Record = {}; for (const k in obj) { if (k !== key) { newObj[k] = obj[k]; @@ -601,3 +577,73 @@ function omit(obj, key) { } return newObj; } + +/** + * mock class method from instance + */ +function classMethod(instance: any, property: string, value?: any) { + mock(instance.constructor.prototype, property, value); +} + +const mockHttp = { + request: mockHttpRequest, + requestError: mockHttpRequestError, +}; + +const mockHttps = { + request: mockHttpsRequest, + requestError: mockHttpsRequestError, +}; + +// import { mm, restore } from 'mm'; +export { + isMocked, + mock, + mock as mm, + mockDatas as datas, + mockDatas, + mockData as data, + mockData, + dataWithAsyncDispose, + mockEmpty as empty, + mockEmpty, + mockError as error, + mockError, + spy, + errorOnce, + syncError, + syncEmpty, + syncData, + mockHttp as http, + mockHttps as https, + spawn, + restore, + classMethod, +}; + +// import mm from 'mm'; +export default Object.assign(mock, { + isMocked, + mock, + mm: mock, + datas: mockDatas, + mockDatas, + data: mockData, + mockData, + dataWithAsyncDispose, + empty: mockEmpty, + mockEmpty, + error: mockError, + mockError, + spy, + errorOnce, + syncError, + syncEmpty, + syncData, + http: mockHttp, + https: mockHttps, + spawn, + restore, + classMethod, +}); + diff --git a/test/async-await.js b/test/async-await.test.ts similarity index 65% rename from test/async-await.js rename to test/async-await.test.ts index 1f6982e..ce691fd 100644 --- a/test/async-await.js +++ b/test/async-await.test.ts @@ -1,6 +1,7 @@ -const mm = require('..'); +import { strict as assert } from 'node:assert'; +import { mm, restore, mockDatas } from '../src/index.js'; -describe('test/async-await.test.js', () => { +describe('test/async-await.test.ts', () => { const foo = { async request() { return 'yes'; @@ -10,7 +11,7 @@ describe('test/async-await.test.js', () => { }, }; - afterEach(mm.restore); + afterEach(restore); describe('mm()', () => { it('should mock async function', async () => { @@ -19,11 +20,11 @@ describe('test/async-await.test.js', () => { return 'no'; }); datas = await foo.request(); - datas.should.equal('no'); + assert.equal(datas, 'no'); - mm.restore(); + restore(); datas = await foo.request(); - datas.should.equal('yes'); + assert(datas, 'yes'); }); it('should mock async function to normal throw type error', async () => { @@ -34,7 +35,8 @@ describe('test/async-await.test.js', () => { foo.request(); throw new Error('should not run this'); } catch (err) { - err.message.should.equal('Can\'t mock async function to normal function for property "request"'); + assert(err instanceof Error); + assert.equal(err.message, 'Can\'t mock async function to normal function for property "request"'); } }); @@ -46,7 +48,8 @@ describe('test/async-await.test.js', () => { foo.generatorRequest(); throw new Error('should not run this'); } catch (err) { - err.message.should.equal('Can\'t mock async function to normal function for property "generatorRequest"'); + assert(err instanceof Error); + assert.equal(err.message, 'Can\'t mock async function to normal function for property "generatorRequest"'); } }); @@ -58,7 +61,8 @@ describe('test/async-await.test.js', () => { foo.request(); throw new Error('should not run this'); } catch (err) { - err.message.should.equal('should not run this'); + assert(err instanceof Error); + assert.equal(err.message, 'should not run this'); } }); }); @@ -66,13 +70,13 @@ describe('test/async-await.test.js', () => { describe('datas(), data()', () => { it('should mock async function', async () => { let datas; - mm.datas(foo, 'request', 'no'); + mockDatas(foo, 'request', 'no'); datas = await foo.request(); - datas.should.equal('no'); + assert.equal(datas, 'no'); - mm.restore(); + restore(); datas = await foo.request(); - datas.should.equal('yes'); + assert.equal(datas, 'yes'); }); }); }); diff --git a/test/asyncDispose.test.ts b/test/asyncDispose.test.ts index f172f69..3a4bc2a 100644 --- a/test/asyncDispose.test.ts +++ b/test/asyncDispose.test.ts @@ -1,6 +1,5 @@ import { strict as assert } from 'node:assert'; -require('@hazae41/symbol-dispose-polyfill'); -import mm from '../index'; +import * as mm from '../src/index.js'; describe('test/asyncDispose.test.ts', () => { const foo = { diff --git a/test/co.js b/test/co.js deleted file mode 100644 index d5a4582..0000000 --- a/test/co.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -const urllib = require('urllib'); -const co = require('co'); -const fs = require('fs'); -const mm = require('../'); - -co(function* () { - mm.http.request(/\//, fs.createReadStream(__filename), { statusCode: 404 }); - const r = yield urllib.request('http://nodejs.org'); - console.log(r.status, r.headers, r.data.length); - r.data.toString().should.equal(fs.readFileSync(__filename, 'utf8')); -})(); diff --git a/test/es6.js b/test/es6.test.ts similarity index 57% rename from test/es6.js rename to test/es6.test.ts index fca410f..f86c207 100644 --- a/test/es6.js +++ b/test/es6.test.ts @@ -1,14 +1,12 @@ -'use strict'; +import { strict as assert } from 'node:assert'; +import mm from '../src/index.js'; -const assert = require('assert'); -const mm = require('..'); - -describe('test/es6.test.js', () => { +describe('test/es6.test.ts', () => { const foo = { - * getMultiValues() { + async getMultiValues() { return [ 1, 2, 3 ]; }, - * getValue() { + async getValue() { return 1; }, }; @@ -16,24 +14,24 @@ describe('test/es6.test.js', () => { afterEach(mm.restore); describe('datas(), data()', () => { - it('should mock generator function', function* () { + it('should mock async function', async () => { let datas; mm.datas(foo, 'getMultiValues', [ 'b1', 'b2', 'b3' ]); - datas = yield* foo.getMultiValues(); - datas.should.eql([ 'b1', 'b2', 'b3' ]); + datas = await foo.getMultiValues(); + assert.deepEqual(datas, [ 'b1', 'b2', 'b3' ]); mm.datas(foo, 'getMultiValues', 1); - datas = yield* foo.getMultiValues(); - datas.should.equal(1); + datas = await foo.getMultiValues(); + assert.equal(datas, 1); let data; mm.data(foo, 'getValue', 2, 500); - data = yield* foo.getValue(); - data.should.equal(2); + data = await foo.getValue(); + assert.equal(data, 2); mm.restore(); - data = yield* foo.getValue(); - data.should.equal(1); + data = await foo.getValue(); + assert.equal(data, 1); }); }); @@ -77,76 +75,77 @@ describe('test/es6.test.js', () => { }); describe('error(), errorOnce()', () => { - it('should mock error', function* () { + it('should mock error', async () => { mm.error(foo, 'getValue'); try { - yield foo.getValue(); + await foo.getValue(); throw new Error('should not run this'); } catch (err) { - err.message.should.equal('mm mock error'); + assert(err instanceof Error); + assert.equal(err.message, 'mm mock error'); } - mm.error(foo, 'getValue', 'foo error', 200); + mm.error(foo, 'getValue', 'foo error', { foo: '200' }); try { - yield* foo.getValue(); + await foo.getValue(); throw new Error('should not run this'); } catch (err) { - err.message.should.equal('foo error'); + assert(err instanceof Error); + assert.equal(err.message, 'foo error'); } mm.error(foo, 'getValue', new Error('new foo error')); try { - yield* foo.getValue(); + await foo.getValue(); throw new Error('should not run this'); - } catch (err) { - err.message.should.equal('new foo error'); + } catch (err: any) { + assert.equal(err.message, 'new foo error'); } mm.error(foo, 'getValue', new Error('new foo error'), { status: 500 }); try { - yield* foo.getValue(); + await foo.getValue(); throw new Error('should not run this'); - } catch (err) { - err.message.should.equal('new foo error'); - err.status.should.equal(500); + } catch (err: any) { + assert.equal(err.message, 'new foo error'); + assert.equal(err.status, 500); } mm.error(foo, 'getValue', new Error('new foo error'), { status: 500 }, 100); - let start; + const start = Date.now(); try { - start = Date.now(); - yield* foo.getValue(); + await foo.getValue(); throw new Error('should not run this'); - } catch (err) { + } catch (err: any) { const use = Date.now() - start; - err.message.should.equal('new foo error'); - err.status.should.equal(500); - use.should.above(90); + assert.equal(err.message, 'new foo error'); + assert.equal(err.status, 500); + assert(use > 90); } }); - it('should mock error once', function* () { + it('should mock error once', async () => { mm.errorOnce(foo, 'getValue'); try { - yield foo.getValue(); + await foo.getValue(); throw new Error('should not run this'); - } catch (err) { - err.message.should.equal('mm mock error'); + } catch (err: any) { + assert.equal(err.message, 'mm mock error'); } - const v = yield foo.getValue(); - v.should.equal(1); + const v = await foo.getValue(); + assert.equal(v, 1); mm.errorOnce(foo, 'getValue'); try { - yield* foo.getValue(); + await foo.getValue(); throw new Error('should not run this'); - } catch (err) { - err.message.should.equal('mm mock error'); + } catch (err: any) { + assert.equal(err.message, 'mm mock error'); } - const v1 = yield* foo.getValue(); - v1.should.equal(1); + const v1 = await foo.getValue(); + assert.equal(v1, 1); }); }); }); diff --git a/test/foo.js b/test/foo.js deleted file mode 100644 index 6a6758d..0000000 --- a/test/foo.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -exports.get = function(query, callback, headers, cache) { - process.nextTick(callback.bind(null, null, { - query, - headers, - cache, - })); -}; - -exports.check = function(callback, a1, a2) { - process.nextTick(callback.bind(null, null, { - a1, - a2, - })); -}; - -exports.query = function(callback) { - process.nextTick(callback.bind(null, null, { - result: 'result', - })); -}; - -exports.getMultiValues = function(callback) { - process.nextTick(callback.bind(null, null, 'a1', 'a2', 'a3')); -}; - -exports.mirror = function(input) { - return input; -}; diff --git a/test/foo.ts b/test/foo.ts new file mode 100644 index 0000000..1a29d7e --- /dev/null +++ b/test/foo.ts @@ -0,0 +1,59 @@ +interface QueryResult { + query: any; + headers: any; + cache: any; +} + +interface CheckResult { + a1: any; + a2: any; +} + +interface QueryCallback { + (err: any, result: QueryResult): void; +} + +interface CheckCallback { + (err: any, result: CheckResult): void; +} + +interface SimpleCallback { + (err: any, result: { result: string }): void; +} + +interface MultiValuesCallback { + (err: any, ...results: any[]): void; +} + +export const foo = { + get(query: any, callback: QueryCallback, headers?: any, cache?: any): void { + process.nextTick(callback.bind(null, null, { + query, + headers, + cache, + })); + }, + + check(callback: CheckCallback, a1: any, a2: any): void { + process.nextTick(callback.bind(null, null, { + a1, + a2, + })); + }, + + query(callback?: SimpleCallback): void { + if (!callback) return; + process.nextTick(callback.bind(null, null, { + result: 'result', + })); + }, + + getMultiValues(callback: MultiValuesCallback): void { + process.nextTick(callback.bind(null, null, 'a1', 'a2', 'a3')); + }, + + mirror(input: T): T { + return input; + }, +}; + diff --git a/test/mm.test.js b/test/mm.test.ts similarity index 80% rename from test/mm.test.js rename to test/mm.test.ts index 3a66fda..4316fa5 100644 --- a/test/mm.test.js +++ b/test/mm.test.ts @@ -1,25 +1,30 @@ -require('should'); -const os = require('os'); -const path = require('path'); -const fs = require('fs'); -const assert = require('assert'); -const http = require('http'); -const https = require('https'); -const child_process = require('child_process'); -const { randomUUID } = require('crypto'); -const pedding = require('pedding'); -const ChunkStream = require('chunkstream'); -const mm = require('../'); -const foo = require('./foo'); +import 'should'; +import os from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import fs from 'node:fs'; +import assert from 'node:assert'; +import http from 'node:http'; +import https from 'node:https'; +import child_process from 'node:child_process'; +import { randomUUID } from 'node:crypto'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import pedding from 'pedding'; +import mm from '../src/index.js'; +import { foo } from './foo.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); describe('test/mm.test.js', () => { - let port = null; - let sslPort = null; + let port: any; + let sslPort: any; before(function(done) { done = pedding(2, done); - const app = http.createServer(function(req, res) { + const app: any = http.createServer(function(req, res) { res.end(req.method + ' ' + req.url); }); app.listen(0, function() { @@ -28,7 +33,7 @@ describe('test/mm.test.js', () => { }); const fixtures = path.join(__dirname, 'fixtures'); - const appssl = https.createServer({ + const appssl: any = https.createServer({ key: fs.readFileSync(path.join(fixtures, 'test_key.pem')), cert: fs.readFileSync(path.join(fixtures, 'test_cert.pem')), }, function(req, res) { @@ -102,7 +107,7 @@ describe('test/mm.test.js', () => { mm.data(foo, 'get', [ 'b1', 'b2' ]); foo.get('q1', function(err, data) { assert(!err); - data.should.eql([ 'b1', 'b2' ]); + assert.deepEqual(data, [ 'b1', 'b2' ]); done(); }); }); @@ -112,12 +117,12 @@ describe('test/mm.test.js', () => { done = pedding(2, done); foo.get('q1', function(err, data) { assert(!err); - data.should.eql([ 'b1', 'b2' ]); + assert.deepEqual(data, [ 'b1', 'b2' ]); done(); }); foo.get('q1', function(err, data) { assert(!err); - data.should.eql([ 'b1', 'b2' ]); + assert.deepEqual(data, [ 'b1', 'b2' ]); done(); }); }); @@ -200,7 +205,7 @@ describe('test/mm.test.js', () => { assert(err); err.name.should.equal('MockError'); err.message.should.equal('mm mock error'); - err.code.should.equal('ENOENT'); + err.code!.should.equal('ENOENT'); assert(!data); done(); }); @@ -228,7 +233,7 @@ describe('test/mm.test.js', () => { assert(err); err.name.should.equal('MockError'); err.message.should.equal('500ms timeout'); - err.code.should.equal('ENOENT'); + err.code!.should.equal('ENOENT'); assert(!data); use.should.above(490); done(); @@ -236,7 +241,6 @@ describe('test/mm.test.js', () => { }); it.skip('should work for callback is not the last params case', function(done) { - const foo = require('./foo'); done = pedding(3, done); mm.error(foo, 'get', 'mock foo.get error'); @@ -264,7 +268,6 @@ describe('test/mm.test.js', () => { }); it.skip('should throw error', function() { - const foo = require('./foo'); mm.error(foo, 'query', 'mock foo.check error'); (function() { foo.query(); @@ -275,13 +278,11 @@ describe('test/mm.test.js', () => { describe('syncData, syncEmpty', function() { it('should mock data ok', function() { - const foo = require('./foo'); mm.syncData(foo, 'mirror', 'test'); foo.mirror('input').should.equal('test'); }); it('should mock empty ok', function() { - const foo = require('./foo'); mm.syncEmpty(foo, 'mirror'); assert(!foo.mirror('input')); }); @@ -289,7 +290,6 @@ describe('test/mm.test.js', () => { describe('syncError', function() { it('should mock error with out error message ok', function() { - const foo = require('./foo'); mm.syncError(foo, 'mirror'); (function() { foo.mirror('input'); @@ -297,7 +297,6 @@ describe('test/mm.test.js', () => { }); it('should mock error with string error message ok', function() { - const foo = require('./foo'); mm.syncError(foo, 'mirror', 'mock error'); (function() { foo.mirror('input'); @@ -305,7 +304,6 @@ describe('test/mm.test.js', () => { }); it('should mock error with error object ok', function() { - const foo = require('./foo'); mm.syncError(foo, 'mirror', new Error('mock error')); (function() { foo.mirror('input'); @@ -325,7 +323,7 @@ describe('test/mm.test.js', () => { const mockURL = '/foo'; const mockResData = 'mock data'; const mockResHeaders = { server: 'mock server' }; - mm[modName].request(mockURL, mockResData, mockResHeaders); + (mm as any)[modName].request(mockURL, mockResData, mockResHeaders); mod.get({ host: '127.0.0.1', @@ -368,7 +366,7 @@ describe('test/mm.test.js', () => { const mockURL = /foo$/; const mockResData = 'mock data with regex url'; const mockResHeaders = { server: 'mock server' }; - mm[modName].request(mockURL, mockResData, mockResHeaders); + (mm as any)[modName].request(mockURL, mockResData, mockResHeaders); done = pedding(2, done); @@ -409,7 +407,7 @@ describe('test/mm.test.js', () => { const mockURL = /foo$/; const mockResData = [ 'mock data with regex url', '哈哈' ]; const mockResHeaders = { server: 'mock server' }; - mm[modName].request(mockURL, mockResData, mockResHeaders, 500); + (mm as any)[modName].request(mockURL, mockResData, mockResHeaders, 500); const start = Date.now(); mod.get({ @@ -435,7 +433,7 @@ describe('test/mm.test.js', () => { it('should mock ' + modName + '.request({url: "/bar/foo"}) 500ms response delay', function(done) { const mockResData = [ 'mock data with regex url', '哈哈' ]; const mockResHeaders = { server: 'mock server' }; - mm[modName].request({ url: '/bar/foo' }, mockResData, mockResHeaders, 500); + (mm as any)[modName].request({ url: '/bar/foo' }, mockResData, mockResHeaders, 500); const start = Date.now(); mod.get({ @@ -458,44 +456,17 @@ describe('test/mm.test.js', () => { }); }); - it.skip('should mock ' + modName + '.request({url: "/bar/foo"}) with stream 500ms response delay', - function(done) { - const mockResData = new ChunkStream([ 'mock data with regex url', '哈哈' ]); - const mockResHeaders = { server: 'mock server' }; - mm[modName].request({ url: '/bar/foo' }, mockResData, mockResHeaders, 500); - - const start = Date.now(); - mod.get({ - host: 'npmjs.org', - path: '/bar/foo', - }, function(res) { - res.headers.should.eql(mockResHeaders); - res.setEncoding('utf8'); - let body = ''; - res.on('data', function(chunk) { - chunk.should.be.a.String; - body += chunk; - }); - res.on('end', function() { - const use = Date.now() - start; - body.should.equal([ 'mock data with regex url', '哈哈' ].join('')); - use.should.above(490); - done(); - }); - }); - }); - it('should mock ' + modName + '.request({url: "/bar/foo"}) pipe res work', done => { const mockResData = fs.createReadStream(__filename); const mockResHeaders = { server: 'mock server', statusCode: 200 }; - mm[modName].request('', mockResData, mockResHeaders); + (mm as any)[modName].request('', mockResData, mockResHeaders); mod.get({ host: 'npmjs.org', path: '/bar/foo', }, onResponse); - function onResponse(res) { + function onResponse(res: any) { res.statusCode.should.equal(200); res.headers.should.eql({ server: 'mock server' }); @@ -511,7 +482,7 @@ describe('test/mm.test.js', () => { function(done) { const mockResData = fs.createReadStream(__filename); const mockResHeaders = { server: 'mock server', statusCode: 201 }; - mm[modName].request('', mockResData, mockResHeaders); + (mm as any)[modName].request('', mockResData, mockResHeaders); const length = 5; done = pedding(length, done); for (let i = 0; i < length; i++) { @@ -520,12 +491,12 @@ describe('test/mm.test.js', () => { path: '/bar/foo', }, onResponse); } - function onResponse(res) { + function onResponse(res: any) { res.statusCode.should.equal(201); res.headers.should.eql({ server: 'mock server' }); res.setEncoding('utf8'); let body = ''; - res.on('data', function(chunk) { + res.on('data', function(chunk: string) { console.log('data emit: chunk size: %d', chunk.length); chunk.should.be.a.String; body += chunk; @@ -543,7 +514,7 @@ describe('test/mm.test.js', () => { it('should mock ' + modName + '.request({host: "cnodejs.org"}) 500ms response delay', function(done) { const mockResData = [ 'mock data with regex url', '哈哈' ]; const mockResHeaders = { server: 'mock server' }; - mm[modName].request({ host: 'cnodejs.org' }, mockResData, mockResHeaders, 500); + (mm as any)[modName].request({ host: 'cnodejs.org' }, mockResData, mockResHeaders, 500); const start = Date.now(); mod.get({ @@ -569,7 +540,7 @@ describe('test/mm.test.js', () => { it('should mock ' + modName + '.request({host: /cnodejs/}) 500ms response delay', function(done) { const mockResData = [ 'mock data with regex url', '哈哈' ]; const mockResHeaders = { server: 'mock server' }; - mm[modName].request({ host: /cnodejs/ }, mockResData, mockResHeaders, 500); + (mm as any)[modName].request({ host: /cnodejs/ }, mockResData, mockResHeaders, 500); const start = Date.now(); mod.get({ @@ -596,7 +567,7 @@ describe('test/mm.test.js', () => { const mockURL = /foo$/; const mockResData = 'mock data with regex url'; const mockResHeaders = { server: 'mock server' }; - mm[modName].request(mockURL, mockResData, mockResHeaders, 500); + (mm as any)[modName].request(mockURL, mockResData, mockResHeaders, 500); done = pedding(2, done); const start = Date.now(); @@ -628,7 +599,7 @@ describe('test/mm.test.js', () => { const mockURL = /foo$/; const mockResData = 'mock data with regex url'; const mockResHeaders = { server: 'mock server' }; - mm[modName].request(mockURL, mockResData, mockResHeaders, 1000); + (mm as any)[modName].request(mockURL, mockResData, mockResHeaders, 1000); const req = mod.get({ host: 'cnodejs.org', @@ -652,14 +623,14 @@ describe('test/mm.test.js', () => { describe('http(s).requestError()', function() { [ 'http', 'https' ].forEach(function(modName) { const mod = modName === 'http' ? http : https; - it('should ' + modName + '.reqeust() return req error', function(done) { + it('should ' + modName + '.request() return req error', function(done) { const modPort = modName === 'http' ? port : sslPort; done = pedding(2, done); const mockURL = '/req'; const reqError = 'mock req error'; - mm[modName].requestError(mockURL, reqError); + (mm as any)[modName].requestError(mockURL, reqError); let req = mod.get({ path: '/req', @@ -696,12 +667,12 @@ describe('test/mm.test.js', () => { it('should ' + modName + '.reqeust() return req error after response emit', function(done) { const mockURL = '/res'; const resError = 'mock res error'; - mm[modName].requestError(mockURL, null, resError); + (mm as any)[modName].requestError(mockURL, null, resError); done = pedding(2, done); const req = mod.get({ path: '/res', - }, function(res) { + }, function(res: any) { res.statusCode.should.eql(200); res.headers.server.should.eql('MockMateServer'); done(); @@ -717,14 +688,14 @@ describe('test/mm.test.js', () => { it('should ' + modName + '.reqeust() return res error 500ms delay', function(done) { const mockURL = '/res'; const resError = 'mock res error with 500ms delay'; - mm[modName].requestError(mockURL, null, resError, 500); + (mm as any)[modName].requestError(mockURL, null, resError, 500); done = pedding(2, done); const start = Date.now(); const req = mod.get({ path: '/res', - }, function(res) { - res.statusCode.should.eql(200); + }, function(res: any) { + res.statusCode!.should.eql(200); res.headers.server.should.eql('MockMateServer'); done(); }); @@ -741,7 +712,7 @@ describe('test/mm.test.js', () => { it('should ' + modName + '.reqeust() not emit req error 1000ms delay after req.abort()', function(done) { const mockURL = '/res'; const resError = 'mock res error with 500ms delay'; - mm[modName].requestError(mockURL, null, resError, 1000); + (mm as any)[modName].requestError(mockURL, null, resError, 1000); const start = Date.now(); const req = mod.get({ @@ -780,7 +751,7 @@ describe('test/mm.test.js', () => { done(); }); ls.on('close', function(code) { - code.should.equal(1); + code!.should.equal(1); done(); }); mm.restore(); @@ -797,25 +768,25 @@ describe('test/mm.test.js', () => { it('should mock process.env.KEY work', function() { const orginalEnv = process.env.NODE_ENV; mm(process.env, 'NODE_ENV', 'test2'); - process.env.NODE_ENV.should.equal('test2'); + process.env.NODE_ENV!.should.equal('test2'); mm.restore(); assert(process.env.NODE_ENV === orginalEnv); mm(process.env, 'NODE_ENV', 'test2'); - process.env.NODE_ENV.should.equal('test2'); + process.env.NODE_ENV!.should.equal('test2'); mm(process.env, 'NODE_ENV', 'production'); - process.env.NODE_ENV.should.equal('production'); + process.env.NODE_ENV!.should.equal('production'); mm.restore(); assert(process.env.NODE_ENV === orginalEnv); }); it('should mm() just like muk()', function(done) { - mm(fs, 'readFile', function(filename, callback) { + mm(fs, 'readFile', function(filename: any, callback: any) { process.nextTick(function() { const str = 'filename: ' + filename; - const buf = Buffer.from ? Buffer.from(str) : new Buffer(str); + const buf = Buffer.from ? Buffer.from(str) : Buffer.from(str); callback(null, buf); }); }); @@ -861,12 +832,12 @@ describe('test/mm.test.js', () => { it('shoud mock function with property', () => { const NativeDate = Date; - const mockNow = function(date) { - const NewDate = function(...args) { + const mockNow = function(date: any) { + const NewDate = function(...args: any[]) { if (args.length === 0) { return new NativeDate(date); } - return new NativeDate(...args); + return new NativeDate(args[0], args[1], args[2]); }; NewDate.now = function() { return new NativeDate(date).getTime(); @@ -892,20 +863,20 @@ describe('test/mm.test.js', () => { }); it('should mock HOME env', function() { - process.env.HOME.should.equal(this.HOME); + process.env.HOME!.should.equal(this.HOME); mm(process.env, 'HOME', '/tmp/home'); - process.env.HOME.should.equal('/tmp/home'); - process.env.TEST_ENV.should.equal('foo'); + process.env.HOME!.should.equal('/tmp/home'); + process.env.TEST_ENV!.should.equal('foo'); }); it('should mock HOME env to another value', function() { - process.env.HOME.should.equal(this.HOME); + process.env.HOME!.should.equal(this.HOME); mm(process.env, 'HOME', '/tmp/home2'); - process.env.HOME.should.equal('/tmp/home2'); - process.env.TEST_ENV.should.equal('foo'); + process.env.HOME!.should.equal('/tmp/home2'); + process.env.TEST_ENV!.should.equal('foo'); mm.restore(); - process.env.HOME.should.equal(this.HOME); + process.env.HOME!.should.equal(this.HOME); assert(!process.env.TEST_ENV); }); @@ -918,14 +889,14 @@ describe('test/mm.test.js', () => { }); describe('restore', function() { - let orgRequest; + let orgRequest: any; beforeEach(function() { orgRequest = http.request; }); afterEach(function() { http.request = orgRequest; }); - it('should not alter the http.request function withou http(s) used', function() { + it('should not alter the http.request function without http(s) used', function() { const obj = { foo() { return 'original foo'; @@ -944,7 +915,7 @@ describe('test/mm.test.js', () => { try { http.request({ path: '/foo' }, function() {}); throw new Error('should not run this'); - } catch (e) { + } catch (e: any) { e.message.should.equal('Never want to send request out'); } }); @@ -959,15 +930,15 @@ describe('test/mm.test.js', () => { mod.request = function() { throw new Error('Never want to send request out'); }; - mm[modName].request(mockURLFoo, mockResData, mockResHeaders); - mm[modName].request(mockURLBar, mockResData, mockResHeaders); + (mm as any)[modName].request(mockURLFoo, mockResData, mockResHeaders); + (mm as any)[modName].request(mockURLBar, mockResData, mockResHeaders); mm.restore(); try { const req = mod.request({ path: '/baz' }, function() {}); req.end(); throw new Error('should not run this'); - } catch (e) { + } catch (e: any) { e.message.should.equal('Never want to send request out'); } }); @@ -986,7 +957,7 @@ describe('test/mm.test.js', () => { it('should spy function with data', () => { const target = { - add(a, b) { + add(a: number, b: number) { return a + b; }, }; @@ -994,14 +965,14 @@ describe('test/mm.test.js', () => { mm.syncData(target, 'add', 3); target.add(1, 1).should.equal(3); target.add(2, 2).should.equal(3); - target.add.called.should.equal(2); - target.add.calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); - target.add.lastCalledArguments.should.eql([ 2, 2 ]); + (target.add as any).called.should.equal(2); + (target.add as any).calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); + (target.add as any).lastCalledArguments.should.eql([ 2, 2 ]); }); it('should spy async function with data', async () => { const target = { - async add(a, b) { + async add(a: number, b: number) { return a + b; }, }; @@ -1009,89 +980,54 @@ describe('test/mm.test.js', () => { mm.data(target, 'add', 3); (await target.add(1, 1)).should.equal(3); (await target.add(2, 2)).should.equal(3); - target.add.called.should.equal(2); - target.add.calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); - target.add.lastCalledArguments.should.eql([ 2, 2 ]); - }); - - it('should spy generator function with data', function* () { - const target = { - * add(a, b) { - return a + b; - }, - }; - - mm.data(target, 'add', 3); - (yield target.add(1, 1)).should.equal(3); - (yield target.add(2, 2)).should.equal(3); - target.add.called.should.equal(2); - target.add.calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); - target.add.lastCalledArguments.should.eql([ 2, 2 ]); + (target.add as any).called.should.equal(2); + (target.add as any).calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); + (target.add as any).lastCalledArguments.should.eql([ 2, 2 ]); }); it('should spy function', () => { const target = { - add(a, b) { + add(a: number, b: number) { this.foo(); return a + b; }, foo() { /* */ }, }; - mm(target, 'add', function() { + mm(target, 'add', function(this: any) { this.foo(); return 3; }); target.add(1, 1).should.equal(3); target.add(2, 2).should.equal(3); - target.add.called.should.equal(2); - target.add.calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); - target.add.lastCalledArguments.should.eql([ 2, 2 ]); + (target.add as any).called.should.equal(2); + (target.add as any).calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); + (target.add as any).lastCalledArguments.should.eql([ 2, 2 ]); }); it('should spy async function', async () => { const target = { - async add(a, b) { + async add(a: number, b: number) { await this.foo(); return a + b; }, async foo() { /* */ }, }; - mm(target, 'add', async function() { + mm(target, 'add', async function(this: any) { await this.foo(); return 3; }); (await target.add(1, 1)).should.equal(3); (await target.add(2, 2)).should.equal(3); - target.add.called.should.equal(2); - target.add.calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); - target.add.lastCalledArguments.should.eql([ 2, 2 ]); - }); - - it('should spy generator function', function* () { - const target = { - * add(a, b) { - yield this.foo(); - return a + b; - }, - * foo() { /* */ }, - }; - - mm(target, 'add', function* () { - yield this.foo(); - return 3; - }); - (yield target.add(1, 1)).should.equal(3); - (yield target.add(2, 2)).should.equal(3); - target.add.called.should.equal(2); - target.add.calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); - target.add.lastCalledArguments.should.eql([ 2, 2 ]); + (target.add as any).called.should.equal(2); + (target.add as any).calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); + (target.add as any).lastCalledArguments.should.eql([ 2, 2 ]); }); it('should ignore jest.fn()', () => { const target = { - add(a, b) { + add(a: number, b: number) { return a + b; }, }; @@ -1109,7 +1045,7 @@ describe('test/mm.test.js', () => { it('should mm.spy() work', async () => { const target = { - async add(a, b) { + async add(a: number, b: number) { await this.foo(); return a + b; }, @@ -1119,30 +1055,26 @@ describe('test/mm.test.js', () => { mm.spy(target, 'add'); (await target.add(1, 1)).should.equal(2); (await target.add(2, 2)).should.equal(4); - target.add.called.should.equal(2); - target.add.calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); - target.add.lastCalledArguments.should.eql([ 2, 2 ]); + (target.add as any).called.should.equal(2); + (target.add as any).calledArguments.should.eql([[ 1, 1 ], [ 2, 2 ]]); + (target.add as any).lastCalledArguments.should.eql([ 2, 2 ]); }); it('should reset spy statistics after restore', () => { const target = { - add(a, b) { + add(a: number, b: number) { return a + b; }, }; mm.spy(target, 'add'); target.add(1, 1); - target.add.called.should.equal(1); - target.add.calledArguments.should.eql([[ 1, 1 ]]); - target.add.lastCalledArguments.should.eql([ 1, 1 ]); + (target.add as any).called.should.equal(1); + (target.add as any).calledArguments.should.eql([[ 1, 1 ]]); + (target.add as any).lastCalledArguments.should.eql([ 1, 1 ]); mm.restore(); - assert.strictEqual(target.add.called, undefined); - assert.strictEqual(target.add.calledArguments, undefined); - assert.strictEqual(target.add.lastCalledArguments, undefined); + assert.strictEqual((target.add as any).called, undefined); + assert.strictEqual((target.add as any).calledArguments, undefined); + assert.strictEqual((target.add as any).lastCalledArguments, undefined); }); }); }); - -require('./es6'); -require('./thunk'); -require('./async-await'); diff --git a/test/thunk.js b/test/thunk.js deleted file mode 100644 index b1d74d5..0000000 --- a/test/thunk.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict'; - -const thunkify = require('thunkify-wrap'); -const mm = require('..'); - -describe('test/thunk.test.js', function() { - const foo = { - getMultiValues: thunkify(function(arg, callback) { - setImmediate(function() { - callback(null, 1, 2, 3); - }); - }), - getValue: thunkify(function(arg, callback) { - setImmediate(function() { - callback(null, 1); - }); - }), - }; - - afterEach(mm.restore); - - describe('datas(), data()', function() { - it('should mock generator function', function* () { - mm.datas(foo, 'getMultiValues', [ 'b1', 'b2', 'b3' ]); - let datas = yield foo.getMultiValues(); - datas.should.eql([ 'b1', 'b2', 'b3' ]); - - mm.datas(foo, 'getMultiValues', 1); - datas = yield foo.getMultiValues('key'); - datas.should.equal(1); - - mm.data(foo, 'getValue', 2); - let data = yield foo.getValue('key'); - data.should.equal(2); - - mm.restore(); - data = yield foo.getValue('key'); - data.should.equal(1); - }); - }); - - describe('error()', function() { - it('should mock error', function* () { - mm.error(foo, 'getValue'); - try { - yield foo.getValue(); - throw new Error('should not run this'); - } catch (err) { - err.message.should.equal('mm mock error'); - } - - mm.error(foo, 'getValue', 'foo error', 200); - try { - yield foo.getValue(); - throw new Error('should not run this'); - } catch (err) { - err.message.should.equal('foo error'); - } - - mm.error(foo, 'getValue', new Error('new foo error')); - try { - yield foo.getValue(); - throw new Error('should not run this'); - } catch (err) { - err.message.should.equal('new foo error'); - } - }); - }); -});