diff --git a/bin/web.js b/bin/web.js index 9e7c0aab..a161b2a5 100644 --- a/bin/web.js +++ b/bin/web.js @@ -1,3 +1,5 @@ +/* eslint-disable no-console */ + var express = require('express'); var uuid = require('uuid'); var basicAuth = require('basic-auth'); @@ -18,15 +20,15 @@ if (process.env.ANALYTICS_TOKEN) { } var myNuts = nuts.Nuts({ - repository: process.env.GITHUB_REPO, - token: process.env.GITHUB_TOKEN, - endpoint: process.env.GITHUB_ENDPOINT, - username: process.env.GITHUB_USERNAME, - password: process.env.GITHUB_PASSWORD, - timeout: process.env.VERSIONS_TIMEOUT, - cache: process.env.VERSIONS_CACHE, + repository: process.env.GITHUB_REPO, + token: process.env.GITHUB_TOKEN, + endpoint: process.env.GITHUB_ENDPOINT, + username: process.env.GITHUB_USERNAME, + password: process.env.GITHUB_PASSWORD, + timeout: process.env.VERSIONS_TIMEOUT, + cache: process.env.VERSIONS_CACHE, refreshSecret: process.env.GITHUB_SECRET, - proxyAssets: !Boolean(process.env.DONT_PROXY_ASSETS) + proxyAssets: !process.env.DONT_PROXY_ASSETS }); // Control access to API @@ -35,28 +37,28 @@ myNuts.before('api', function(access, next) { function unauthorized() { next(new Error('Invalid username/password for API')); - }; + } var user = basicAuth(access.req); if (!user || !user.name || !user.pass) { return unauthorized(); - }; + } if (user.name === apiAuth.username && user.pass === apiAuth.password) { return next(); } else { return unauthorized(); - }; + } }); // Log download myNuts.before('download', function(download, next) { - console.log('download', download.platform.filename, "for version", download.version.tag, "on channel", download.version.channel, "for", download.platform.type); + console.log('download', download.platform.filename, 'for version', download.version.tag, 'on channel', download.version.channel, 'for', download.platform.type); next(); }); myNuts.after('download', function(download, next) { - console.log('downloaded', download.platform.filename, "for version", download.version.tag, "on channel", download.version.channel, "for", download.platform.type); + console.log('downloaded', download.platform.filename, 'for version', download.version.tag, 'on channel', download.version.channel, 'for', download.platform.type); // Track on segment if enabled if (analytics) { @@ -92,7 +94,7 @@ app.use(myNuts.router); // Error handling app.use(function(req, res, next) { - res.status(404).send("Page not found"); + res.status(404).send('Page not found'); }); app.use(function(err, req, res, next) { var msg = err.message || err; diff --git a/lib/backends/github.js b/lib/backends/github.js index 3eaf155c..dae8209e 100644 --- a/lib/backends/github.js +++ b/lib/backends/github.js @@ -1,14 +1,13 @@ var _ = require('lodash'); var Q = require('q'); var util = require('util'); -var destroy = require('destroy'); var GitHub = require('octocat'); var request = require('request'); -var Buffer = require('buffer').Buffer; var githubWebhook = require('github-webhook-handler'); var Backend = require('./backend'); + function GitHubBackend() { var that = this; Backend.apply(this, arguments); @@ -17,19 +16,15 @@ function GitHubBackend() { proxyAssets: true }); - if ((!this.opts.username || !this.opts.password) && (!this.opts.token)) { - throw new Error('GitHub backend require "username" and "token" options'); - } - this.client = new GitHub({ - token: this.opts.token, + token: this.opts.token, endpoint: this.opts.endpoint, username: this.opts.username, password: this.opts.password }); this.ghrepo = this.client.repo(this.opts.repository); - this.releases = this.memoize(this._releases); + this.releases = this.memoize(this.releases); // GitHub webhook to refresh list of versions this.webhookHandler = githubWebhook({ @@ -45,15 +40,24 @@ function GitHubBackend() { } util.inherits(GitHubBackend, Backend); -// List all releases for this repository -GitHubBackend.prototype._releases = function() { +/** + * List all releases for this repository + * @return {Promise>} + */ +GitHubBackend.prototype.releases = function() { return this.ghrepo.releases() .then(function(page) { return page.all(); }); }; -// Return stream for an asset +/** + * Return stream for an asset + * @param {Asset} asset + * @param {Request} req + * @param {Response} res + * @return {Promise}? + */ GitHubBackend.prototype.serveAsset = function(asset, req, res) { if (!this.opts.proxyAssets) { res.redirect(asset.raw.browser_download_url); @@ -62,7 +66,11 @@ GitHubBackend.prototype.serveAsset = function(asset, req, res) { } }; -// Return stream for an asset +/** + * Return stream for an asset + * @param {Asset} asset + * @return {Promise} + */ GitHubBackend.prototype.getAssetStream = function(asset) { var headers = { 'User-Agent': 'nuts', @@ -88,5 +96,4 @@ GitHubBackend.prototype.getAssetStream = function(asset) { })); }; - module.exports = GitHubBackend; diff --git a/lib/index.js b/lib/index.js index bbe28f1c..1fd69e0e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,9 +1,35 @@ +var express = require('express'); + var Nuts = require('./nuts'); var platforms = require('./utils/platforms'); var winReleases = require('./utils/win-releases'); +/** + * Create an express application with Nuts binded to it. + * This is mostly used for unit testing. + * + * @param {Object} options + * @return {Express.Application} app + */ +function createApp(options) { + var app = express(); + var nuts = Nuts(options); + + app.use(nuts.router); + + app.use(function(err, req, res, next) { + res.status(err.statusCode || 500); + res.send({ + message: err.message + }); + }); + + return app; +} + module.exports = { - Nuts: Nuts, - platforms: platforms, - winReleases: winReleases + Nuts: Nuts, + platforms: platforms, + winReleases: winReleases, + createApp: createApp }; diff --git a/lib/nuts.js b/lib/nuts.js index 1642158b..42e9b092 100644 --- a/lib/nuts.js +++ b/lib/nuts.js @@ -5,12 +5,13 @@ var urljoin = require('urljoin.js'); var Understudy = require('understudy'); var express = require('express'); var useragent = require('express-useragent'); +var createError = require('http-errors'); -var BACKENDS = require('./backends'); var Versions = require('./versions'); var notes = require('./utils/notes'); var platforms = require('./utils/platforms'); var winReleases = require('./utils/win-releases'); +var BACKENDS = require('./backends'); var API_METHODS = require('./api'); function getFullUrl(req) { @@ -90,13 +91,22 @@ Nuts.prototype._init = function() { return that.backend.init(); }) .then(function() { - if (!that.opts.preFetch) return + if (!that.opts.preFetch) { + return; + } + return that.versions.list(); }); -} +}; -// Perform a hook using promised functions +/** + * Perform a hook using promised functions + * @param {String} name + * @param {Object} arg + * @param {Function} fn + * @return {Promise} + */ Nuts.prototype.performQ = function(name, arg, fn) { var that = this; fn = fn || function() { }; @@ -109,10 +119,17 @@ Nuts.prototype.performQ = function(name, arg, fn) { .then(function() { next(); }, next); - }) + }); }; -// Serve an asset to the response +/** + * Serve an asset to the response + * @param {Request} req + * @param {Response} res + * @param {Version} version + * @param {String} asset + * @return {Promise} + */ Nuts.prototype.serveAsset = function(req, res, version, asset) { var that = this; @@ -123,18 +140,18 @@ Nuts.prototype.serveAsset = function(req, res, version, asset) { version: version, platform: asset }, function() { - return that.backend.serveAsset(asset, req, res) + return that.backend.serveAsset(asset, req, res); }); }); }; // Handler for download routes Nuts.prototype.onDownload = function(req, res, next) { - var that = this; - var channel = req.params.channel; - var platform = req.params.platform; - var tag = req.params.tag || 'latest'; - var filename = req.params.filename; + var that = this; + var channel = req.params.channel; + var platform = req.params.platform; + var tag = req.params.tag || 'latest'; + var filename = req.params.filename; var filetypeWanted = req.query.filetype; // When serving a specific file, platform is not required @@ -147,13 +164,17 @@ Nuts.prototype.onDownload = function(req, res, next) { if (req.useragent.isLinux64) platform = platforms.LINUX_64; } - if (!platform) return next(new Error('No platform specified and impossible to detect one')); + if (!platform) { + return next(createError(400, 'No platform specified and impossible to detect one')); + } } else { platform = null; } // If specific version, don't enforce a channel - if (tag != 'latest') channel = '*'; + if (tag != 'latest') { + channel = '*'; + } this.versions.resolve({ channel: channel, @@ -186,7 +207,10 @@ Nuts.prototype.onDownload = function(req, res, next) { }); } - if (!asset) throw new Error("No download available for platform "+platform+" for version "+version.tag+" ("+(channel || "beta")+")"); + if (!asset) { + throw createError(404, 'No download available for platform ' + platform + + ' for version ' + version.tag + ' (' + (channel || 'beta') + ')'); + } // Call analytic middleware, then serve return that.serveAsset(req, res, version, asset); @@ -211,40 +235,52 @@ Nuts.prototype.onUpdateRedirect = function(req, res, next) { Nuts.prototype.onUpdate = function(req, res, next) { var that = this; var fullUrl = getFullUrl(req); + var platform = req.params.platform; - var channel = req.params.channel || '*'; + var channel = req.params.channel || 'stable'; var tag = req.params.version; - var filetype = req.query.filetype ? req.query.filetype : "zip"; + var filetype = req.query.filetype ? req.query.filetype : 'zip'; Q() .then(function() { - if (!tag) throw new Error('Requires "version" parameter'); - if (!platform) throw new Error('Requires "platform" parameter'); + if (!tag) { + throw createError(400, 'Requires "version" parameter'); + } + if (!platform) { + throw createError(400, 'Requires "platform" parameter'); + } platform = platforms.detect(platform); return that.versions.filter({ tag: '>='+tag, platform: platform, - channel: channel + channel: channel, + stripChannel: true }); }) .then(function(versions) { - var latest = _.first(versions); - if (!latest || latest.tag == tag) return res.status(204).send('No updates'); + var latest = versions[0]; - var notesSlice = versions.slice(0, -1); - if (versions.length === 1) { - notesSlice = [versions[0]]; + // Already using latest version? + if (!latest || latest.tag == tag) { + return res.status(204).send('No updates'); } + + // Extract release notes from all versions in range + var notesSlice = versions.length === 1? [versions[0]] : versions.slice(0, -1); var releaseNotes = notes.merge(notesSlice, { includeTag: false }); - console.error(latest.tag); - var gitFilePath = (channel === '*' ? '/../../../' : '/../../../../../'); - res.status(200).send({ - "url": urljoin(fullUrl, gitFilePath, '/download/version/'+latest.tag+'/'+platform+'?filetype='+filetype), - "name": latest.tag, - "notes": releaseNotes, - "pub_date": latest.published_at.toISOString() + + // URL for download should be absolute + var gitFilePath = (req.params.channel ? '/../../../../../' : '/../../../' ); + + res.status(200) + .send({ + 'url': urljoin(fullUrl, gitFilePath, + '/download/version/' + latest.tag + '/' + platform + '?filetype=' + filetype), + 'name': latest.tag, + 'notes': releaseNotes, + 'pub_date': latest.published_at.toISOString() }); }) .fail(next); @@ -274,16 +310,16 @@ Nuts.prototype.onUpdateWin = function(req, res, next) { .then(function(versions) { // Update needed? var latest = _.first(versions); - if (!latest) throw new Error("Version not found"); + if (!latest) throw new Error('Version not found'); // File exists var asset = _.find(latest.platforms, { filename: 'RELEASES' }); - if (!asset) throw new Error("File not found"); + if (!asset) throw new Error('File not found'); - return that.backend.readAsset(asset) - .then(function(content) { + return that.backend.readAsset(asset) + .then(function(content) { var releases = winReleases.parse(content.toString('utf-8')); releases = _.chain(releases) @@ -301,9 +337,9 @@ Nuts.prototype.onUpdateWin = function(req, res, next) { var output = winReleases.generate(releases); res.header('Content-Length', output.length); - res.attachment("RELEASES"); + res.attachment('RELEASES'); res.send(output); - }); + }); }) .fail(next); }; @@ -322,21 +358,19 @@ Nuts.prototype.onServeNotes = function(req, res, next) { }) .then(function(versions) { var latest = _.first(versions); - - if (!latest) throw new Error('No versions matching'); + if (!latest) { + throw new Error('No versions matching'); + } res.format({ - 'text/plain': function(){ - res.send(notes.merge(versions)); - }, 'application/json': function(){ res.send({ - "notes": notes.merge(versions, { includeTag: false }), - "pub_date": latest.published_at.toISOString() + 'notes': notes.merge(versions, { includeTag: false }), + 'pub_date': latest.published_at.toISOString() }); }, 'default': function() { - res.send(releaseNotes); + res.send(notes.merge(versions)); } }); }) @@ -390,5 +424,4 @@ Nuts.prototype.onAPIAccessControl = function(req, res, next) { }, next); }; - module.exports = Nuts; diff --git a/lib/versions.js b/lib/versions.js index 467d96a1..366c0c66 100644 --- a/lib/versions.js +++ b/lib/versions.js @@ -1,16 +1,24 @@ var _ = require('lodash'); -var Q = require('q'); var semver = require('semver'); +var createError = require('http-errors'); var platforms = require('./utils/platforms'); -// Normalize tag name +/** + * Normalize a tag name to remove the prefix "v" + * @param {String} tag + * @return {String} tag + */ function normalizeTag(tag) { if (tag[0] == 'v') tag = tag.slice(1); return tag; } -// Extract channel of version +/** + * Extract channel of tag name + * @param {String} tag + * @return {String} channel + */ function extractChannel(tag) { var suffix = tag.split('-')[1]; if (!suffix) return 'stable'; @@ -18,10 +26,25 @@ function extractChannel(tag) { return suffix.split('.')[0]; } -// Normalize a release to a version +/** + * Strip channel from a tag name + * @param {String} tag + * @return {String} tag + */ +function stripChannel(tag) { + return tag.split('-')[0]; +} + +/** + * Normalize a release to a version + * @param {Object} release + * @return {Version} version? + */ function normalizeVersion(release) { // Ignore draft - if (release.draft) return null; + if (release.draft) { + return null; + } var downloadCount = 0; var releasePlatforms = _.chain(release.assets) @@ -43,15 +66,20 @@ function normalizeVersion(release) { .value(); return { - tag: normalizeTag(release.tag_name).split('-')[0], - channel: extractChannel(release.tag_name), - notes: release.body || "", + tag: normalizeTag(release.tag_name), + channel: extractChannel(release.tag_name), + notes: release.body || '', published_at: new Date(release.published_at), - platforms: releasePlatforms + platforms: releasePlatforms }; } -// Compare two version +/** + * Compare two version + * @param {Version} v1 + * @param {Version} v2 + * @return {Number} + */ function compareVersions(v1, v2) { if (semver.gt(v1.tag, v2.tag)) { return -1; @@ -64,7 +92,6 @@ function compareVersions(v1, v2) { function Versions(backend) { this.backend = backend; - } // List versions normalized @@ -86,45 +113,74 @@ Versions.prototype.get = function(tag) { }); }; -// Filter versions with criterias +/** + * Filter versions with criterias + * @param {String} opts.tag? : tag name filter to satisfy (ex: ">=2.0.0") + * @param {String} opts.platform? : name of the platform supported by the version + * @param {String} opts.channel? : only list version of this channel ("*" for all) + * @param {Boolean} opts.stripChannel : compare tag name withotu the channel + * @return {Promise>} versions + */ Versions.prototype.filter = function(opts) { opts = _.defaults(opts || {}, { - tag: 'latest', - platform: null, - channel: 'stable' + tag: 'latest', + platform: null, + stripChannel: false, + channel: 'stable' }); - if (opts.platform) opts.platform = platforms.detect(opts.platform); + + if (opts.platform) { + opts.platform = platforms.detect(opts.platform); + } return this.list() .then(function(versions) { return _.chain(versions) .filter(function(version) { // Check channel - if (opts.channel != '*' && version.channel != opts.channel) return false; + if (opts.channel !== '*' && version.channel != opts.channel) { + return false; + } // Not available for requested paltform - if (opts.platform && !platforms.satisfies(opts.platform, _.pluck(version.platforms, 'type'))) return false; + if (opts.platform && !platforms.satisfies(opts.platform, _.pluck(version.platforms, 'type'))) { + return false; + } // Check tag satisfies request version - return opts.tag == 'latest' || semver.satisfies(version.tag, opts.tag); + var tagName = version.tag; + if (opts.stripChannel) { + tagName = stripChannel(tagName); + } + + return (opts.tag == 'latest' || semver.satisfies(tagName, opts.tag)); }) .value(); }); }; -// Resolve a platform, by filtering then taking the first result +/** + * Resolve a platform, by filtering then taking the first result + * @param {Object} opts + * @return {Promise} version + */ Versions.prototype.resolve = function(opts) { return this.filter(opts) .then(function(versions) { var version = _.first(versions); - if (!version) throw new Error('Version not found: '+opts.tag); + if (!version) { + throw createError(404, 'Version not found: ' + opts.tag); + } return version; }); }; -// List all channels from releases -Versions.prototype.channels = function(opts) { +/** + * List all channels from releases + * @return {Promise} + */ +Versions.prototype.channels = function() { return this.list() .then(function(versions) { var channels = {}; @@ -149,5 +205,4 @@ Versions.prototype.channels = function(opts) { }); }; - module.exports = Versions; diff --git a/package.json b/package.json index 3d1af7f9..8e036188 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "express-useragent": "0.1.9", "feed": "^0.3.0", "github-webhook-handler": "0.5.0", + "http-errors": "^1.5.0", "lodash": "3.7.0", "lru-diskcache": "1.1.1", "octocat": "0.10.2", @@ -27,8 +28,10 @@ "uuid": "2.0.1" }, "devDependencies": { + "expect": "^1.20.2", "mocha": "1.18.2", - "should": "7.0.4" + "should": "7.0.4", + "supertest": "^2.0.0" }, "bugs": { "url": "https://github.com/GitbookIO/nuts/issues" @@ -45,6 +48,6 @@ }, "scripts": { "start": "node bin/web.js", - "test": "mocha --reporter list" + "test": "mocha --bail --reporter spec --timeout 600000 ./test/all.js" } } diff --git a/test/all.js b/test/all.js new file mode 100644 index 00000000..77878c48 --- /dev/null +++ b/test/all.js @@ -0,0 +1,10 @@ +require('should'); + +// Sync tests +require('./platforms'); +require('./win-releases'); + +// Require a backend +require('./versions'); +require('./download'); +require('./update'); diff --git a/test/download.js b/test/download.js new file mode 100644 index 00000000..11c0ed09 --- /dev/null +++ b/test/download.js @@ -0,0 +1,57 @@ +var request = require('supertest'); +var app = require('./testing').app; + +var MAC_USERAGENT = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us)' + + ' AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19'; +var WIN_USERAGENT = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; Touch; rv:11.0) like Gecko'; + +describe('Download', function() { + var agent = request.agent(app); + + describe('Latest version (/)', function() { + + it('should fail if no user-agent to detect platform', function(done) { + agent + .get('/') + .expect(400, done); + }); + + it('should download windows file', function(done) { + agent + .get('/') + .set('User-Agent', WIN_USERAGENT) + .expect('Content-Length', '13') + .expect('Content-Disposition', 'attachment; filename=test.exe') + .expect(200, done); + }); + + it('should download OS X file as DMG', function(done) { + agent + .get('/') + .set('User-Agent', MAC_USERAGENT) + .expect('Content-Length', '19') + .expect('Content-Disposition', 'attachment; filename=test-osx.dmg') + .expect(200, done); + }); + + }); + + describe('Previous version (/download/version/)', function() { + it('should not have a windows file to download', function(done) { + agent + .get('/download/version/0.9.0') + .set('User-Agent', WIN_USERAGENT) + .expect(404, done); + }); + + it('should download OS X file as DMG', function(done) { + agent + .get('/download/version/0.9.0') + .set('User-Agent', MAC_USERAGENT) + .expect('Content-Length', '19') + .expect('Content-Disposition', 'attachment; filename=test-osx.dmg') + .expect(200, done); + }); + }); + +}); diff --git a/test/platforms.js b/test/platforms.js index 7aadaecb..446c105c 100644 --- a/test/platforms.js +++ b/test/platforms.js @@ -1,4 +1,3 @@ -require('should'); var platforms = require('../lib/utils/platforms'); describe('Platforms', function() { diff --git a/test/testing.js b/test/testing.js new file mode 100644 index 00000000..f5a0557c --- /dev/null +++ b/test/testing.js @@ -0,0 +1,14 @@ +var nuts = require('../lib'); + +var config = { + repository: 'SamyPesse/nuts-testing', + token: process.env.GITHUB_TOKEN +}; + +var instance = nuts.Nuts(config); +var app = nuts.createApp(config); + +module.exports = { + app: app, + nuts: instance +}; diff --git a/test/update.js b/test/update.js new file mode 100644 index 00000000..f8603bed --- /dev/null +++ b/test/update.js @@ -0,0 +1,92 @@ +var request = require('supertest'); +var expect = require('expect'); + +var app = require('./testing').app; + +describe('Update', function() { + var agent = request.agent(app); + + describe('Squirrel.Mac (OS X)', function() { + + describe('/update/osx/', function() { + it('should return a 204 if using latest version', function(done) { + agent + .get('/update/osx/1.0.0') + .expect(204, done); + }); + + it('should return a 200 with json if using old stable version', function(done) { + agent + .get('/update/osx/0.9.0') + .expect('Content-Type', /json/) + .expect(function(res) { + expect(res.body.name).toBe('1.0.0'); + expect(res.body.url).toExist(); + expect(res.body.pub_date).toExist(); + }) + .expect(200, done); + }); + + it('should return a 200 with json if using old beta version (1)', function(done) { + agent + .get('/update/osx/0.9.1-beta') + .expect('Content-Type', /json/) + .expect(function(res) { + expect(res.body.name).toBe('1.0.0'); + }) + .expect(200, done); + }); + + it('should return a 200 with json if using old beta version (2)', function(done) { + agent + .get('/update/osx/0.9.1-beta.1') + .expect('Content-Type', /json/) + .expect(function(res) { + expect(res.body.name).toBe('1.0.0'); + }) + .expect(200, done); + }); + + it('should return a 200 with json if using old alpha version (1)', function(done) { + agent + .get('/update/osx/0.9.2-alpha.2') + .expect('Content-Type', /json/) + .expect(function(res) { + expect(res.body.name).toBe('1.0.0'); + }) + .expect(200, done); + }); + }); + + describe('/update/channel/beta/osx', function() { + it('should update from 0.9.1-beta to 1.0.1-beta.0', function(done) { + agent + .get('/update/channel/beta/osx/0.9.1-beta') + .expect('Content-Type', /json/) + .expect(function(res) { + expect(res.body.name).toBe('1.0.1-beta.0'); + }) + .expect(200, done); + }); + + it('should not update from 1.0.1-beta.0', function(done) { + agent + .get('/update/channel/beta/osx/1.0.1-beta.0') + .expect(204, done); + }); + }); + + describe('/update/channel/alpha/osx', function() { + it('should update from 0.9.1-beta to 1.1.0-alpha.0', function(done) { + agent + .get('/update/channel/alpha/osx/0.9.1-beta') + .expect('Content-Type', /json/) + .expect(function(res) { + expect(res.body.name).toBe('1.1.0-alpha.0'); + }) + .expect(200, done); + }); + }); + }); + +}); diff --git a/test/versions.js b/test/versions.js new file mode 100644 index 00000000..7f99805f --- /dev/null +++ b/test/versions.js @@ -0,0 +1,39 @@ +var expect = require('expect'); +var versions = require('./testing').nuts.versions; + +describe('Versions', function() { + + describe('.list', function() { + it('should list all versions', function() { + return versions.list() + .then(function(out) { + expect(out.length).toEqual(7); + }); + }); + }); + + describe('.filter', function() { + + it('should filter correctly by tag name', function() { + return versions.filter({ tag: '>=0.9.0' }) + .then(function(out) { + expect(out.length).toEqual(2); + expect(out[0].tag).toEqual('1.0.0'); + expect(out[1].tag).toEqual('0.9.0'); + }); + }); + + it('should filter correctly by tag name (stripChannel)', function() { + return versions.filter({ + tag: '>=1.0.0', + channel: '*', + stripChannel: true + }) + .then(function(out) { + expect(out.length).toEqual(3); + }); + }); + + }); + +}); diff --git a/test/win-releases.js b/test/win-releases.js index e06fac03..70b72da4 100644 --- a/test/win-releases.js +++ b/test/win-releases.js @@ -1,4 +1,3 @@ -require('should'); var winReleases = require('../lib/utils/win-releases'); describe('Windows RELEASES', function() {