diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml new file mode 100644 index 0000000..0f8d781 --- /dev/null +++ b/.github/workflows/nodejs.yml @@ -0,0 +1,46 @@ +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +name: Node.js CI + +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + schedule: + - cron: '0 2 * * *' + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + node-version: [14, 16, 18] + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - name: Checkout Git Source + uses: actions/checkout@v2 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Dependencies + run: npm i + + - name: Continuous Integration + run: npm run ci + + - name: Code Coverage + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5a2d376..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -sudo: false -language: node_js -node_js: - - '8' - - '10' - - '12' - - '13' -before_install: - - npm i npminstall -g -install: - - npminstall -script: - - npm run ci -after_script: - - npminstall codecov && codecov diff --git a/azure-pipelines.template.yml b/azure-pipelines.template.yml deleted file mode 100644 index 7166fa4..0000000 --- a/azure-pipelines.template.yml +++ /dev/null @@ -1,51 +0,0 @@ -# Node.js -# Build a general Node.js application with npm. -# Add steps that analyze code, save build artifacts, deploy, and more: -# https://docs.microsoft.com/vsts/pipelines/languages/javascript -# demo: https://github.com/parcel-bundler/parcel/blob/master/azure-pipelines-template.yml - -jobs: -- job: ${{ parameters.name }} - pool: - vmImage: ${{ parameters.vmImage }} - strategy: - matrix: - node_8: - node_version: 8 - node_10: - node_version: 10 - node_12: - node_version: 12 - node_13: - node_version: 13 - maxParallel: 4 - steps: - - task: NodeTool@0 - inputs: - versionSpec: $(node_version) - displayName: 'Install Node.js' - - # Set ENV - - ${{ if ne(parameters.name, 'windows') }}: - - script: | - echo $PWD - export PATH="$PATH:$PWD/node_modules/.bin" - echo "##vso[task.setvariable variable=PATH]$PATH" - displayName: Set ENV - - ${{ if eq(parameters.name, 'windows') }}: - - script: | - echo %cd% - set PATH=%PATH%;%cd%\node_modules\.bin - echo "##vso[task.setvariable variable=PATH]%PATH%" - displayName: Set ENV - - - script: | - npm i npminstall && npminstall - displayName: 'Install Packages' - - script: | - npm run ci - displayName: 'Build & Unit Test' - - ${{ if ne(parameters.name, 'windows') }}: - - script: | - npminstall codecov && codecov - displayName: 'Report Coverage' diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 554935f..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,16 +0,0 @@ -jobs: -- template: azure-pipelines.template.yml - parameters: - name: linux - vmImage: 'ubuntu-16.04' - -- template: azure-pipelines.template.yml - parameters: - name: windows - vmImage: 'vs2017-win2016' - -- template: azure-pipelines.template.yml - parameters: - name: macos - vmImage: 'xcode9-macos10.13' - diff --git a/lib/mm.js b/lib/mm.js index 70cad54..59346b5 100644 --- a/lib/mm.js +++ b/lib/mm.js @@ -19,45 +19,24 @@ function spyFunction(target, property, fn) { if (!is.function(fn)) return fn; // support mock with jest.fn() if (fn._isMockFunction && fn.mock) return fn; - if (is.asyncFunction(fn)) { - const spy = async function(...args) { - spy.called = spy.called || 0; - spy.calledArguments = spy.calledArguments || []; - spy.calledArguments.push(args); - spy.lastCalledArguments = args; - spy.called++; - return await fn.call(this, ...args); - }; - return spy; - } - - if (is.generatorFunction(fn)) { - const spy = function* (...args) { - spy.called = spy.called || 0; - spy.calledArguments = spy.calledArguments || []; - spy.calledArguments.push(args); - spy.lastCalledArguments = args; - spy.called++; - return yield fn.call(this, ...args); - }; - return spy; - } // don't allow mock async function to common function - const isAsyncLike = isAsyncLikeFunction(target, property); - const spy = function(...args) { - spy.called = spy.called || 0; - spy.calledArguments = spy.calledArguments || []; - spy.calledArguments.push(args); - spy.lastCalledArguments = args; - spy.called++; - const res = fn.call(this, ...args); - if (isAsyncLike && !is.promise(res)) { - throw new Error(`Can\'t mock async function to normal function for property "${property}"`); - } - return res; - }; - return spy; + const isGenerator = is.generatorFunction(fn); + const isAsyncLike = !isGenerator && isAsyncLikeFunction(target, property); + return new Proxy(fn, { + apply(target, thisArg, args) { + fn.called = fn.called || 0; + fn.calledArguments = fn.calledArguments || []; + fn.calledArguments.push(args); + fn.lastCalledArguments = args; + fn.called++; + const res = Reflect.apply(target, thisArg, args); + if (isAsyncLike && !is.promise(res)) { + throw new Error(`Can\'t mock async function to normal function for property "${property}"`); + } + return res; + }, + }); } function isAsyncLikeFunction(target, property) { diff --git a/package.json b/package.json index 3b76f9f..64c2208 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "chunkstream": "^0.0.1", "co": "^4.6.0", "egg-bin": "^1.11.1", - "egg-ci": "^1.13.1", + "egg-ci": "^2.2.0", "enable": "^3.4.0", "eslint": "^6.8.0", "eslint-config-egg": "^8.0.1", @@ -48,11 +48,10 @@ "test" ], "engines": { - "node": ">=8.0.0" + "node": ">=14.0.0" }, "ci": { - "type": "travis, azure-pipelines", - "version": "8, 10, 12, 13" + "version": "14, 16, 18" }, "author": "fengmk2 (https://fengmk2.com)", "license": "MIT" diff --git a/test/mm.test.js b/test/mm.test.js index e283a63..3a28fad 100644 --- a/test/mm.test.js +++ b/test/mm.test.js @@ -860,6 +860,28 @@ describe('test/mm.test.js', () => { }); }); }); + + it('shoud mock function with property', () => { + const NativeDate = Date; + const mockNow = function(date) { + const NewDate = function(...args) { + if (args.length === 0) { + return new NativeDate(date); + } + return new NativeDate(...args); + }; + NewDate.now = function() { + return new NativeDate(date).getTime(); + }; + NewDate.UTC = NativeDate.UTC; + NewDate.parse = NativeDate.parse; + NewDate.prototype = NativeDate.prototype; + return NewDate; + }; + mm(global, 'Date', mockNow('2022-11-20T05:22:04.569Z')); + assert(Date.now() === 1668921724569); + assert(new Date().getTime() === 1668921724569); + }); }); describe('mm(process.env, "HOME")', function() {