{"id":13685458,"url":"https://github.com/node-modules/coffee","last_synced_at":"2025-10-26T21:14:31.949Z","repository":{"id":25989322,"uuid":"29431577","full_name":"node-modules/coffee","owner":"node-modules","description":"Test command line on Node.js","archived":false,"fork":false,"pushed_at":"2023-09-19T17:02:05.000Z","size":164,"stargazers_count":99,"open_issues_count":8,"forks_count":18,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-05-24T22:07:38.528Z","etag":null,"topics":["cli","command-line","unittesting"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/node-modules.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-01-18T16:09:14.000Z","updated_at":"2024-08-22T05:28:57.000Z","dependencies_parsed_at":"2024-06-16T19:22:44.828Z","dependency_job_id":"5964036c-6f8b-47db-b925-5319e5b44bdc","html_url":"https://github.com/node-modules/coffee","commit_stats":{"total_commits":107,"total_committers":11,"mean_commits":9.727272727272727,"dds":"0.30841121495327106","last_synced_commit":"c6bca4f5bcd6c98e9af9f2bedd29977f7b383de8"},"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"purl":"pkg:github/node-modules/coffee","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-modules%2Fcoffee","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-modules%2Fcoffee/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-modules%2Fcoffee/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-modules%2Fcoffee/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/node-modules","download_url":"https://codeload.github.com/node-modules/coffee/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-modules%2Fcoffee/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259752078,"owners_count":22905972,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","command-line","unittesting"],"created_at":"2024-08-02T14:00:51.826Z","updated_at":"2025-10-26T21:14:26.913Z","avatar_url":"https://github.com/node-modules.png","language":"JavaScript","readme":"# Coffee\n\nTest command line on Node.js.\n\n---\n\n[![NPM version](https://img.shields.io/npm/v/coffee.svg?style=flat)](https://npmjs.org/package/coffee)\n[![Build Status](https://img.shields.io/travis/node-modules/coffee.svg?style=flat)](https://travis-ci.org/node-modules/coffee)\n[![codecov.io](https://img.shields.io/codecov/c/github/node-modules/coffee.svg?style=flat)](http://codecov.io/github/node-modules/coffee?branch=master)\n[![NPM downloads](http://img.shields.io/npm/dm/coffee.svg?style=flat)](https://npmjs.org/package/coffee)\n\n## Install\n\n```bash\n$ npm i coffee --save-dev\n```\n\n## Usage\n\nCoffee is useful for test command line in test frammework (like Mocha).\n\n### Fork\n\nYou can use `fork` for spawning Node processes.\n\n```js\nconst coffee = require('coffee');\n\ndescribe('cli', () =\u003e {\n  it('should fork node cli', () =\u003e {\n    return coffee.fork('/path/to/file.js')\n    .expect('stdout', '12\\n')\n    .expect('stderr', /34/)\n    .expect('code', 0)\n    .end();\n  });\n});\n```\n\nIn file.js\n\n```js\nconsole.log(12);\nconsole.error(34);\n```\n\nYou can pass `args` and `opts` to [child_process fork](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options).\n\n```js\ncoffee.fork('/path/to/file.js', [ 'args' ], { execArgv: [ '--inspect' ]})\n  .expect('stdout', '12\\n')\n  .expect('stderr', '34\\n')\n  .expect('code', 0)\n  .end();\n```\n\nAnd more:\n\n```js\ncoffee.fork('/path/to/file.js')\n  // print origin stdio\n  .debug()\n\n  // inject a script\n  .beforeScript(mockScript)\n\n  // interact with prompt\n  .waitForPrompt()\n  .write('tz\\n')\n\n  // string strict equals\n  .expect('stdout', 'abcdefg')\n  // regex\n  .expect('stdout', /^abc/)\n  // multiple\n  .expect('stdout', [ 'abcdefg', /abc/ ])\n  .expect('code', 0)\n  .end();\n```\n\nsee the API chapter below for more details.\n\n### Spawn\n\nYou can also use `spawn` for spawning normal shell scripts.\n\n```js\ncoffee.spawn('cat')\n  .write('1')\n  .write('2')\n  .expect('stdout', '12')\n  .expect('code', 0)\n  .end();\n```\n\n## Rule\n\n### code\n\nCheck the exit code.\n\n```js\ncoffee.fork('/path/to/file.js', [ 'args' ])\n  .expect('code', 0)\n  // .expect('code', 1)\n  .end();\n```\n\n### stdout / stderr\n\nCheck the stdout and stderr.\n\n```js\ncoffee.fork('/path/to/file.js', [ 'args' ])\n  .expect('stdout', '12\\n')\n  .expect('stderr', '34\\n')\n  .expect('code', 0)\n  .end();\n```\n\n### custom\n\nSupport custom rules, see `test/fixtures/extendable` for more details.\n\n```js\nconst { Coffee, Rule } = require('coffee');\n\nclass FileRule extends Rule {\n  constructor(opts) {\n    super(opts);\n    // `args` is which pass to `expect(type, ...args)`, `expected` is the first args.\n    const { args, expected } = opts;\n  }\n\n  assert(actual, expected, message) {\n    // do sth\n    return super.assert(fs.existsSync(expected), true, `should exists file ${expected}`);\n  }\n}\n\nclass MyCoffee extends Coffee {\n  constructor(...args) {\n    super(...args);\n    this.setRule('file', FileRule);\n  }\n\n  static fork(modulePath, args, opt) {\n    return new MyCoffee({\n      method: 'fork',\n      cmd: modulePath,\n      args,\n      opt,\n    });\n  }\n}\n```\n\nUsage:\n\n```js\n// test/custom.test.js\nconst coffee = require('MyCoffee');\ncoffee.fork('/path/to/file.js', [ 'args' ])\n  .expect('file', `${root}/README.md`);\n  .notExpect('file', `${root}/not-exist`);\n```\n\n## Support multiple process coverage with nyc\n\nRecommend to use [nyc] for coverage, you can use [any test frammework supported by nyc](https://istanbul.js.org/docs/tutorials/).\n\n## API\n\n### coffee.spawn\n\nRun command using `child_process.spawn`, then return `Coffee` instance.\n\nArguments see [child_process.spawn](http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options)\n\n### coffee.fork\n\nRun command using `child_process.fork`, then return `Coffee` instance.\n\nArguments see [child_process.fork](http://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options)\n\n### coffee.Coffee\n\nAssertion object\n\n#### coffee.expect(type, ...args)\n\nAssert type with expected value, expected value can be string, regular expression, and array.\n\n```js\ncoffee.spawn('echo', [ 'abcdefg' ])\n  .expect('stdout', 'abcdefg')\n  .expect('stdout', /^abc/)\n  .expect('stdout', [ 'abcdefg', /abc/ ])\n  .end();\n```\n\nAccept type: `stdout` / `stderr` / `code` / `error`, see built-in rules description above.\n\n#### coffee.notExpect(type, ...args)\n\nThe opposite assertion of `expect`.\n\n#### coffee.includes(type, ...args)\n\nAssert type with expected string value, expected value should be string only.\n\n```js\ncoffee.spawn('echo', [ 'abcdefg' ])\n  .includes('stdout', 'abc')\n  .expect('stdout', [ 'abc', 'efg' ])\n  .end();\n```\n\nAccept type: `stdout` / `stderr`, see built-in rules description above.\n\n#### coffee.notIncludes(type, ...args)\n\nThe opposite assertion of `includes`.\n\n#### coffee.write(data)\n\nWrite data to stdin.\n\n```js\ncoffee.fork(path.join(fixtures, 'stdin.js'))\n  .write('1\\n')\n  .write('2')\n  .expect('stdout', '1\\n2')\n  .end();\n```\n\n#### coffee.writeKey(...args)\n\nWrite special key sequence to stdin, support `UP` / `DOWN` / `LEFT` / `RIGHT` / `ENTER` / `SPACE`.\n\nAll args will join as one key.\n\n```js\ncoffee.fork(path.join(fixtures, 'stdin.js'))\n  .writeKey('1', 'ENTER', '2')\n  .expect('stdout', '1\\n2')\n  .end();\n```\n\n#### coffee.waitForPrompt(bool)\n\nIf you set false, coffee will write stdin immediately, otherwise will wait for `prompt` message.\n\n```js\ncoffee.fork('/path/to/cli', [ 'abcdefg' ])\n  .waitForPrompt()\n  .write('tz\\n')\n  // choose the second item\n  .writeKey('DOWN', 'DOWN', 'ENTER');\n  .end(done);\n```\n\n**cli process should emit `prompt` message:**\n\n\u003e Or use `coffee.on('stdout', callback)` instead, see docs below.\n\n```js\nconst readline = require('readline');\n\nconst rl = readline.createInterface({\n  input: process.stdin,\n  output: process.stdout\n});\n\nfunction ask(q, callback) {\n  process.send({ type: 'prompt' });\n  rl.question(q, callback);\n}\n\nask('What\\'s your name? ', answer =\u003e {\n  console.log(`hi, ${answer}`);\n  ask('How many coffee do you want? ', answer =\u003e {\n    console.log(`here is your ${answer} coffee`);\n    rl.close();\n  });\n});\n```\n\n#### coffee.end([callback])\n\nCallback will be called after completing the assertion, the first argument is Error if throw exception.\n\n```js\ncoffee.fork('path/to/cli')\n  .expect('stdout', 'abcdefg')\n  .end(done);\n\n// recommended to left undefind and use promise style.\nconst { stdout, stderr, code } = await coffee.fork('path/to/cli').end();\nassert(stdout.includes(abcdefg));\n```\n\n#### coffee.on(event, callback)\n\nEmit `stdout/stderr` event.\n\nuse for kill long-run process:\n\n```js\ncoffee.fork('path/to/cli')\n  .on('stdout', (buf, { proc }) =\u003e {\n    if (buf.includes('egg-ready')) {\n      proc.exitCode = 0;\n      proc.kill();\n    }\n  })\n  .expect('stdout', 'egg-ready')\n  .end(done);\n```\n\nuse for prompt:\n\n```js\n// do not call `waitForPrompt` / `write` / `writeKey`\ncoffee.fork('path/to/cli')\n  .on('stdout', (buf, { proc }) =\u003e {\n    if (buf.includes('Your Name: ')) {\n      proc.stdin.write('TZ\\n');\n    }\n  })\n  .expect('stdout', 'Your Name: TZ\\n')\n  .end(done);\n```\n\n#### coffee.debug(level)\n\nWrite data to process.stdout and process.stderr for debug\n\n`level` can be\n\n- 0 (default): pipe stdout + stderr\n- 1: pipe stdout\n- 2: pipe stderr\n- false: disable\n\nAlternative you can use `COFFEE_DEBUG` env.\n\n#### coffee.coverage()\n\nIf you set false, coffee will not generate coverage.json, default: true.\n\n#### coffee.beforeScript(scriptFile)\n\nAdd a hook script before fork child process run.\n\n### coffee.Rule\n\nAssertion Rule base class.\n\n## LICENSE\n\nCopyright (c) 2017 - 2019 node-modules. Licensed under the MIT license.\n\n[nyc]: https://github.com/istanbuljs/nyc\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnode-modules%2Fcoffee","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnode-modules%2Fcoffee","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnode-modules%2Fcoffee/lists"}