Skip to content

Commit

Permalink
feat: added mock and improved std mocking
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Jan 15, 2018
1 parent 9c9c4a8 commit d0c1e97
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 339 deletions.
10 changes: 5 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ jobs:
- checkout
- restore_cache: &restore_cache
keys:
- v0-yarn-{{ .Environment.CIRCLE_JOB }}-{{checksum ".circleci/config.yml"}}-{{ .Branch }}-{{checksum "yarn.lock"}}
- v0-yarn-{{ .Environment.CIRCLE_JOB }}-{{checksum ".circleci/config.yml"}}-{{ .Branch }}-
- v0-yarn-{{ .Environment.CIRCLE_JOB }}-{{checksum ".circleci/config.yml"}}-master
- v0-yarn-{{ .Environment.CIRCLE_JOB }}-{{checksum "scripts/circleci"}}-{{checksum ".circleci/config.yml"}}-{{ .Branch }}-{{checksum "yarn.lock"}}
- v0-yarn-{{ .Environment.CIRCLE_JOB }}-{{checksum "scripts/circleci"}}-{{checksum ".circleci/config.yml"}}-{{ .Branch }}-
- v0-yarn-{{ .Environment.CIRCLE_JOB }}-{{checksum "scripts/circleci"}}-{{checksum ".circleci/config.yml"}}-master-
- run: ./scripts/circleci
- store_test_results:
path: ~/cli/reports
- save_cache: &save_cache
key: v0-yarn-{{ .Environment.CIRCLE_JOB }}-{{checksum ".circleci/config.yml"}}-{{ .Branch }}-{{checksum "yarn.lock"}}
key: v0-yarn-{{ .Environment.CIRCLE_JOB }}-{{checksum "scripts/circleci"}}-{{checksum ".circleci/config.yml"}}-{{ .Branch }}-{{checksum "yarn.lock"}}
paths:
- ~/cli/node_modules
- /usr/local/share/.cache/yarn
Expand All @@ -36,7 +36,7 @@ jobs:

workflows:
version: 2
test:
"@dxcli/dev-test":
jobs:
- node-latest
- node-8
Expand Down
7 changes: 3 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
*-debug.log
*-error.log
/coverage
/lib
/node_modules
/tmp
/lib
/.nyc_output

*-error.log
*-debug.log
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
@dxcli/dev-test
===============

test helpers for dxcli components

