-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refacor: Use new lib functions to clean up main
- removes lab and sinon devDependencies - rework test to focus on main flow - update scripts to use tap and require 100% coverage
- Loading branch information
1 parent
2a06623
commit 22bc183
Showing
3 changed files
with
73 additions
and
215 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,38 @@ | ||
const Assert = require('assert') | ||
const Child = require('child_process') | ||
const FS = require('fs') | ||
const Parallel = require('run-parallel') | ||
const Path = require('path') | ||
const Parallel = require('run-parallel') | ||
const { command, project } = require('./lib') | ||
|
||
// get details about package and environment | ||
module.exports = (options, done) => { | ||
if (typeof options === 'function') { | ||
done = options | ||
options = {} | ||
module.exports = (commands, done) => { | ||
if (typeof commands === 'function') { | ||
done = commands | ||
commands = {} | ||
} | ||
|
||
Assert.equal(typeof options, 'object', 'Options must be an object') | ||
Assert.equal(typeof done, 'function', 'Must pass in a callback function') | ||
Assert(isObject(commands), '`commands` must be an object') | ||
Assert(isFunction(done), 'Must pass in a callback function') | ||
|
||
Parallel([ | ||
packageDetails, | ||
(next) => process.nextTick(next, null, { env: process.env.NODE_ENV }), | ||
(next) => { | ||
const cmds = Object.assign({ node: 'node -v', npm: 'npm -v' }, options) | ||
|
||
commandDetails(cmds, next) | ||
} | ||
(next) => project(Path.dirname(require.main.filename), next), | ||
(next) => commandDetails(commands, next) | ||
], (err, results) => { | ||
if (err) done(err) | ||
else done(null, Object.assign({}, ...results)) | ||
done(err, Object.assign({}, ...results)) | ||
}) | ||
} | ||
|
||
// get name and version from package | ||
const packageDetails = (done) => { | ||
const packagePath = Path.join(process.cwd(), 'package.json') | ||
|
||
FS.readFile(packagePath, 'utf8', (err, data) => { | ||
if (err) return done(err) | ||
|
||
const { name, version } = JSON.parse(data) | ||
const isObject = (value) => typeof value === 'object' && !Array.isArray(value) | ||
|
||
done(null, { name, version }) | ||
}) | ||
} | ||
const isFunction = (value) => typeof value === 'function' | ||
|
||
// execute all passed commands and yield results | ||
const commandDetails = (cmds, done) => { | ||
const commandDetails = (commands, done) => { | ||
const cmds = Object.assign({ node: 'node -v', npm: 'npm -v' }, commands) | ||
const tasks = Object.keys(cmds) | ||
.reduce((acc, key) => Object.assign(acc, { [key]: proc(cmds[key]) }), {}) | ||
.reduce((acc, key) => Object.assign(acc, { | ||
[key]: (next) => command(cmds[key], next) | ||
}), {}) | ||
|
||
Parallel(tasks, done) | ||
} | ||
|
||
// curry command, return function that yields error string or result | ||
const proc = (cmd) => (done) => { | ||
Child.exec(cmd, (err, stdout) => { | ||
if (err) done(null, err.toString()) | ||
else done(null, stdout.replace(/\n/g, '')) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,200 +1,80 @@ | ||
const Child = require('child_process') | ||
const Fs = require('fs') | ||
const Lab = require('lab') | ||
const Path = require('path') | ||
const Sinon = require('sinon') | ||
const PkgFixture = require('./fixtures/package.json') | ||
const TD = require('testdouble') | ||
const { command, project } = TD.replace('../lib') | ||
const { expect } = require('code') | ||
|
||
const lab = exports.lab = Lab.script() | ||
const { describe, after, afterEach, before, beforeEach, it } = lab | ||
const { test: describe } = require('tap') | ||
|
||
const KnockKnock = require('..') | ||
|
||
describe('knock-knock', () => { | ||
const packagePath = Path.join(process.cwd(), 'package.json') | ||
const temp = process.env.NODE_ENV | ||
describe('knock-knock', ({ afterEach, beforeEach, plan, test }) => { | ||
const pkgData = { name: PkgFixture.name, version: PkgFixture.version } | ||
const nodeVersion = 'v1.2.3' | ||
const npmVersion = 'v4.5.6' | ||
|
||
plan(4) | ||
|
||
let nodeVersion = process.version | ||
let npmVersion | ||
let out = Object.prototype | ||
beforeEach((done) => { | ||
TD.when(command('node -v')).thenCallback(null, nodeVersion) | ||
TD.when(command('npm -v')).thenCallback(null, npmVersion) | ||
TD.when(project(__dirname)).thenCallback(null, pkgData) | ||
|
||
before((done) => { | ||
process.env.NODE_ENV = 'testenv' | ||
done() | ||
}) | ||
|
||
after((done) => { | ||
process.env.NODE_ENV = temp | ||
afterEach((done) => { | ||
TD.reset() | ||
|
||
done() | ||
}) | ||
|
||
describe('mocking methods', () => { | ||
beforeEach((done) => { | ||
out = { | ||
name: 'test', | ||
version: '0.1.0', | ||
env: process.env.NODE_ENV, | ||
node: nodeVersion, | ||
npm: '2.14.7' | ||
} | ||
out = JSON.stringify(out) | ||
|
||
Sinon.stub(Child, 'exec').yields(new Error('exec')) | ||
Sinon.stub(Fs, 'readFile').yields(new Error('readFile')) | ||
done() | ||
}) | ||
test('yeilds an object', (t) => { | ||
const expected = { | ||
name: PkgFixture.name, | ||
version: PkgFixture.version, | ||
node: nodeVersion, | ||
npm: npmVersion, | ||
env: process.env.NODE_ENV | ||
} | ||
|
||
afterEach((done) => { | ||
Child.exec.restore() | ||
Fs.readFile.restore() | ||
KnockKnock((err, result) => { | ||
expect(err).to.not.exist() | ||
expect(result).to.equal(expected) | ||
|
||
done() | ||
t.end() | ||
}) | ||
}) | ||
|
||
describe('when called', () => { | ||
beforeEach((done) => { | ||
Child.exec.onFirstCall().yields(null, nodeVersion) | ||
.onSecondCall().yields(null, '2.14.7') | ||
Fs.readFile.yields(null, out) | ||
done() | ||
}) | ||
|
||
it('yields an object', (done) => { | ||
KnockKnock((err, results) => { | ||
expect(err).to.be.null() | ||
expect(Fs.readFile.calledWith(packagePath)).to.be.true() | ||
expect(results).to.be.an.object() | ||
done() | ||
}) | ||
}) | ||
|
||
describe('the object', () => { | ||
it('has a key called name', (done) => { | ||
KnockKnock((err, results) => { | ||
expect(err).to.be.null() | ||
expect(results.name).to.equal('test') | ||
done() | ||
}) | ||
}) | ||
|
||
it('has a key called version', (done) => { | ||
KnockKnock((err, results) => { | ||
expect(err).to.be.null() | ||
expect(results.version).to.equal('0.1.0') | ||
done() | ||
}) | ||
}) | ||
|
||
it('has a key called env', (done) => { | ||
KnockKnock((err, results) => { | ||
expect(err).to.be.null() | ||
expect(results.env).to.equal('testenv') | ||
done() | ||
}) | ||
}) | ||
|
||
it('has a key called node', (done) => { | ||
KnockKnock((err, results) => { | ||
expect(err).to.be.null() | ||
expect(results.node).to.equal(nodeVersion) | ||
done() | ||
}) | ||
}) | ||
|
||
it('has a key called npm', (done) => { | ||
KnockKnock((err, results) => { | ||
expect(err).to.be.null() | ||
expect(results.npm).to.equal('2.14.7') | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
test('accepts commands map', (t) => { | ||
const commands = { 'test': 'someBin' } | ||
const output = 'some output' | ||
|
||
describe('when options are passed in', () => { | ||
beforeEach((done) => { | ||
Child.exec.onFirstCall().yields(null, nodeVersion) | ||
.onSecondCall().yields(null, '2.14.7') | ||
.onThirdCall().yields(null, 'value') | ||
Fs.readFile.yields(null, out) | ||
done() | ||
}) | ||
|
||
it('executes all commands', (done) => { | ||
KnockKnock({ key: 'value -v' }, (err, results) => { | ||
expect(err).to.be.null() | ||
expect(results.key).to.equal('value') | ||
done() | ||
}) | ||
}) | ||
}) | ||
TD.when(command(commands.test)).thenCallback(null, output) | ||
|
||
describe('when package.json is not found', () => { | ||
it('yields an error', (done) => { | ||
KnockKnock((err, results) => { | ||
expect(err).to.be.instanceof(Error) | ||
expect(err.message).to.equal('readFile') | ||
expect(results).to.be.undefined() | ||
done() | ||
}) | ||
}) | ||
}) | ||
KnockKnock(commands, (err, result) => { | ||
expect(err).to.not.exist() | ||
expect(result.test).to.equal(output) | ||
|
||
describe('when executing a command fails', () => { | ||
beforeEach((done) => { | ||
Child.exec.onFirstCall().yields(new Error('exec error').toString()) | ||
.onSecondCall().yields(new Error('exec error').toString()) | ||
Fs.readFile.yields(null, out) | ||
done() | ||
}) | ||
|
||
it('stringifies the err', (done) => { | ||
KnockKnock((err, results) => { | ||
expect(err).to.be.null() | ||
expect(results.node).to.equal('Error: exec error') | ||
expect(results.npm).to.equal('Error: exec error') | ||
done() | ||
}) | ||
}) | ||
t.end() | ||
}) | ||
}) | ||
|
||
describe('when options is not an object', () => { | ||
it('throws an error', (done) => { | ||
expect(() => { | ||
return KnockKnock('string', () => { | ||
test('throws when not passed a callback', (t) => { | ||
const fns = [ | ||
KnockKnock, | ||
() => KnockKnock({}) | ||
] | ||
|
||
}) | ||
}).to.throw() | ||
done() | ||
}) | ||
}) | ||
fns.forEach((fn) => expect(fn).to.throw()) | ||
|
||
describe('when no callback passed', () => { | ||
it('throws an error', (done) => { | ||
expect(() => { | ||
return KnockKnock({}) | ||
}).to.throw() | ||
done() | ||
}) | ||
}) | ||
t.end() | ||
}) | ||
|
||
describe('handling newline characters', () => { | ||
before((done) => { | ||
// eslint-disable-next-line handle-callback-err | ||
Child.exec('npm -v', (err, stdout) => { | ||
npmVersion = stdout.replace(/\n/g, '') | ||
done() | ||
}) | ||
}) | ||
test('throws when commands is not an object', (t) => { | ||
const invalid = ['', 1, [], false] | ||
const fns = invalid.map((param) => () => KnockKnock(param, () => {})) | ||
|
||
it('removes newline characters', (done) => { | ||
KnockKnock((err, results) => { | ||
expect(err).to.be.null() | ||
expect(results.node).to.equal(nodeVersion) | ||
expect(results.npm).to.equal(npmVersion) | ||
done() | ||
}) | ||
}) | ||
fns.forEach((fn) => expect(fn).to.throw()) | ||
|
||
t.end() | ||
}) | ||
}) |