diff --git a/.gitignore b/.gitignore index 6da6bc88eb..a3e485223c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules/ package-lock.json yarn.lock +coverage diff --git a/package.json b/package.json index b8c40dccce..37f70c0f40 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,10 @@ "node": ">= 16.0.0" }, "scripts": { - "test": "node zx.mjs test.mjs" + "test:cov": "c8 --reporter=html npm run test", + "test": "npm run test:index && npm run test:cli", + "test:cli": "node zx.mjs test/zx.test.mjs", + "test:index": "node zx.mjs test/index.test.mjs" }, "dependencies": { "@types/fs-extra": "^9.0.13", diff --git a/tests/interactive.mjs b/test/fixtures/interactive.mjs similarity index 100% rename from tests/interactive.mjs rename to test/fixtures/interactive.mjs diff --git a/tests/no-extension b/test/fixtures/no-extension similarity index 100% rename from tests/no-extension rename to test/fixtures/no-extension diff --git a/tests/no-extension.mjs b/test/fixtures/no-extension.mjs similarity index 100% rename from tests/no-extension.mjs rename to test/fixtures/no-extension.mjs diff --git a/test.mjs b/test/index.test.mjs similarity index 78% rename from test.mjs rename to test/index.test.mjs index 62900a57a5..94936d481d 100755 --- a/test.mjs +++ b/test/index.test.mjs @@ -12,20 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {strict as assert} from 'assert' -import {retry, echo, startSpinner, withTimeout } from './src/experimental.mjs' -let всегоТестов = 0 +import {inspect} from 'util' +import chalk from 'chalk' +import {Writable} from 'stream' +import {Socket} from 'net' -function test(name) { - let фильтр = process.argv[3] || '.' - if (RegExp(фильтр).test(name)) { - console.log('\n' + chalk.bgGreenBright.black(` ${name} `)) - всегоТестов++ - return true - } - return false -} +import {assert, test, testcount} from './test-utils.mjs' +import {retry, echo, startSpinner, withTimeout} from '../src/experimental.mjs' if (test('Only stdout is used during command substitution')) { let hello = await $`echo Error >&2; echo Hello` @@ -86,29 +80,20 @@ if (test('The toString() is called on arguments')) { if (test('Can use array as an argument')) { try { - let files = ['./zx.mjs', './test.mjs'] + let files = ['./zx.mjs', './test/index.test.mjs'] await $`tar czf archive ${files}` } finally { await $`rm archive` } } -if (test('Scripts with no extension')) { - await $`node zx.mjs tests/no-extension` - assert.match((await fs.readFile('tests/no-extension.mjs')).toString(), /Test file to verify no-extension didn't overwrite similarly name .mjs file./) -} - -if (test('The require() is working from stdin')) { - await $`node zx.mjs <<< 'require("./package.json").name'` -} - -if (test('Markdown scripts are working')) { - await $`node zx.mjs docs/markdown.md` -} - if (test('Quiet mode is working')) { - let {stdout} = await $`node zx.mjs --quiet docs/markdown.md` - assert(!stdout.includes('whoami')) + let stdout = '' + let log = console.log + console.log = (...args) => {stdout += args.join(' ')} + await quiet($`echo 'test'`) + console.log = log + assert(!stdout.includes('echo')) } if (test('Pipes are working')) { @@ -131,6 +116,43 @@ if (test('Pipes are working')) { } } +if ('question') { + let p = question('foo or bar? ', {choices: ['foo', 'bar']}) + + setTimeout(() => { + process.stdin.emit('data', 'f') + process.stdin.emit('data', '\t') + process.stdin.emit('data', '\r\n') + }, 100) + + assert.equal((await p).toString(), 'foo') + + await p +} + +if (test('ProcessPromise pipe')) { + let contents = '' + let stream = new Writable({ + write: function(chunk, encoding, next) { + contents += chunk.toString() + next() + } + }) + let p = $`echo 'test'`.pipe(stream) + await p + assert(p._piped) + assert.equal(contents, 'test\n') + assert(p.stderr instanceof Socket) + + let err + try { + $`echo 'test'`.pipe('str') + } catch (p) { + err = p + } + assert.equal(err.message, 'The pipe() method does not take strings. Forgot $?') +} + if (test('ProcessOutput thrown as error')) { let err try { @@ -139,6 +161,13 @@ if (test('ProcessOutput thrown as error')) { err = p } assert(err.exitCode > 0) + assert.equal(err.stderr, '/bin/bash: wtf: command not found\n') + assert.equal(err[inspect.custom](), `ProcessOutput { + stdout: '', + stderr: \x1B[31m'/bin/bash: wtf: command not found\\n'\x1B[39m, + signal: null, + exitCode: \x1B[31m127\x1B[39m\x1B[90m (Command not found)\x1B[39m +}`) } if (test('The pipe() throws if already resolved')) { @@ -174,6 +203,19 @@ if (test('globby available')) { assert(typeof globby.isGitIgnored === 'function') assert(typeof globby.isGitIgnoredSync === 'function') console.log(chalk.greenBright('globby available')) + + assert(await globby('test/fixtures/*'), [ + 'test/fixtures/interactive.mjs', + 'test/fixtures/no-extension', + 'test/fixtures/no-extension.mjs' + ]) +} + +if (test('fetch')) { + assert( + await fetch('https://example.com'), + await fetch('https://example.com', {method: 'GET'}) + ) } if (test('Executes a script from $PATH')) { @@ -289,7 +331,7 @@ if (test('spinner works (experimental)')) { let version if (test('require() is working in ESM')) { - let data = require('./package.json') + let data = require('../package.json') version = data.version assert.equal(data.name, 'zx') assert.equal(data, require('zx/package.json')) @@ -297,5 +339,5 @@ if (test('require() is working in ESM')) { console.log('\n' + chalk.black.bgYellowBright(` zx version is ${version} `) + '\n' + - chalk.greenBright(` 🍺 ${всегоТестов} tests passed `) + chalk.greenBright(` 🍺 ${testcount()} tests passed `) ) diff --git a/test/test-utils.mjs b/test/test-utils.mjs new file mode 100644 index 0000000000..ebf2789f83 --- /dev/null +++ b/test/test-utils.mjs @@ -0,0 +1,31 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import chalk from 'chalk' + +export {strict as assert} from 'assert' + +let всегоТестов = 0 + +export function test(name) { + let фильтр = process.argv[3] || '.' + if (RegExp(фильтр).test(name)) { + console.log('\n' + chalk.bgGreenBright.black(` ${name} `)) + всегоТестов++ + return true + } + return false +} + +export const testcount = () => всегоТестов diff --git a/test/zx.test.mjs b/test/zx.test.mjs new file mode 100644 index 0000000000..c9238e3f62 --- /dev/null +++ b/test/zx.test.mjs @@ -0,0 +1,63 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {assert, test, testcount} from './test-utils.mjs' +import chalk from 'chalk' + +if (test('supports `-v` flag / prints version')) { + let v = (await $`node zx.mjs -v`).toString().trim() + assert.equal(v, require('../package.json').version) +} + +if (test('prints help')) { + let help + try { + await $`node zx.mjs` + } catch (err) { + help = err.toString().trim() + } + assert(help.includes('print current zx version')) +} + +if (test('supports `--experimental` flag')) { + await $`echo 'echo("test")' | node zx.mjs --experimental` +} + +if (test('supports `--quiet` flag / Quiet mode is working')) { + let p = await $`node zx.mjs --quiet docs/markdown.md` + assert(!p.stdout.includes('whoami')) +} + +if (test('Eval script from https ref')) { + let p = $`node zx.mjs https://medv.io/example-script.mjs` // Need another script + setTimeout(() => p.kill(), 500) +} + +if (test('Scripts with no extension')) { + await $`node zx.mjs test/fixtures/no-extension` + assert.match((await fs.readFile('test/fixtures/no-extension.mjs')).toString(), /Test file to verify no-extension didn't overwrite similarly name .mjs file./) +} + +if (test('The require() is working from stdin')) { + await $`node zx.mjs <<< 'require("./package.json").name'` +} + +if (test('Markdown scripts are working')) { + await $`node zx.mjs docs/markdown.md` +} + +console.log('\n' + + chalk.black.bgYellowBright(` zx version is ${require('../package.json').version} `) + '\n' + + chalk.greenBright(` 🍺 ${testcount()} tests passed `) +)