[![Version](https://img.shields.io/npm/v/@dxcli/dev-test.svg)](https://npmjs.org/package/@dxcli/dev-test)
[![CircleCI](https://circleci.com/gh/dxcli/dev-test/tree/master.svg?style=svg)](https://circleci.com/gh/dxcli/dev-test/tree/master)
[![Appveyor CI](https://ci.appveyor.com/api/projects/status/github/dxcli/dev-test?branch=master&svg=true)](https://ci.appveyor.com/project/heroku/dev-test/branch/master)
Expand Down
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
"ts-node": "^4.1.0"
},
"devDependencies": {
"@dxcli/dev": "^1.0.0",
"@dxcli/dev": "^1.1.2",
"@dxcli/dev-semantic-release": "^0.0.3",
"@dxcli/dev-tslint": "^0.0.6",
"@dxcli/dev-test": "^0.0.1",
"@dxcli/dev-tslint": "^0.0.8",
"@types/node": "^9.3.0",
"del-cli": "^1.1.0",
"eslint": "^4.15.0",
"eslint-config-dxcli": "^1.1.2",
"eslint-config-dxcli": "^1.1.4",
"husky": "^0.14.3",
"mocha": "^4.1.0",
"nyc": "^11.4.1",
Expand All @@ -34,16 +34,16 @@
"workflows": {
"test": [
"eslint .",
"tsc",
"tslint -p .",
"commitlint --from master",
"tsc -p test --noEmit",
"tslint -p test",
"commitlint --from origin/master",
"mocha \"test/**/*.ts\""
],
"lint": [
"eslint .",
"tsc",
"tslint -p .",
"commitlint --from master"
"tsc -p test --noEmit",
"tslint -p test",
"commitlint --from origin/master"
]
}
},
Expand All @@ -60,7 +60,7 @@
"commitmsg": "dxcli-dev-commitmsg",
"lint": "dxcli-dev lint",
"precommit": "dxcli-dev test",
"prepare": "del-cli lib && tsc",
"prepare": "rm -rf lib && tsc",
"test": "dxcli-dev test"
},
"types": "lib/index.d.ts"
Expand Down
9 changes: 5 additions & 4 deletions scripts/circleci
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ if [[ "$CLI_ENGINE_GREENKEEPER_BRANCH" == 1 ]]; then
greenkeeper-lockfile-upload
fi

NYC="npx nyc --nycrc-path node_modules/@dxcli/dev-nyc-config/.nycrc"
CWD=$(pwd)
NYC="./node_modules/.bin/nyc --nycrc-path node_modules/@dxcli/dev-nyc-config/.nycrc"
mkdir -p reports
MOCHA_FILE="reports/mocha.xml" \
MOCHA_FILE="$CWD/reports/mocha.xml" \
DXCLI_MOCHA_OPTS="--reporter mocha-junit-reporter" \
DXCLI_ESLINT_OPTS="--format junit --output-file reports/eslint.xml" \
DXCLI_TSLINT_OPTS="--format junit > reports/tslint.xml" \
DXCLI_ESLINT_OPTS="--format junit --output-file $CWD/reports/eslint.xml" \
DXCLI_TSLINT_OPTS="--format junit > $CWD/reports/tslint.xml" \
$NYC yarn test
$NYC report --reporter=text-lcov > coverage.lcov

Expand Down
143 changes: 76 additions & 67 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,85 +36,94 @@ interface Options {
only?: boolean
skip?: boolean
print?: boolean
env?: {[k: string]: string}
mock?: [any, string, any][]
}

export interface Describe {
(description: string, cb: (this: mocha.ISuiteCallbackContext) => void): mocha.ISuite
stdout: Describe
stderr: Describe
only: Describe
skip: Describe
print: Describe
env(env?: {[k: string]: string}): Describe
export interface Settings<T, U> {
(description: string, cb: (this: T) => void): U
stdout: Settings<T, U>
stderr: Settings<T, U>
only: Settings<T, U>
skip: Settings<T, U>
print: Settings<T, U>
mock(object: any, path: string, value: any): Settings<T, U>
env(env?: {[k: string]: string}): Settings<T, U>
}

export type Describe = Settings<mocha.ISuiteCallbackContext, mocha.ISuite>
export type It = Settings<mocha.ITestCallbackContext, mocha.ITest>

const env = process.env

const __describe = (options: Options = {}): Describe => {
_.defaults(options, {
stdout: false,
stderr: false,
print: false,
only: false,
skip: false,
function hooks(options: Options) {
options.mock!.forEach(([object, path, value]) => {
const desc = ['mock', path].join(':')
const orig = _.get(object, path)
beforeEach(desc, () => _.set(object, path, value))
afterEach(desc, () => _.set(object, path, orig))
})

// always reset process.env no matter what
afterEach('resetEnv', () => process.env = env)
// always reset stdMocks
afterEach('std-mocks', () => stdMocks.restore())

if (options.stdout || options.stderr) {
const desc = _([options.stdout && 'stdout', options.stderr && 'stderr']).compact().join(':')
beforeEach(desc, () => stdMocks.use(options))
}
}

const settings = (builder: any, opts: Options) => {
const prop = (name: string, value: any): PropertyDescriptor => {
const prop: PropertyDescriptor = {enumerable: true}
if (typeof value === 'function') {
prop.value = (...args: any[]) => builder({...opts, [name]: value(...args)})
} else {
prop.get = () => builder({...opts, [name]: value})
}
return prop
}
const mock = opts.mock = opts.mock || []

return {
print: prop('print', true),
stdout: prop('stdout', true),
stderr: prop('stderr', true),
only: prop('only', true),
skip: prop('skip', true),
env: prop('mock', (env: {[k: string]: string} = {}) => mock.concat(Object.entries(env).map(([k, v]) => [process.env, k, v] as [any, string, any]))),
mock: prop('mock', (object: any, path: string, value: any) => mock.concat([[object, path, value]])),
}
}

const __describe = (options: Options = {}): Describe => {
return Object.defineProperties((description: string, cb: (this: mocha.ISuiteCallbackContext) => void) => {
let thisSuite: any = describe
if (options.only) thisSuite = describe.only
if (options.skip) thisSuite = describe.skip

return thisSuite(description, function (this: mocha.ISuiteCallbackContext) {
if (options.env) {
beforeEach(() => {
process.env = {}
})
}

// always reset process.env no matter what
afterEach(() => {
process.env = env
return ((options.only && describe.only) || (options.skip && describe.skip) || describe)(
description,
function (this: mocha.ISuiteCallbackContext) {
hooks(options)
cb.call(this)
})
}, settings(__describe, options))
}

if (options.stdout || options.stderr) {
beforeEach(() => {
stdMocks.use(options)
})
afterEach(() => {
stdMocks.restore()
})
}
cb.call(this)
})
}, {
print: {
enumerable: true,
get: () => __describe({...options, print: true})
},
stdout: {
enumerable: true,
get: () => __describe({...options, stdout: true})
},
stderr: {
enumerable: true,
get: () => __describe({...options, stderr: true})
},
only: {
enumerable: true,
get: () => __describe({...options, only: true})
},
skip: {
enumerable: true,
get: () => __describe({...options, skip: true})
},
env: {
enumerable: true,
value: (env: {[k: string]: string} = {}) => __describe({...options, env})
},
})
const __it = (options: Options = {}): It => {
return Object.defineProperties((expectation: string, cb: (this: mocha.ITestCallbackContext) => void) => {
return ((options.only && it.only) || (options.skip && it.skip) || it)(
expectation,
async function (this: mocha.ITestCallbackContext) {
hooks(options)
await cb.call(this)
stdMocks.restore()
})
}, settings(__it, options))
}

const _describe = __describe()
const _it = __it()

export {_describe as describe}
export {
_describe as describe,
_it as it,
}
35 changes: 34 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,43 @@
// tslint:disable no-console

import {describe, expect, output} from '../src'
import * as OS from 'os'

import {describe, expect, it, output} from '../src'

describe.stdout('stdout', () => {
it('logs', () => {
console.log('foo')
expect(output.stdout).to.equal('foo\n')
})

it('logs twice', () => {
console.log('foo')
expect(output.stdout).to.equal('foo\n')
console.log('bar')
expect(output.stdout).to.equal('bar\n')
})
})

describe.stdout.stderr('stdout + stderr', () => {
it('logs and errors', () => {
console.log('foo')
console.error('bar')
expect(output.stdout).to.equal('foo\n')
expect(output.stderr).to.equal('bar\n')
})
})

const os = ['darwin', 'win32', 'linux']
os.forEach(os => {
describe.mock(OS, 'platform', () => os)(os, () => {
it('sets os', () => {
expect(OS.platform()).to.equal(os)
})
})
})

describe.env({foo: 'bar'})('mock env', () => {
it('gets env', () => {
expect(process.env).to.include({foo: 'bar'})
})
})
Loading

0 comments on commit d0c1e97

Please sign in to comment.