diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 471f579f1..f11501440 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,7 +52,7 @@ This is for adding a module to be included in the default `citgm-all` runs. * Module source code must be on Github. * Published versions must include a tag on Github * The test process must be executable with only the commands -`npm install && npm test` using the tarball downloaded from the Github tag +`npm install && npm test` or (`yarn install && yarn test`) using the tarball downloaded from the Github tag mentioned above * The tests pass on supported major release lines * The maintainers of the module remain responsive when there are problems diff --git a/README.md b/README.md index 3b7492627..17e51ab86 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ Options: --includeTags tag1 tag2 Only test modules from the lookup that contain a matching tag field --excludeTags tag1 tag2 Specify which tags to skip from the lookup (takes priority over includeTags) Module names are automatically added as tags. + -y, --yarn Install and test the project using yarn instead of npm ``` When using a JSON config file, the properties need to be the same as the @@ -140,6 +141,7 @@ For syntax, see [lookup.json](./lib/lookup.json), the available attributes are: "maintainers": ["user1", "user2"] - List of module maintainers to be contacted with issues "tags": ["tag1", "tag2"] Specify which tags apply to the module "ignoreGitHead": Ignore the gitHead field if it exists and fallback to using github tags +"yarn": Install and test the project using yarn instead of npm ``` If you want to pass options to npm, eg `--registry`, you can usually define an diff --git a/bin/citgm-all.js b/bin/citgm-all.js index 3d8d157dd..ffe138664 100755 --- a/bin/citgm-all.js +++ b/bin/citgm-all.js @@ -71,6 +71,7 @@ const options = { timeoutLength: app.timeout, tmpDir: app.tmpDir, customTest: app.customTest, + yarn: app.yarn, includeTags: app.includeTags || [], excludeTags: app.excludeTags || [] }; diff --git a/bin/citgm.js b/bin/citgm.js index c905cf9c4..c74bc268a 100755 --- a/bin/citgm.js +++ b/bin/citgm.js @@ -48,7 +48,8 @@ const options = { timeoutLength: app.timeout, sha: app.sha, tmpDir: app.tmpDir, - customTest: app.customTest + customTest: app.customTest, + yarn: app.yarn }; if (!citgm.windows) { diff --git a/lib/citgm.js b/lib/citgm.js index 2e28a0f37..352a90c51 100644 --- a/lib/citgm.js +++ b/lib/citgm.js @@ -10,7 +10,7 @@ let which = require('which'); // Mocked in tests const grabModuleData = require('./grab-module-data'); const grabProject = require('./grab-project'); const lookup = require('./lookup'); -const npm = require('./npm'); +const packageManager = require('./package-manager'); const tempDirectory = require('./temp-directory'); const unpack = require('./unpack'); @@ -29,14 +29,28 @@ exports.windows = windows; * 5. Output the results. **/ -function find(app, context, next) { - which(app, function(err, resolved) { +function findNode(context, next) { + which('node', function(err, resolved) { if (err) { - next(Error(app + ' not found in path!')); + next(err); return; } - context.emit('data', 'verbose', context.module.name + ' using-' + app, - resolved); + context.emit('data', 'verbose', context.module.name + ' using-node', + resolved); + next(null, context); + }); +} + +function findPackageManagers(context, next) { + packageManager.getPackageManagers((err, res) => { + if (err) { + next(err); + return; + } + + context.npmPath = res.npm; + context.yarnPath = res.yarn; + next(null, context); }); } @@ -46,13 +60,13 @@ function init(context, next) { if (!windows) { if (context.options.uid) context.emit('data', 'verbose', context.module.name + ' using-uid', - context.options.uid); + context.options.uid); if (context.options.gid) context.emit('data', 'verbose', context.module.name + ' using-gid', - context.options.gid); + context.options.gid); } context.emit('data', 'silly', context.module.name + ' init-detail', - context.module); + context.module); next(null, context); // Inject the context } @@ -98,15 +112,15 @@ Tester.prototype.run = function() { async.waterfall([ init.bind(null, this), - find.bind(null, 'node'), - find.bind(null, 'npm'), + findNode, + findPackageManagers, tempDirectory.create, grabModuleData, lookup, grabProject, unpack, - npm.install, - npm.test + packageManager.install, + packageManager.test ], (err) => { if (!this.cleanexit) { const payload = { @@ -136,7 +150,7 @@ Tester.prototype.run = function() { }); }; -Tester.prototype.cleanup = function () { +Tester.prototype.cleanup = function() { this.cleanexit = true; const payload = { name: this.module.name || this.module.raw, diff --git a/lib/common-args.js b/lib/common-args.js index 26a566826..818cdb0f2 100644 --- a/lib/common-args.js +++ b/lib/common-args.js @@ -75,6 +75,12 @@ module.exports = function commonArgs (app) { description: 'Set timeout for npm install', default: 1000 * 60 * 10 }) + .option('yarn', { + alias: 'y', + type: 'boolean', + description: 'Install and test the project using yarn instead of npm', + default: false + }) .example('citgm-all --customTest /path/to/customTest.js', 'Runs a custom node test script instead of "npm test"'); diff --git a/lib/lookup.js b/lib/lookup.js index edc3c3902..35a2ea431 100644 --- a/lib/lookup.js +++ b/lib/lookup.js @@ -114,6 +114,9 @@ function resolve(context, next) { if (rep.tags) { context.module.tags = rep.tags; } + if (rep.yarn) { + context.module.useYarn = true; + } context.module.flaky = context.options.failFlaky ? false : isMatch(rep.flaky); context.module.expectFail = context.options.expectFail ? diff --git a/lib/npm/index.js b/lib/npm/index.js deleted file mode 100644 index eeb1b8fc9..000000000 --- a/lib/npm/index.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; -const install = require('./install'); -const test = require('./test'); - -module.exports = { - install: install, - test: test -}; diff --git a/lib/package-manager/get-executable.js b/lib/package-manager/get-executable.js new file mode 100644 index 000000000..34a856587 --- /dev/null +++ b/lib/package-manager/get-executable.js @@ -0,0 +1,30 @@ +'use strict'; + +const path = require('path'); +const which = require('which'); +const npmWhich = require('npm-which')(__dirname); + +const windows = (process.platform === 'win32'); + +module.exports = function getExecutable(binaryName, next) { + if (binaryName === 'yarn') { + // Use `npm-which` for yarn to get the locally version + npmWhich(binaryName, next); + + return; + } + + which(binaryName, (err, packageManagerBin) => { + if (err) { + next(err); + return; + } + + if (windows) { + packageManagerBin = path.join(path.dirname(packageManagerBin), + 'node_modules', 'npm', 'bin', 'npm-cli.js'); + } + + next(null, packageManagerBin); + }); +}; diff --git a/lib/package-manager/index.js b/lib/package-manager/index.js new file mode 100644 index 000000000..ac50df480 --- /dev/null +++ b/lib/package-manager/index.js @@ -0,0 +1,43 @@ +'use strict'; + +const async = require('async'); + +const install = require('./install'); +const test = require('./test'); +const getExecutable = require('./get-executable'); + +function pkgInstall(context, next) { + if (context.options.yarn || context.module.useYarn) { + install('yarn', context, next); + } else { + install('npm', context, next); + } +} + +function pkgTest(context, next) { + if (context.options.yarn || context.module.useYarn) { + test('yarn', context, next); + } else { + test('npm', context, next); + } +} + +function getPackageManagers(next) { + async.parallel([ + getExecutable.bind(null, 'npm'), + getExecutable.bind(null, 'yarn') + ], (err, res) => { + if (err) { + next(err); + return; + } + + next(null, { npm: res[0], yarn: res[1] }); + }); +} + +module.exports = { + install: pkgInstall, + test: pkgTest, + getPackageManagers: getPackageManagers +}; diff --git a/lib/npm/install.js b/lib/package-manager/install.js similarity index 59% rename from lib/npm/install.js rename to lib/package-manager/install.js index 8cfbeddab..010a1c3bd 100644 --- a/lib/npm/install.js +++ b/lib/package-manager/install.js @@ -7,22 +7,31 @@ const createOptions = require('../create-options'); const spawn = require('../spawn'); const timeout = require('../timeout'); -function install(context, next) { +function install(packageManager, context, next) { const options = createOptions( path.join(context.path, context.module.name), context); let args = ['install']; - context.emit('data', 'info', context.module.name + ' npm:', - 'npm install started'); + context.emit('data', 'info', context.module.name + ' ' + packageManager + + ':', + packageManager + ' install started'); - context.emit('data', 'verbose', context.module.name + ' npm:', + context.emit('data', 'verbose', context.module.name + ' ' + packageManager + + ':', 'Using temp directory: "' + options.env['npm_config_tmp'] + '"'); if (context.module.install) { args = args.concat(context.module.install); } - const proc = spawn('npm', args, options); + const packageManagerBin = packageManager === 'npm' + ? context.npmPath + : context.yarnPath; + + const binDirectory = path.dirname(packageManagerBin); + options.env.PATH = binDirectory + ':' + process.env.PATH; + + const proc = spawn(packageManagerBin, args, options); const finish = timeout(context, proc, next, 'Install'); proc.stderr.on('data', function (chunk) { @@ -31,7 +40,8 @@ function install(context, next) { chunk = stripAnsi(chunk.toString()); chunk = chunk.replace(/\r/g, '\n'); } - context.emit('data', 'warn', context.module.name + ' npm-install:', + context.emit('data', 'warn', context.module.name + + ' ' + packageManager + '-install:', chunk.toString()); }); @@ -41,7 +51,8 @@ function install(context, next) { chunk = stripAnsi(chunk.toString()); chunk = chunk.replace(/\r/g, '\n'); } - context.emit('data', 'verbose', context.module.name + ' npm-install:', + context.emit('data', 'verbose', context.module.name + + ' ' + packageManager + '-install:', chunk.toString()); }); @@ -53,8 +64,9 @@ function install(context, next) { if (code > 0) { return finish(Error('Install Failed')); } - context.emit('data', 'info', context.module.name + ' npm:', - 'npm install successfully completed'); + context.emit('data', 'info', context.module.name + ' ' + + packageManager + ':', + packageManager + ' install successfully completed'); return finish(null, context); }); } diff --git a/lib/npm/test.js b/lib/package-manager/test.js similarity index 78% rename from lib/npm/test.js rename to lib/package-manager/test.js index 6726b37d9..719707465 100644 --- a/lib/npm/test.js +++ b/lib/package-manager/test.js @@ -9,9 +9,7 @@ const createOptions = require('../create-options'); const spawn = require('../spawn'); const timeout = require('../timeout'); -const windows = (process.platform === 'win32'); let nodeBinName = 'node'; // Mocked in tests -const npmBinName = 'npm'; function authorName(author) { if (typeof author === 'string') return author; @@ -22,9 +20,9 @@ function authorName(author) { return parts.join(' '); } -function test(context, next) { +function test(packageManager, context, next) { const wd = path.join(context.path, context.module.name); - context.emit('data', 'info', context.module.name + ' npm:', + context.emit('data', 'info', context.module.name + ' ' + packageManager + ':', 'test suite started'); readPackage(path.join(wd, 'package.json'), false, function(err, data) { if (err) { @@ -35,10 +33,10 @@ function test(context, next) { data.scripts.test === undefined) { if (data.author) { context.emit('data', 'warn', context.module.name + ' notice', - 'Please contact the module developer to request adding npm' + - ' test support: ' + authorName(data.author)); + 'Please contact the module developer to request adding ' + + packageManager + '' + ' test support: ' + authorName(data.author)); } - next(new Error('Module does not support npm-test!')); + next(new Error('Module does not support ' + packageManager + '-test!')); return; } @@ -49,17 +47,18 @@ function test(context, next) { nodeBin = which(nodeBinName, {path: options.env.PATH || process.env.PATH}); } - let npmBin = which(npmBinName, {path: options.env.PATH - || process.env.PATH}); - if (windows) { - npmBin = path.join(path.dirname(npmBin), 'node_modules', 'npm', 'bin', - 'npm-cli.js'); - } - /* Run `npm test`, or `/path/to/customTest.js` if the customTest option + const packageManagerBin = packageManager === 'npm' + ? context.npmPath + : context.yarnPath; + + const binDirectory = path.dirname(packageManagerBin); + options.env.PATH = binDirectory + ':' + process.env.PATH; + + /* Run `npm/yarn test`, or `/path/to/customTest.js` if the customTest option was passed */ const args = context.options.customTest ? - [context.options.customTest] : [npmBin, 'test']; + [context.options.customTest] : [packageManagerBin, 'test']; const proc = spawn(nodeBin, args, options); const finish = timeout(context, proc, next, 'Test'); diff --git a/package.json b/package.json index 673771816..e2b9f3da6 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "mkdirp": "^0.5.1", "normalize-git-url": "^3.0.2", "npm-package-arg": "^6.1.0", + "npm-which": "^3.0.1", "osenv": "^0.1.5", "read-package-json": "^2.0.13", "request": "^2.88.0", @@ -55,7 +56,8 @@ "winston": "^2.4.4", "xml-sanitizer": "^1.1.6", "xmlbuilder": "^10.1.1", - "yargs": "^12.0.2" + "yargs": "^12.0.2", + "yarn": "^1.12.3" }, "devDependencies": { "eslint": "^5.7.0", diff --git a/test/helpers/make-context.js b/test/helpers/make-context.js index ff412d412..77315907d 100644 --- a/test/helpers/make-context.js +++ b/test/helpers/make-context.js @@ -2,7 +2,7 @@ const BufferList = require('bl'); -function npmContext(mod, path, options) { +function npmContext(mod, packageManagers, path, options) { if (typeof mod === 'string') { mod = { name: mod @@ -16,7 +16,9 @@ function npmContext(mod, path, options) { testOutput: new BufferList(), testError: new BufferList(), meta: {}, - options: options + options: options, + npmPath: packageManagers.npm, + yarnPath: packageManagers.yarn }; return context; } diff --git a/test/npm/test-npm-author-name.js b/test/npm/test-npm-author-name.js index 098ddfd55..91acb269f 100644 --- a/test/npm/test-npm-author-name.js +++ b/test/npm/test-npm-author-name.js @@ -3,7 +3,7 @@ const test = require('tap').test; const rewire = require('rewire'); -const npmTest = rewire('../../lib/npm/test'); +const npmTest = rewire('../../lib/package-manager/test'); const authorName = npmTest.__get__('authorName'); diff --git a/test/npm/test-npm-install.js b/test/npm/test-npm-install.js index cbf6cde3b..d93cd46c9 100644 --- a/test/npm/test-npm-install.js +++ b/test/npm/test-npm-install.js @@ -8,7 +8,8 @@ const mkdirp = require('mkdirp'); const rimraf = require('rimraf'); const ncp = require('ncp'); -const npmInstall = require('../../lib/npm/install'); +const packageManager = require('../../lib/package-manager'); +const packageManagerInstall = require('../../lib/package-manager/install'); const makeContext = require('../helpers/make-context'); const sandbox = path.join(os.tmpdir(), 'citgm-' + Date.now()); @@ -20,8 +21,14 @@ const extraParamTemp = path.join(sandbox, 'omg-i-pass-with-install-param'); const badFixtures = path.join(fixtures, 'omg-bad-tree'); const badTemp = path.join(sandbox, 'omg-bad-tree'); +let packageManagers; + test('npm-install: setup', function (t) { - t.plan(7); + t.plan(8); + packageManager.getPackageManagers((e, res) => { + packageManagers = res; + t.error(e); + }); mkdirp(sandbox, function (err) { t.error(err); ncp(moduleFixtures, moduleTemp, function (e) { @@ -40,10 +47,11 @@ test('npm-install: setup', function (t) { }); test('npm-install: basic module', function (t) { - const context = makeContext.npmContext('omg-i-pass', sandbox, { - npmLevel: 'silly' - }); - npmInstall(context, function (err) { + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + npmLevel: 'silly' + }); + packageManagerInstall('npm', context, function (err) { t.error(err); t.end(); }); @@ -53,10 +61,10 @@ test('npm-install: extra install parameters', function (t) { const context = makeContext.npmContext({ name: 'omg-i-pass-with-install-param', install: ['--extra-param'] - }, sandbox, { + }, packageManagers, sandbox, { npmLevel: 'silly' }); - npmInstall(context, function (err) { + packageManagerInstall('npm', context, function (err) { t.error(err); t.notOk(context.module.flaky, 'Module passed and is not flaky'); t.end(); @@ -64,10 +72,11 @@ test('npm-install: extra install parameters', function (t) { }); test('npm-install: no package.json', function (t) { - const context = makeContext.npmContext('omg-i-fail', sandbox, { - npmLevel: 'silly' - }); - npmInstall(context, function (err) { + const context = makeContext.npmContext('omg-i-fail', packageManagers, + sandbox, { + npmLevel: 'silly' + }); + packageManagerInstall('npm', context, function (err) { t.equals(err && err.message, 'Install Failed'); t.notOk(context.module.flaky, 'Module failed but is not flaky'); t.end(); @@ -75,11 +84,12 @@ test('npm-install: no package.json', function (t) { }); test('npm-install: timeout', function (t) { - const context = makeContext.npmContext('omg-i-pass', sandbox, { - npmLevel: 'silly', - timeoutLength: 100 - }); - npmInstall(context, function (err) { + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + npmLevel: 'silly', + timeoutLength: 100 + }); + packageManagerInstall('npm', context, function (err) { t.ok(context.module.flaky, 'Module is Flaky because install timed out'); t.equals(err && err.message, 'Install Timed Out'); t.end(); @@ -87,14 +97,15 @@ test('npm-install: timeout', function (t) { }); test('npm-install: failed install', function (t) { - const context = makeContext.npmContext('omg-bad-tree', sandbox, { - npmLevel: 'http' - }); + const context = makeContext.npmContext('omg-bad-tree', packageManagers, + sandbox, { + npmLevel: 'http' + }); const expected = { testOutput: /^$/, testError: /npm ERR! 404 Not [Ff]ound\s*: THIS-WILL-FAIL(@0\.0\.1)?/ }; - npmInstall(context, function (err) { + packageManagerInstall('npm', context, function (err) { t.notOk(context.module.flaky, 'Module failed is not flaky'); t.equals(err && err.message, 'Install Failed'); t.match(context, expected, 'Install error reported'); diff --git a/test/npm/test-npm-test.js b/test/npm/test-npm-test.js index 6c094b4d6..867150ce5 100644 --- a/test/npm/test-npm-test.js +++ b/test/npm/test-npm-test.js @@ -10,7 +10,8 @@ const ncp = require('ncp'); const rewire = require('rewire'); const makeContext = require('../helpers/make-context'); -const npmTest = rewire('../../lib/npm/test'); +const packageManager = require('../../lib/package-manager'); +const packageManagerTest = rewire('../../lib/package-manager/test'); const sandbox = path.join(os.tmpdir(), 'citgm-' + Date.now()); const fixtures = path.join(__dirname, '..', 'fixtures'); @@ -24,8 +25,14 @@ const failTemp = path.join(sandbox, 'omg-i-fail'); const badFixtures = path.join(fixtures, 'omg-i-do-not-support-testing'); const badTemp = path.join(sandbox, 'omg-i-do-not-support-testing'); +let packageManagers; + test('npm-test: setup', function (t) { - t.plan(7); + t.plan(8); + packageManager.getPackageManagers((e, res) => { + packageManagers = res; + t.error(e); + }); mkdirp(sandbox, function (err) { t.error(err); ncp(passFixtures, passTemp, function (e) { @@ -44,18 +51,20 @@ test('npm-test: setup', function (t) { }); test('npm-test: basic module passing', function (t) { - const context = makeContext.npmContext('omg-i-pass', sandbox, { - npmLevel: 'silly' - }); - npmTest(context, function (err) { + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + npmLevel: 'silly' + }); + packageManagerTest('npm', context, function (err) { t.error(err); t.end(); }); }); test('npm-test: basic module failing', function (t) { - const context = makeContext.npmContext('omg-i-fail', sandbox); - npmTest(context, function (err) { + const context = makeContext.npmContext('omg-i-fail', packageManagers, + sandbox); + packageManagerTest('npm', context, function (err) { t.equals(err && err.message, 'The canary is dead:'); t.end(); }); @@ -63,16 +72,18 @@ test('npm-test: basic module failing', function (t) { test('npm-test: basic module no test script', function (t) { const context = - makeContext.npmContext('omg-i-do-not-support-testing', sandbox); - npmTest(context, function (err) { + makeContext.npmContext('omg-i-do-not-support-testing', packageManagers, + sandbox); + packageManagerTest('npm', context, function (err) { t.equals(err && err.message, 'Module does not support npm-test!'); t.end(); }); }); test('npm-test: no package.json', function (t) { - const context = makeContext.npmContext('omg-i-dont-exist', sandbox); - npmTest(context, function (err) { + const context = makeContext.npmContext('omg-i-dont-exist', packageManagers, + sandbox); + packageManagerTest('npm', context, function (err) { t.equals(err && err.message, 'Package.json Could not be found'); t.end(); }); @@ -80,25 +91,27 @@ test('npm-test: no package.json', function (t) { test('npm-test: alternative test-path', function (t) { // Same test as 'basic module passing', except with alt node bin which fails. - const nodeBinName = npmTest.__get__('nodeBinName'); - npmTest.__set__('nodeBinName', 'fake-node'); - const context = makeContext.npmContext('omg-i-pass', sandbox, { - npmLevel: 'silly', - testPath: path.resolve(__dirname, '..', 'fixtures', 'fakenodebin') - }); - npmTest(context, function (err) { - npmTest.__set__('nodeBinName', nodeBinName); + const nodeBinName = packageManagerTest.__get__('nodeBinName'); + packageManagerTest.__set__('nodeBinName', 'fake-node'); + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + npmLevel: 'silly', + testPath: path.resolve(__dirname, '..', 'fixtures', 'fakenodebin') + }); + packageManagerTest('npm', context, function (err) { + packageManagerTest.__set__('nodeBinName', nodeBinName); t.equals(err && err.message, 'The canary is dead:'); t.end(); }); }); test('npm-test: timeout', function (t) { - const context = makeContext.npmContext('omg-i-pass', sandbox, { - npmLevel: 'silly', - timeoutLength: 100 - }); - npmTest(context, function (err) { + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + npmLevel: 'silly', + timeoutLength: 100 + }); + packageManagerTest('npm', context, function (err) { t.ok(context.module.flaky, 'Module is Flaky because tests timed out'); t.equals(err && err.message, 'Test Timed Out'); t.end(); diff --git a/test/test-citgm.js b/test/test-citgm.js index 62239f31d..d2b114ece 100644 --- a/test/test-citgm.js +++ b/test/test-citgm.js @@ -4,7 +4,6 @@ const test = require('tap').test; const rewire = require('rewire'); const citgm = rewire('../lib/citgm'); -const find = citgm.__get__('find'); test('citgm: omg-i-pass', function (t) { const options = { @@ -47,15 +46,3 @@ test('citgm: omg-i-pass from git url', function (t) { t.end(); }).run(); }); - -test('citgm: internal function find with error', function (t) { - const which = citgm.__get__('which'); - citgm.__set__('which', function (app, next) { - return next('Error'); - }); - find(undefined, undefined, function (err) { - t.equals(err && err.message, 'undefined not found in path!'); - citgm.__set__('which', which); - t.end(); - }); -}); diff --git a/test/test-timeout.js b/test/test-timeout.js index 2f591024d..80114ef55 100644 --- a/test/test-timeout.js +++ b/test/test-timeout.js @@ -5,15 +5,27 @@ const path = require('path'); const test = require('tap').test; const rewire = require('rewire'); +const packageManager = require('../lib/package-manager'); const timeout = rewire('../lib/timeout'); const makeContext = require('./helpers/make-context'); const sandbox = path.join(os.tmpdir(), 'citgm-' + Date.now()); -test('timeout:', function (t) { - const context = makeContext.npmContext('omg-i-pass', sandbox, { - npmLevel: 'silly', - timeoutLength: 100 +let packageManagers; + +test('timeout: setup', function (t) { + t.plan(1); + packageManager.getPackageManagers((e, res) => { + packageManagers = res; + t.error(e); }); +}); + +test('timeout:', function (t) { + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + npmLevel: 'silly', + timeoutLength: 100 + }); const proc = { kill() { this.killed++; @@ -42,10 +54,11 @@ test('timeout:', function (t) { }); test('timeout:', function (t) { - const context = makeContext.npmContext('omg-i-pass', sandbox, { - npmLevel: 'silly', - timeoutLength: 100 - }); + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + npmLevel: 'silly', + timeoutLength: 100 + }); const proc = { kill() { this.killed++; diff --git a/test/yarn/test-yarn-install.js b/test/yarn/test-yarn-install.js new file mode 100644 index 000000000..d7d07e573 --- /dev/null +++ b/test/yarn/test-yarn-install.js @@ -0,0 +1,107 @@ +'use strict'; +const os = require('os'); +const path = require('path'); +const fs = require('fs'); + +const test = require('tap').test; +const mkdirp = require('mkdirp'); +const rimraf = require('rimraf'); +const ncp = require('ncp'); + +const packageManager = require('../../lib/package-manager'); +const packageManagerInstall = require('../../lib/package-manager/install'); +const makeContext = require('../helpers/make-context'); + +const sandbox = path.join(os.tmpdir(), 'citgm-' + Date.now()); +const fixtures = path.join(__dirname, '..', 'fixtures'); +const moduleFixtures = path.join(fixtures, 'omg-i-pass'); +const moduleTemp = path.join(sandbox, 'omg-i-pass'); +const extraParamFixtures = path.join(fixtures, 'omg-i-pass-with-install-param'); +const extraParamTemp = path.join(sandbox, 'omg-i-pass-with-install-param'); +const badFixtures = path.join(fixtures, 'omg-bad-tree'); +const badTemp = path.join(sandbox, 'omg-bad-tree'); + +let packageManagers; + +test('yarn-install: setup', function (t) { + t.plan(8); + packageManager.getPackageManagers((e, res) => { + packageManagers = res; + t.error(e); + }); + mkdirp(sandbox, function (err) { + t.error(err); + ncp(moduleFixtures, moduleTemp, function (e) { + t.error(e); + t.ok(fs.existsSync(path.join(moduleTemp, 'package.json'))); + }); + ncp(extraParamFixtures, extraParamTemp, function (e) { + t.error(e); + t.ok(fs.existsSync(path.join(moduleTemp, 'package.json'))); + }); + ncp(badFixtures, badTemp, function (e) { + t.error(e); + t.ok(fs.existsSync(path.join(badTemp, 'package.json'))); + }); + }); +}); + +test('yarn-install: basic module', function (t) { + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox); + packageManagerInstall('yarn', context, function (err) { + context.testOutput = context.testOutput.toString(); + context.testError = context.testError.toString(); + t.error(err); + t.end(); + }); +}); + +test('yarn-install: no package.json', function (t) { + const context = makeContext.npmContext('omg-i-fail', packageManagers, + sandbox); + packageManagerInstall('yarn', context, function (err) { + context.testOutput = context.testOutput.toString(); + context.testError = context.testError.toString(); + t.equals(err && err.message, 'Install Failed'); + t.notOk(context.module.flaky, 'Module failed but is not flaky'); + t.end(); + }); +}); + +test('yarn-install: timeout', function (t) { + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + timeoutLength: 100 + }); + packageManagerInstall('yarn', context, function (err) { + context.testOutput = context.testOutput.toString(); + context.testError = context.testError.toString(); + t.ok(context.module.flaky, 'Module is Flaky because install timed out'); + t.equals(err && err.message, 'Install Timed Out'); + t.end(); + }); +}); + +test('yarn-install: failed install', function (t) { + const context = makeContext.npmContext('omg-bad-tree', packageManagers, + sandbox); + const expected = { + testError: /\/THIS-WILL-FAIL: Not found/ + }; + packageManagerInstall('yarn', context, function (err) { + context.testOutput = context.testOutput.toString(); + context.testError = context.testError.toString(); + t.notOk(context.module.flaky, 'Module failed is not flaky'); + t.equals(err && err.message, 'Install Failed'); + t.match(context, expected, 'Install error reported'); + t.end(); + }); +}); + +test('yarn-install: teardown', function (t) { + rimraf(sandbox, function (err) { + t.error(err); + t.end(); + }); +}); diff --git a/test/yarn/test-yarn-test.js b/test/yarn/test-yarn-test.js new file mode 100644 index 000000000..154f1f387 --- /dev/null +++ b/test/yarn/test-yarn-test.js @@ -0,0 +1,122 @@ +'use strict'; +const os = require('os'); +const path = require('path'); +const fs = require('fs'); + +const test = require('tap').test; +const mkdirp = require('mkdirp'); +const rimraf = require('rimraf'); +const ncp = require('ncp'); +const rewire = require('rewire'); + +const makeContext = require('../helpers/make-context'); +const packageManager = require('../../lib/package-manager'); +const packageManagerTest = rewire('../../lib/package-manager/test'); + +const sandbox = path.join(os.tmpdir(), 'citgm-' + Date.now()); +const fixtures = path.join(__dirname, '..', 'fixtures'); + +const passFixtures = path.join(fixtures, 'omg-i-pass'); +const passTemp = path.join(sandbox, 'omg-i-pass'); + +const failFixtures = path.join(fixtures, 'omg-i-fail'); +const failTemp = path.join(sandbox, 'omg-i-fail'); + +const badFixtures = path.join(fixtures, 'omg-i-do-not-support-testing'); +const badTemp = path.join(sandbox, 'omg-i-do-not-support-testing'); + +let packageManagers; + +test('yarn-test: setup', function (t) { + t.plan(8); + packageManager.getPackageManagers((e, res) => { + packageManagers = res; + t.error(e); + }); + mkdirp(sandbox, function (err) { + t.error(err); + ncp(passFixtures, passTemp, function (e) { + t.error(e); + t.ok(fs.existsSync(path.join(passTemp, 'package.json'))); + }); + ncp(failFixtures, failTemp, function (e) { + t.error(e); + t.ok(fs.existsSync(path.join(failTemp, 'package.json'))); + }); + ncp(badFixtures, badTemp, function (e) { + t.error(e); + t.ok(fs.existsSync(path.join(badTemp, 'package.json'))); + }); + }); +}); + +test('yarn-test: basic module passing', function (t) { + const context = makeContext.npmContext('omg-i-pass', + packageManagers, sandbox); + packageManagerTest('yarn', context, function (err) { + t.error(err); + t.end(); + }); +}); + +test('yarn-test: basic module failing', function (t) { + const context = makeContext.npmContext('omg-i-fail', + packageManagers, sandbox); + packageManagerTest('yarn', context, function (err) { + t.equals(err && err.message, 'The canary is dead:'); + t.end(); + }); +}); + +test('yarn-test: basic module no test script', function (t) { + const context = + makeContext.npmContext('omg-i-do-not-support-testing', + packageManagers, sandbox); + packageManagerTest('yarn', context, function (err) { + t.equals(err && err.message, 'Module does not support yarn-test!'); + t.end(); + }); +}); + +test('yarn-test: no package.json', function (t) { + const context = makeContext.npmContext('omg-i-dont-exist', + packageManagers, sandbox); + packageManagerTest('yarn', context, function (err) { + t.equals(err && err.message, 'Package.json Could not be found'); + t.end(); + }); +}); + +test('yarn-test: alternative test-path', function (t) { + // Same test as 'basic module passing', except with alt node bin which fails. + const nodeBinName = packageManagerTest.__get__('nodeBinName'); + packageManagerTest.__set__('nodeBinName', 'fake-node'); + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + testPath: path.resolve(__dirname, '..', 'fixtures', 'fakenodebin') + }); + packageManagerTest('yarn', context, function (err) { + packageManagerTest.__set__('nodeBinName', nodeBinName); + t.equals(err && err.message, 'The canary is dead:'); + t.end(); + }); +}); + +test('yarn-test: timeout', function (t) { + const context = makeContext.npmContext('omg-i-pass', packageManagers, + sandbox, { + timeoutLength: 100 + }); + packageManagerTest('yarn', context, function (err) { + t.ok(context.module.flaky, 'Module is Flaky because tests timed out'); + t.equals(err && err.message, 'Test Timed Out'); + t.end(); + }); +}); + +test('yarn-test: teardown', function (t) { + rimraf(sandbox, function (err) { + t.error(err); + t.end(); + }); +});