Comparing version 4.2.0 to 5.0.1
5.0.1 / 2018-08-07 | ||
================== | ||
5.0.0 / 2018-08-07 | ||
================== | ||
**others** | ||
* [[`f7309da`](http://github.com/node-modules/coffee/commit/f7309da0eeb1e400f704024daaab456756890820)] - refactor: [BREAKING] make coffee more extendable (#62) (TZ | 天猪 <<atian25@qq.com>>) | ||
* [[`728418b`](http://github.com/node-modules/coffee/commit/728418b91799ed8c4b6865ca78951ef7288829e6)] - chore: migrate to node-modules (#61) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
4.2.0 / 2018-08-01 | ||
@@ -6,3 +16,3 @@ ================== | ||
**features** | ||
* [[`b0e16e0`](http://github.com/popomore/coffee/commit/b0e16e00aa903b53950e28606959719df68539cc)] - feat: support prompt (#60) (TZ | 天猪 <<atian25@qq.com>>) | ||
* [[`b0e16e0`](http://github.com/node-modules/coffee/commit/b0e16e00aa903b53950e28606959719df68539cc)] - feat: support prompt (#60) (TZ | 天猪 <<atian25@qq.com>>) | ||
@@ -9,0 +19,0 @@ 4.1.0 / 2017-06-29 |
'use strict'; | ||
var Coffee = require('./lib/coffee'); | ||
const Coffee = require('./lib/coffee'); | ||
const Rule = require('./lib/rule'); | ||
exports.Coffee = Coffee; | ||
exports.Rule = Rule; | ||
@@ -7,0 +9,0 @@ /** |
'use strict'; | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
@@ -10,9 +9,6 @@ const EventEmitter = require('events'); | ||
const spawn = require('cross-spawn'); | ||
const assertion = require('./assert'); | ||
const show = require('./show'); | ||
const Rule = require('./rule'); | ||
const ErrorRule = require('./rule_error'); | ||
// Only accept these type below for assertion | ||
const acceptType = [ 'stdout', 'stderr', 'code', 'error' ]; | ||
class Coffee extends EventEmitter { | ||
@@ -29,2 +25,11 @@ | ||
this.opt = opt.opt; | ||
// Only accept these type below for assertion | ||
this.RuleMapping = { | ||
stdout: Rule, | ||
stderr: Rule, | ||
code: Rule, | ||
error: ErrorRule, | ||
}; | ||
this.restore(); | ||
@@ -46,36 +51,23 @@ | ||
this.once('close', code => { | ||
this._unpatchHookScripts(); | ||
debug('output code `%s`', show(code)); | ||
this.code = code; | ||
this.complete = true; | ||
try { | ||
assertion(this.waitAssert.stdout, this.stdout, 'match stdout'); | ||
assertion(this.waitAssert.stderr, this.stderr, 'match stderr'); | ||
assertion(this.waitAssert.code, this.code, 'match code'); | ||
this.error && assertion(this.waitAssert.error, this.error.message, 'match error message'); | ||
for (const rule of this._waitAssert) { | ||
rule.validate(); | ||
} | ||
// suc | ||
const result = { | ||
stdout: this.stdout, | ||
stderr: this.stderr, | ||
code: this.code, | ||
error: this.error, | ||
}; | ||
this.emit('complete_success', result); | ||
this.cb && this.cb(undefined, result); | ||
} catch (err) { | ||
return done(err); | ||
this.emit('complete_error', err); | ||
return this.cb && this.cb(err); | ||
} | ||
done(); | ||
}); | ||
const self = this; | ||
function done(err) { | ||
self.complete = true; | ||
if (self.cb) { | ||
self.cb.call(self, err, { | ||
stdout: self.stdout, | ||
stderr: self.stderr, | ||
code: self.code, | ||
error: self.error, | ||
}); | ||
} else { | ||
if (err) { | ||
self.emit('complete_error', err); | ||
} else { | ||
self.emit('complete_success', { | ||
stdout: self.stdout, | ||
stderr: self.stderr, | ||
}); | ||
} | ||
} | ||
} | ||
@@ -97,10 +89,2 @@ if (process.env.COFFEE_DEBUG) { | ||
beforeScript(scriptFile) { | ||
assert(this.method === 'fork', `can't set beforeScript on ${this.method} process`); | ||
assert(path.isAbsolute(this.cmd), `can't set beforeScript, ${this.cmd} must be absolute path`); | ||
this._beforeScriptFile = scriptFile; | ||
return this; | ||
} | ||
debug(level) { | ||
@@ -131,28 +115,50 @@ this._debug_stderr = false; | ||
expect(type, value) { | ||
if (acceptType.indexOf(type) > -1) { | ||
const rule = new Rule(value); | ||
if (this.complete) { | ||
assertion([ rule ], this[type], 'match ' + type); | ||
} else { | ||
this.waitAssert[type].push(rule); | ||
} | ||
} | ||
/** | ||
* Assert type with expected value | ||
* | ||
* @param {String} type - assertion rule type | ||
* @param {String|RegExp|Array} expected - expected value | ||
* @return {Coffee} return self for chain | ||
*/ | ||
expect(type, expected) { | ||
this._addAssertion({ type, expected }); | ||
return this; | ||
} | ||
notExpect(type, value) { | ||
if (acceptType.indexOf(type) > -1) { | ||
const rule = new Rule(value, true); | ||
if (this.complete) { | ||
assertion([ rule ], this[type], 'match ' + type); | ||
} else { | ||
this.waitAssert[type].push(rule); | ||
} | ||
} | ||
/** | ||
* Assert type with not expected value, opposite assertion of `expect`. | ||
* | ||
* @param {String} type - assertion rule type | ||
* @param {String|RegExp|Array} expected - not expected value | ||
* @return {Coffee} return self for chain | ||
*/ | ||
notExpect(type, expected) { | ||
this._addAssertion({ type, expected, isOpposite: true }); | ||
return this; | ||
} | ||
_addAssertion({ type, expected, isOpposite }) { | ||
const RuleClz = this.RuleMapping[type]; | ||
assert(RuleClz, `unknown rule type: ${type}`); | ||
const rule = new RuleClz({ ctx: this, type, expected, isOpposite }); | ||
if (this.complete) { | ||
rule.validate(); | ||
} else { | ||
this._waitAssert.push(rule); | ||
} | ||
} | ||
/** | ||
* allow user to custom rule | ||
* @param {String} type - rule type | ||
* @param {Rule} RuleClz - custom rule class | ||
* @protected | ||
*/ | ||
setRule(type, RuleClz) { | ||
this.RuleMapping[type] = RuleClz; | ||
} | ||
/** | ||
* Write data to stdin of the command | ||
@@ -168,2 +174,9 @@ * @param {String} input - input text | ||
/** | ||
* whether set as prompt mode | ||
* | ||
* mark as `prompt`, all stdin call by `write` will wait for `prompt` event then output | ||
* @param {Boolean} [enable] - default to true | ||
* @return {Coffee} return self for chain | ||
*/ | ||
waitForPrompt(enable) { | ||
@@ -174,2 +187,8 @@ this._isWaitForPrompt = enable !== false; | ||
/** | ||
* get `end` hook | ||
* | ||
* @param {Function} [cb] - callback, recommended to left undefind and use promise | ||
* @return {Promise} - end promise | ||
*/ | ||
end(cb) { | ||
@@ -185,38 +204,26 @@ this.cb = cb; | ||
get _hookScripts() { | ||
if (this._beforeScriptFile) { | ||
return `\n\nrequire(${JSON.stringify(this._beforeScriptFile)}); // !!!!!!!coffee hook script, don't change it!!!!!!!!\n\n`; | ||
} | ||
} | ||
/** | ||
* inject script file for mock purpose | ||
* | ||
* @param {String} scriptFile - script file full path | ||
* @return {Coffee} return self for chain | ||
*/ | ||
beforeScript(scriptFile) { | ||
assert(this.method === 'fork', `can't set beforeScript on ${this.method} process`); | ||
assert(path.isAbsolute(this.cmd), `can't set beforeScript, ${this.cmd} must be absolute path`); | ||
this._beforeScriptFile = scriptFile; | ||
_patchHookScripts() { | ||
const hookScriptsContent = this._hookScripts; | ||
if (!hookScriptsContent) return; | ||
let content = fs.readFileSync(this.cmd, 'utf8'); | ||
if (!content.includes(hookScriptsContent)) { | ||
// skip shebang | ||
if (content.startsWith('#!')) { | ||
content = content.replace('\n', `\n${hookScriptsContent}`); | ||
} else { | ||
content = hookScriptsContent + content; | ||
} | ||
debug('add hook scripts %j into %s', hookScriptsContent, this.cmd); | ||
fs.writeFileSync(this.cmd, content); | ||
} | ||
return this; | ||
} | ||
_unpatchHookScripts() { | ||
const hookScriptsContent = this._hookScripts; | ||
if (!hookScriptsContent) return; | ||
let content = fs.readFileSync(this.cmd, 'utf8'); | ||
if (content.includes(hookScriptsContent)) { | ||
content = content.replace(hookScriptsContent, ''); | ||
debug('remove hook scripts %j from %s', hookScriptsContent, this.cmd); | ||
fs.writeFileSync(this.cmd, content); | ||
_run() { | ||
this._isEndCalled = true; | ||
if (this._beforeScriptFile) { | ||
this.opt = this.opt || {}; | ||
const execArgv = this.opt.execArgv ? this.opt.execArgv : process.execArgv; | ||
execArgv.push('-r', this._beforeScriptFile); | ||
this.opt.execArgv = execArgv; | ||
} | ||
} | ||
_run() { | ||
this._isEndCalled = true; | ||
this._patchHookScripts(); | ||
const cmd = this.proc = run(this.method, this.cmd, this.args, this.opt); | ||
@@ -263,8 +270,3 @@ | ||
// cache expected output | ||
this.waitAssert = { | ||
stderr: [], | ||
stdout: [], | ||
code: [], | ||
error: [], | ||
}; | ||
this._waitAssert = []; | ||
this.complete = false; | ||
@@ -301,4 +303,5 @@ this._isEndCalled = false; | ||
let handler = cp[method]; | ||
/* istanbul ignore next */ | ||
if (process.platform === 'win32' && method === 'spawn') handler = spawn; | ||
return handler(cmd, args, opt); | ||
} |
'use strict'; | ||
function Rule(val, isOpposite) { | ||
if (Array.isArray(val)) { | ||
this.value = val.map(function(item) { | ||
return new Rule(item, isOpposite); | ||
}); | ||
} else { | ||
this.value = val; | ||
const is = require('is-type-of'); | ||
const assert = require('assert'); | ||
const show = require('./show'); | ||
class Rule { | ||
constructor({ ctx, type, expected, isOpposite }) { | ||
this.ctx = ctx; | ||
this.type = type; | ||
this.expected = [].concat(expected); | ||
this.isOpposite = isOpposite === true; | ||
} | ||
this.isOpposite = isOpposite === true; | ||
validate(message) { | ||
const actual = this.ctx[this.type]; | ||
for (const expected of this.expected) { | ||
message = this.formatMessage(actual, expected, message); | ||
this.assert(actual, expected, message); | ||
} | ||
} | ||
assert(actual, expected, message) { | ||
const assertFn = assert[this.isOpposite ? 'notStrictEqual' : 'strictEqual']; | ||
if (is.regexp(expected)) { | ||
return assertFn(expected.test(actual), true, message); | ||
} | ||
return assertFn(actual, expected, message); | ||
} | ||
formatMessage(actual, expected, message) { | ||
message = message || `match ${this.type}`; | ||
return `should ${this.isOpposite ? 'not ' : ''}${message} expected \`${this.inspectObj(expected)}\` but actual \`${this.inspectObj(actual)}\``; | ||
} | ||
inspectObj(obj) { | ||
return show(obj); | ||
} | ||
restore() { | ||
// do nth | ||
} | ||
} | ||
module.exports = Rule; |
{ | ||
"name": "coffee", | ||
"version": "4.2.0", | ||
"description": "Test command line on nodejs", | ||
"version": "5.0.1", | ||
"description": "Test command line on Node.js.", | ||
"main": "index.js", | ||
"dependencies": { | ||
"cross-spawn": "^6.0.5", | ||
"debug": "^3.1.0" | ||
"debug": "^3.1.0", | ||
"is-type-of": "^1.2.0" | ||
}, | ||
"devDependencies": { | ||
"autod": "^3.0.1", | ||
"egg-bin": "^4.8.1", | ||
"egg-ci": "^1.8.0", | ||
"eslint": "^3.19.0", | ||
"eslint-config-egg": "^4.1.0", | ||
"eslint": "^5.3.0", | ||
"eslint-config-egg": "^7.0.0", | ||
"mm": "^2.2.2", | ||
"mocha": "2", | ||
"mocha": "^5.2.0", | ||
"nyc": "^11.0.2", | ||
@@ -22,7 +24,8 @@ "spy": "^1.0.0" | ||
"type": "git", | ||
"url": "git://github.com/popomore/coffee.git" | ||
"url": "git://github.com/node-modules/coffee.git" | ||
}, | ||
"homepage": "https://github.com/popomore/coffee", | ||
"homepage": "https://github.com/node-modules/coffee", | ||
"author": "popomore <sakura9515@gmail.com>", | ||
"keywords": [ | ||
"cli", | ||
"test", | ||
@@ -37,6 +40,6 @@ "shell", | ||
"engines": { | ||
"node": ">= 4.0.0" | ||
"node": ">= 6.0.0" | ||
}, | ||
"ci": { | ||
"version": "4, 6, 8" | ||
"version": "6, 8, 10" | ||
}, | ||
@@ -46,5 +49,6 @@ "scripts": { | ||
"lint": "eslint .", | ||
"test": "mocha -R spec -t 80000 test/*.js", | ||
"cov": "nyc -r json -r lcov -r text-summary npm test", | ||
"ci": "npm run lint && npm run cov" | ||
"test": "npm run lint -- --fix && egg-bin pkgfiles && npm run test-local", | ||
"test-local": "egg-bin test", | ||
"cov": "egg-bin cov", | ||
"ci": "npm run lint && egg-bin pkgfiles --check && npm run cov" | ||
}, | ||
@@ -51,0 +55,0 @@ "files": [ |
@@ -8,4 +8,4 @@ # Coffee | ||
[![NPM version](https://img.shields.io/npm/v/coffee.svg?style=flat)](https://npmjs.org/package/coffee) | ||
[![Build Status](https://img.shields.io/travis/popomore/coffee.svg?style=flat)](https://travis-ci.org/popomore/coffee) | ||
[![codecov.io](https://img.shields.io/codecov/c/github/popomore/coffee.svg?style=flat)](http://codecov.io/github/popomore/coffee?branch=master) | ||
[![Build Status](https://img.shields.io/travis/node-modules/coffee.svg?style=flat)](https://travis-ci.org/node-modules/coffee) | ||
[![codecov.io](https://img.shields.io/codecov/c/github/node-modules/coffee.svg?style=flat)](http://codecov.io/github/node-modules/coffee?branch=master) | ||
[![NPM downloads](http://img.shields.io/npm/dm/coffee.svg?style=flat)](https://npmjs.org/package/coffee) | ||
@@ -15,5 +15,5 @@ | ||
```bash | ||
$ npm i coffee --save-dev | ||
``` | ||
$ npm install coffee -g | ||
``` | ||
@@ -27,9 +27,9 @@ ## Usage | ||
it('should concat input', function(done) { | ||
var coffee = require('coffee'); | ||
const coffee = require('coffee'); | ||
coffee.spawn('cat') | ||
.write('1') | ||
.write('2') | ||
.expect('stdout', '12') | ||
.expect('code', 0) | ||
.end(done); | ||
.write('1') | ||
.write('2') | ||
.expect('stdout', '12') | ||
.expect('code', 0) | ||
.end(done); | ||
}) | ||
@@ -42,7 +42,7 @@ }) | ||
```js | ||
coffee.fork('/path/to/file.js', ['args ']) | ||
.expect('stdout', '12\n') | ||
.expect('stderr', '34\n') | ||
.expect('code', 0) | ||
.end(done); | ||
coffee.fork('/path/to/file.js', [ 'args' ]) | ||
.expect('stdout', '12\n') | ||
.expect('stderr', '34\n') | ||
.expect('code', 0) | ||
.end(done); | ||
``` | ||
@@ -75,3 +75,3 @@ | ||
### Coffee | ||
### coffee.Coffee | ||
@@ -85,7 +85,7 @@ Assertion object | ||
```js | ||
coffee.spawn('echo', ['abcdefg']) | ||
.expect('stdout', 'abcdefg') | ||
.expect('stdout', /^abc/) | ||
.expect('stdout', ['abcdefg', /abc/]) | ||
.end(done); | ||
coffee.spawn('echo', [ 'abcdefg' ]) | ||
.expect('stdout', 'abcdefg') | ||
.expect('stdout', /^abc/) | ||
.expect('stdout', [ 'abcdefg', /abc/ ]) | ||
.end(done); | ||
``` | ||
@@ -108,7 +108,7 @@ | ||
```js | ||
coffee.fork('/path/to/cli', ['abcdefg']) | ||
.waitForPrompt() | ||
.write('tz\n') | ||
.write('2\n'); | ||
.end(done); | ||
coffee.fork('/path/to/cli', [ 'abcdefg' ]) | ||
.waitForPrompt() | ||
.write('tz\n') | ||
.write('2\n'); | ||
.end(done); | ||
``` | ||
@@ -144,2 +144,12 @@ | ||
```js | ||
coffee.fork('path/to/cli') | ||
.expect('stdout', 'abcdefg') | ||
.end(done); | ||
// recommended to left undefind and use promise. | ||
const { stdout, stderr, code } = await coffee.fork('path/to/cli').end(); | ||
assert(stdout.includes(abcdefg)); | ||
``` | ||
#### coffee.debug(level) | ||
@@ -166,7 +176,30 @@ | ||
### coffee.Rule | ||
Assertion Rule | ||
you could add your custom rule, see `test/fixtures/extendable` for more details. | ||
```js | ||
const { Coffee, Rule } = require('coffee'); | ||
class FileRule extends Rule { | ||
assert(actual, expected, message) { | ||
// do sth | ||
return super.assert(content, pattern, message); | ||
} | ||
} | ||
class MyCoffee extends Coffee { | ||
constructor(...args) { | ||
super(...args); | ||
this.setRule('file', FileRule); | ||
} | ||
} | ||
``` | ||
## LISENCE | ||
Copyright (c) 2017 popomore. Licensed under the MIT license. | ||
Copyright (c) 2017 node-modules. Licensed under the MIT license. | ||
[nyc]: https://github.com/istanbuljs/nyc |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
19530
356
197
2
3
9
2
+ Addedis-type-of@^1.2.0
+ Addedcore-util-is@1.0.3(transitive)
+ Addedis-class-hotfix@0.0.6(transitive)
+ Addedis-type-of@1.4.0(transitive)
+ Addedisstream@0.1.2(transitive)