diff --git a/.travis.yml b/.travis.yml index 7a360b48f..66d46cf72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ node_js: - "7" env: - NPM_SCRIPT=test + - NPM_SCRIPT=test:cli matrix: fast_finish: true allow_failures: diff --git a/package.json b/package.json index 74b08084e..49ff98832 100644 --- a/package.json +++ b/package.json @@ -31,19 +31,21 @@ "LICENSE.txt" ], "dependencies": { - "commander": "^2.9.0", - "js-reporters": "^1.1.0", - "walk-sync": "^0.3.1" + "commander": "2.9.0", + "js-reporters": "1.1.0", + "walk-sync": "0.3.1" }, "devDependencies": { "async": "2.1.4", "babel-plugin-external-helpers": "6.18.0", "babel-preset-es2015": "6.18.0", "browserstack-runner": "0.4.4", + "co": "4.6.0", "commitplease": "2.7.6", "eslint-config-jquery": "1.0.0", "eslint-plugin-html": "1.7.0", "eslint-plugin-qunit": "2.3.0", + "execa": "0.6.1", "fs-extra": "1.0.0", "grunt": "1.0.1", "grunt-cli": "1.2.0", @@ -66,7 +68,8 @@ "browserstack": "grunt build && sh build/run-browserstack.sh", "build": "grunt build", "test": "grunt", - "coverage": "grunt coverage" + "coverage": "grunt coverage", + "test:cli": "grunt build && npm link && qunit test/cli/*.js" }, "commitplease": { "components": [ diff --git a/test/cli/.eslintrc.json b/test/cli/.eslintrc.json new file mode 100644 index 000000000..10d223883 --- /dev/null +++ b/test/cli/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "env": { + "node": true + } +} diff --git a/test/cli/fixtures/double.js b/test/cli/fixtures/double.js new file mode 100644 index 000000000..53cae98c1 --- /dev/null +++ b/test/cli/fixtures/double.js @@ -0,0 +1,9 @@ +QUnit.module( "Double", function() { + QUnit.test( "has a test", function( assert ) { + assert.ok( true ); + } ); + + QUnit.test( "has another test", function( assert ) { + assert.ok( true ); + } ); +} ); diff --git a/test/cli/fixtures/fail/failure.js b/test/cli/fixtures/fail/failure.js new file mode 100644 index 000000000..afa9859c3 --- /dev/null +++ b/test/cli/fixtures/fail/failure.js @@ -0,0 +1,5 @@ +QUnit.module( "Failure", function() { + QUnit.test( "bad", function( assert ) { + assert.ok( false ); + } ); +} ); diff --git a/test/cli/fixtures/glob/a-test.js b/test/cli/fixtures/glob/a-test.js new file mode 100644 index 000000000..1d48c7c42 --- /dev/null +++ b/test/cli/fixtures/glob/a-test.js @@ -0,0 +1,5 @@ +QUnit.module( "A-Test", function() { + QUnit.test( "derp", function( assert ) { + assert.ok( true ); + } ); +} ); diff --git a/test/cli/fixtures/glob/not-a.js b/test/cli/fixtures/glob/not-a.js new file mode 100644 index 000000000..1270745a5 --- /dev/null +++ b/test/cli/fixtures/glob/not-a.js @@ -0,0 +1,5 @@ +QUnit.module( "Not-A", function() { + QUnit.test( "1", function( assert ) { + assert.ok( true ); + } ); +} ); diff --git a/test/cli/fixtures/glob/sub/nested-test.js b/test/cli/fixtures/glob/sub/nested-test.js new file mode 100644 index 000000000..8508ad6df --- /dev/null +++ b/test/cli/fixtures/glob/sub/nested-test.js @@ -0,0 +1,5 @@ +QUnit.module( "Nested-Test", function() { + QUnit.test( "herp", function( assert ) { + assert.ok( true ); + } ); +} ); diff --git a/test/cli/fixtures/single.js b/test/cli/fixtures/single.js new file mode 100644 index 000000000..dbf667163 --- /dev/null +++ b/test/cli/fixtures/single.js @@ -0,0 +1,5 @@ +QUnit.module( "Single", function() { + QUnit.test( "has a test", function( assert ) { + assert.ok( true ); + } ); +} ); diff --git a/test/cli/fixtures/test/first.js b/test/cli/fixtures/test/first.js new file mode 100644 index 000000000..3ae68d74c --- /dev/null +++ b/test/cli/fixtures/test/first.js @@ -0,0 +1,5 @@ +QUnit.module( "First", function() { + QUnit.test( "1", function( assert ) { + assert.ok( true ); + } ); +} ); diff --git a/test/cli/fixtures/test/second.js b/test/cli/fixtures/test/second.js new file mode 100644 index 000000000..61da2da29 --- /dev/null +++ b/test/cli/fixtures/test/second.js @@ -0,0 +1,5 @@ +QUnit.module( "Second", function() { + QUnit.test( "1", function( assert ) { + assert.ok( true ); + } ); +} ); diff --git a/test/cli/main.js b/test/cli/main.js new file mode 100644 index 000000000..1d72e6df0 --- /dev/null +++ b/test/cli/main.js @@ -0,0 +1,84 @@ +"use strict"; + +const path = require( "path" ); +const exec = require( "execa" ).shell; +const co = require( "co" ); + +function execute( command ) { + const cwd = process.cwd(); + process.chdir( path.join( __dirname, "fixtures" ) ); + + const execution = exec( command ); + + process.chdir( cwd ); + + return execution; +} + +QUnit.module( "cli", function() { + QUnit.test( "defaults to running tests in 'test' directory", co.wrap( function* ( assert ) { + const execution = yield execute( "qunit" ); + + assert.equal( execution.code, 0 ); + assert.equal( execution.stderr, "" ); + assert.equal( execution.stdout, "TAP version 13\nok 1 1\nok 2 1\n1..2" ); + } ) ); + + QUnit.test( "errors if no test files are found to run", co.wrap( function* ( assert ) { + try { + yield execute( "qunit does-not-exist.js" ); + } catch ( e ) { + assert.equal( e.stderr.indexOf( "No files were found matching" ), 0 ); + } + } ) ); + + QUnit.test( "accepts globs for test files to run", co.wrap( function* ( assert ) { + const execution = yield execute( "qunit 'glob/**/*-test.js'" ); + + assert.equal( execution.code, 0 ); + assert.equal( execution.stderr, "" ); + assert.equal( execution.stdout, "TAP version 13\nok 1 derp\nok 2 herp\n1..2" ); + } ) ); + + QUnit.test( "runs a single JS file", co.wrap( function* ( assert ) { + const execution = yield execute( "qunit single.js" ); + + assert.equal( execution.code, 0 ); + assert.equal( execution.stderr, "" ); + assert.equal( execution.stdout, "TAP version 13\nok 1 has a test\n1..1" ); + } ) ); + + QUnit.test( "runs multiple JS files", co.wrap( function* ( assert ) { + const execution = yield execute( "qunit single.js double.js" ); + + assert.equal( execution.code, 0 ); + assert.equal( execution.stderr, "" ); + assert.equal( execution.stdout, "TAP version 13\nok 1 has a test\n" + + "ok 2 has another test\nok 3 has a test\n1..3" ); + } ) ); + + QUnit.test( "runs all JS files in a directory matching an arg", co.wrap( function* ( assert ) { + const execution = yield execute( "qunit test" ); + + assert.equal( execution.code, 0 ); + assert.equal( execution.stderr, "" ); + assert.equal( execution.stdout, "TAP version 13\nok 1 1\nok 2 1\n1..2" ); + } ) ); + + QUnit.test( "runs multiple types of file paths", co.wrap( function* ( assert ) { + const execution = yield execute( "qunit test single.js 'glob/**/*-test.js'" ); + + assert.equal( execution.code, 0 ); + assert.equal( execution.stderr, "" ); + assert.equal( execution.stdout, "TAP version 13\nok 1 derp\nok 2 herp\n" + + "ok 3 has a test\nok 4 1\nok 5 1\n1..5" ); + } ) ); + + QUnit.test( "exit code is equal to number of failed tests", co.wrap( function* ( assert ) { + try { + yield execute( "qunit fail/*.js" ); + } catch ( e ) { + assert.equal( e.code, 1 ); + } + } ) ); +} ); diff --git a/yarn.lock b/yarn.lock index 7153ceceb..4de38a624 100644 --- a/yarn.lock +++ b/yarn.lock @@ -708,7 +708,7 @@ cliui@^2.1.0: right-align "^0.1.1" wordwrap "0.0.2" -co@^4.6.0: +co@4.6.0, co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -730,7 +730,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@^2.9.0: +commander@2.9.0, commander@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: @@ -805,6 +805,14 @@ coveralls@^2.11.1: minimist "1.2.0" request "2.75.0" +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -1170,6 +1178,18 @@ eventemitter2@^0.4.9, eventemitter2@~0.4.13: version "0.4.14" resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" +execa@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.6.1.tgz#79eda42ade78c387718b0aad48e0f573b5525cde" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -1317,6 +1337,10 @@ get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + getobject@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/getobject/-/getobject-0.1.0.tgz#047a449789fa160d018f5486ed91320b6ec7885c" @@ -1788,7 +1812,7 @@ is-resolvable@^1.0.0: dependencies: tryit "^1.0.1" -is-stream@^1.0.1: +is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -1837,7 +1861,7 @@ jodid25519@^1.0.0: dependencies: jsbn "~0.1.0" -js-reporters@^1.1.0: +js-reporters@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/js-reporters/-/js-reporters-1.1.0.tgz#c83c00fe0d4c9f67f944b4edd5f3b2957497cd62" @@ -2006,6 +2030,13 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lru-cache@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + dependencies: + pseudomap "^1.0.1" + yallist "^2.0.0" + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -2129,6 +2160,12 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + nue@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/nue/-/nue-0.7.1.tgz#33523bdf09c98e249fd42d4c928e8cc6c765073f" @@ -2204,6 +2241,10 @@ os-tmpdir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + "package@>= 1.0.0 < 1.2.0": version "1.0.1" resolved "https://registry.yarnpkg.com/package/-/package-1.0.1.tgz#d25a1f99e2506dcb27d6704b83dca8a312e4edcc" @@ -2242,6 +2283,10 @@ path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -2314,6 +2359,10 @@ progress@^1.1.8, progress@~1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + pump@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.2.tgz#3b3ee6512f94f0e575538c17995f9f16990a5d51" @@ -2673,6 +2722,16 @@ setprototypeof@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + shelljs@^0.7.5: version "0.7.6" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad" @@ -2819,6 +2878,10 @@ strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" @@ -2985,7 +3048,7 @@ verror@1.3.6: dependencies: extsprintf "1.0.2" -walk-sync@^0.3.1: +walk-sync@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.3.1.tgz#558a16aeac8c0db59c028b73c66f397684ece465" dependencies: @@ -3002,7 +3065,7 @@ websocket-extensions@>=0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" -which@^1.1.1, which@~1.2.1, which@~1.2.10: +which@^1.1.1, which@^1.2.9, which@~1.2.1, which@~1.2.10: version "1.2.12" resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" dependencies: @@ -3038,6 +3101,10 @@ xtend@^4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" +yallist@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"