diff --git a/README.md b/README.md index 64a3898..d52116c 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,9 @@ Table of Contents * [Usage](#usage) * [Stdout/Stderr Mocking](#stdoutstderr-mocking) * [Environment Variables](#environment-variables) +* [Nock](#nock) * [Run](#run) +* [Mock](#mock) * [Catch](#catch) * [Chai](#chai) * [Chaining](#chaining) @@ -79,6 +81,29 @@ describe('stdmock tests', () => { }) ``` +Nock +---- + +(not to be confused with [mock](#mock)) + +Uses [nock](https://github.com/node-nock/nock) to mock out HTTP calls to external APIs. +Automatically calls `done()` to ensure the calls were made and `cleanAll()` to remove any pending requests. + +```js +describe('nock tests', () => { + fancy() + .nock('https://api.github.com', nock => { + nock + .get('/me') + .reply(200, {name: 'jdxcode'}) + }) + .it('mocks http call to github', async () => { + const {body: user} = await HTTP.get('https://api.github.com/me') + expect(user).to.have.property('name', 'jdxcode') + }) +}) +``` + Environment Variables --------------------- @@ -127,6 +152,31 @@ describe('run', () => { }) ``` +Mock +---- + +(not to be confused with [nock](#nock)) + +Mock any object. Like all fancy plugins, it ensures that it is reset to normal after the test runs. +```js +import * as os from 'os' + +describe('mock tests', () => { + fancy() + .mock(os, 'platform', () => 'foobar') + .it('sets os', () => { + expect(os.platform()).to.equal('foobar') + }) + + fancy() + .mock(os, 'platform', sinon.stub().returns('foobar')) + .it('uses sinon', () => { + expect(os.platform()).to.equal('foobar') + expect(os.platform.called).to.equal(true) + }) +}) +``` + Catch ----- diff --git a/package.json b/package.json index dd08eb6..a47e910 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,13 @@ "@types/mocha": "^2.2.47", "@types/nock": "^9.1.2", "@types/node": "^9.3.0", + "@types/sinon": "^4.1.3", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chalk": "^2.3.0", "eslint": "^4.16.0", "eslint-config-dxcli": "^1.1.4", + "http-call": "^5.0.2", "husky": "^0.14.3", "mocha": "^5.0.0", "mocha-junit-reporter": "^1.16.0", @@ -31,6 +33,7 @@ "nps": "^5.7.1", "nps-utils": "^1.5.0", "nyc": "^11.4.1", + "sinon": "^4.2.1", "ts-node": "^4.1.0", "typescript": "^2.6.2" }, diff --git a/src/base.ts b/src/base.ts index 267fd58..4f115d6 100644 --- a/src/base.ts +++ b/src/base.ts @@ -41,7 +41,7 @@ const fancy = (plugins: any, chain: Chain = ctx = assignWithProps({}, ctx, extra) const [next, args] = chain.shift() || [null, null] if (next) return next(run, ctx, ...args as any[]) - if (callback) callback.call(this, ctx) + if (callback) return callback.call(this, ctx) } await run() }) diff --git a/src/index.ts b/src/index.ts index d6fb9c3..6778839 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,14 +2,16 @@ import base, {Base, Fancy, Next, Plugin} from './base' import _catch, {CatchOptions} from './catch' import env, {EnvOptions} from './env' import mock from './mock' +import nock, {NockHosts} from './nock' import {stderr, StdmockOptions, stdout} from './stdmock' export const fancy = base -.register('stdout', stdout) -.register('stderr', stderr) -.register('mock', mock) -.register('env', env) .register('catch', _catch) +.register('env', env) +.register('mock', mock) +.register('nock', nock) +.register('stderr', stderr) +.register('stdout', stdout) export { Base, @@ -18,6 +20,7 @@ export { Next, CatchOptions, EnvOptions, + NockHosts, StdmockOptions, } export default fancy diff --git a/src/nock.ts b/src/nock.ts new file mode 100644 index 0000000..473d573 --- /dev/null +++ b/src/nock.ts @@ -0,0 +1,16 @@ +import * as Nock from 'nock' + +import {Next} from './base' + +export interface NockHosts {[host: string]: Nock.Scope} + +export type NockCallback = (nock: Nock.Scope) => any + +export default async (next: Next<{nock: NockHosts}>, ctx: any, host: string, cb: NockCallback) => { + const hosts: NockHosts = ctx.nock || {} + const nock: typeof Nock = require('nock') + const api = hosts[host] || nock(host) + await cb(api) + await next({nock: hosts}) + api.done() +} diff --git a/test/mock.test.ts b/test/mock.test.ts index af2ecba..43e4d31 100644 --- a/test/mock.test.ts +++ b/test/mock.test.ts @@ -1,19 +1,27 @@ // tslint:disable no-console -import * as os from 'os' +import * as sinon from 'sinon' import {expect, fancy} from '../src' +const os = require('os') const platform = os.platform() describe('mock', () => { + // from readme fancy() .mock(os, 'platform', () => 'foobar') - .stdout() - .it('sets os', output => { - console.log(os.platform()) - expect(output.stdout).to.equal('foobar\n') + .it('sets os', () => { + expect(os.platform()).to.equal('foobar') + }) + + fancy() + .mock(os, 'platform', sinon.stub().returns('foobar')) + .it('uses sinon', () => { + expect(os.platform()).to.equal('foobar') + expect(os.platform.called).to.equal(true) }) + // from readme fancy() .stdout() diff --git a/test/nock.test.ts b/test/nock.test.ts new file mode 100644 index 0000000..a30bb17 --- /dev/null +++ b/test/nock.test.ts @@ -0,0 +1,27 @@ +import HTTP from 'http-call' + +import {expect, fancy} from '../src' + +describe('nock', () => { + // from readme + fancy() + .nock('https://api.github.com', nock => { + nock + .get('/me') + .reply(200, {name: 'jdxcode'}) + }) + .it('mocks http call to github', async () => { + const {body: user} = await HTTP.get('https://api.github.com/me') + expect(user).to.have.property('name', 'jdxcode') + }) + // from readme + + fancy() + .catch('Mocks not yet satisfied:\nGET https://api.github.com:443/me') + .nock('https://api.github.com', nock => { + nock + .get('/me') + .reply(200, {name: 'jdxcode'}) + }) + .it('calls .done()') +}) diff --git a/yarn.lock b/yarn.lock index 1503214..ac41c59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -275,6 +275,10 @@ version "9.3.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.3.0.tgz#3a129cda7c4e5df2409702626892cb4b96546dd5" +"@types/sinon@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.1.3.tgz#2ee25e0e302f31e78a945650a60029e08878eaf8" + "@types/strip-bom@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" @@ -985,6 +989,10 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" +content-type@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + conventional-changelog-angular@^1.3.3, conventional-changelog-angular@^1.4.0: version "1.6.0" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-1.6.0.tgz#0a26a071f2c9fcfcf2b86ba0cfbf6e6301b75bfa" @@ -1757,6 +1765,12 @@ form-data@~2.3.1: combined-stream "^1.0.5" mime-types "^2.1.12" +formatio@1.2.0, formatio@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb" + dependencies: + samsam "1.x" + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -2094,6 +2108,17 @@ hosted-git-info@^2.1.4, hosted-git-info@^2.4.2: version "2.5.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" +http-call@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/http-call/-/http-call-5.0.2.tgz#21bec3655f1631de128c0cdaa470777b1fbbc365" + dependencies: + content-type "^1.0.4" + debug "^3.1.0" + is-retry-allowed "^1.1.0" + is-stream "^1.1.0" + tslib "^1.8.1" + tunnel-agent "^0.6.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -2397,6 +2422,10 @@ is-resolvable@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.1.tgz#acca1cd36dbe44b974b924321555a70ba03b1cf4" +is-retry-allowed@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + is-ssh@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.0.tgz#ebea1169a2614da392a63740366c3ce049d8dff6" @@ -2574,6 +2603,10 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +just-extend@^1.1.26: + version "1.1.27" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-1.1.27.tgz#ec6e79410ff914e472652abfa0e603c03d60e905" + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -2743,6 +2776,14 @@ lodash@^4.0.0, lodash@^4.17.4, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, loda version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" +lolex@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6" + +lolex@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.1.tgz#3d2319894471ea0950ef64692ead2a5318cff362" + longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" @@ -3015,6 +3056,16 @@ nested-error-stacks@^1.0.0, nested-error-stacks@^1.0.1: dependencies: inherits "~2.0.1" +nise@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.2.0.tgz#079d6cadbbcb12ba30e38f1c999f36ad4d6baa53" + dependencies: + formatio "^1.2.0" + just-extend "^1.1.26" + lolex "^1.6.0" + path-to-regexp "^1.7.0" + text-encoding "^0.6.4" + nock@^9.1.6: version "9.1.6" resolved "https://registry.yarnpkg.com/nock/-/nock-9.1.6.tgz#16395af4c45b0fd84d1a4a9668154e16fa6624db" @@ -3398,6 +3449,12 @@ path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" +path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -3825,6 +3882,10 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +samsam@1.x: + version "1.3.0" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" + semantic-release@^12.2.2: version "12.2.2" resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-12.2.2.tgz#6f114c2e2cb55040b78f985e11b0aa7d8d2ee111" @@ -3912,6 +3973,18 @@ signal-exit@^3.0.0, signal-exit@^3.0.1, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" +sinon@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.2.1.tgz#3664e90ea4bccec6ebde3c06e3da8179983371d9" + dependencies: + diff "^3.1.0" + formatio "1.2.0" + lodash.get "^4.4.2" + lolex "^2.2.0" + nise "^1.2.0" + supports-color "^5.1.0" + type-detect "^4.0.5" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -4232,6 +4305,12 @@ supports-color@^4.0.0: dependencies: has-flag "^2.0.0" +supports-color@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5" + dependencies: + has-flag "^2.0.0" + table@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" @@ -4264,6 +4343,10 @@ test-exclude@^4.1.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" +text-encoding@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" + text-extensions@^1.0.0: version "1.7.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.7.0.tgz#faaaba2625ed746d568a23e4d0aacd9bf08a8b39" @@ -4454,7 +4537,7 @@ type-detect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" -type-detect@^4.0.0, type-detect@^4.0.3: +type-detect@^4.0.0, type-detect@^4.0.3, type-detect@^4.0.5: version "4.0.7" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.7.tgz#862bd2cf6058ad92799ff5a5b8cf7b6cec726198"