diff --git a/NEWS.md b/NEWS.md index 3eeebc0a..cb050f75 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,11 @@ ## Unreleased +### Changed + +* Promise support for `packager` - function returns a Promise instead of the return value of the + callback (#658) + ## [8.7.0] - 2017-05-01 ### Added diff --git a/docs/api.md b/docs/api.md index c9307757..710cc0b2 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,12 +1,20 @@ # electron-packager API -Short example: +Short [callback](#callback) example: ```javascript var packager = require('electron-packager') packager(options, function done_callback (err, appPaths) { /* … */ }) ``` +Short Promise example: + +```javascript +const packager = require('electron-packager') +packager(options) + .then((appPaths) => { /* … */ }) +``` + ## `options` ### Required diff --git a/index.js b/index.js index 671190d8..5d1eef30 100644 --- a/index.js +++ b/index.js @@ -8,6 +8,7 @@ const getMetadataFromPackageJSON = require('./infer') const ignore = require('./ignore') const metadata = require('./package.json') const path = require('path') +const pify = require('pify') const series = require('run-series') const targets = require('./targets') @@ -102,7 +103,9 @@ function createSeries (opts, archs, platforms) { function checkOverwrite () { var finalPath = common.generateFinalPath(comboOpts) - fs.exists(finalPath, function (exists) { + fs.pathExists(finalPath, function (err, exists) { + if (err) return callback(err) + if (exists) { if (opts.overwrite) { fs.remove(finalPath, function () { @@ -133,7 +136,7 @@ function createSeries (opts, archs, platforms) { })) } -module.exports = function packager (opts, cb) { +module.exports = pify(function packager (opts, cb) { debugHostInfo() if (debug.enabled) debug(`Packager Options: ${JSON.stringify(opts)}`) @@ -170,4 +173,4 @@ module.exports = function packager (opts, cb) { })) }) }) -} +}) diff --git a/package.json b/package.json index 5e7bc78d..894b9d7d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "fs-extra": "^3.0.0", "get-package-info": "^1.0.0", "minimist": "^1.1.1", + "pify": "^3.0.0", "plist": "^2.0.0", "rcedit": "^0.9.0", "resolve": "^1.1.6", @@ -39,13 +40,13 @@ "eslint-config-standard": "^10.0.0", "eslint-plugin-import": "^2.2.0", "eslint-plugin-node": "^4.2.2", - "eslint-plugin-promise": "^3.0.0", + "eslint-plugin-promise": "^3.5.0", "eslint-plugin-standard": "^3.0.0", "eslint-plugin-tape": "^1.1.0", + "mz": "^2.6.0", "nyc": "^10.0.0", "pkg-up": "^2.0.0", "rimraf": "^2.3.2", - "run-waterfall": "^1.1.1", "tape": "^4.0.0", "which": "^1.2.14" }, @@ -64,6 +65,7 @@ "keywords": [], "eslintConfig": { "extends": [ + "plugin:promise/recommended", "plugin:tape/recommended", "standard" ], @@ -71,6 +73,7 @@ "sourceType": "script" }, "plugins": [ + "promise", "tape" ], "rules": { diff --git a/test/asar.js b/test/asar.js index 80f30781..e3ef47ea 100644 --- a/test/asar.js +++ b/test/asar.js @@ -7,10 +7,9 @@ const packager = require('..') const path = require('path') const test = require('tape') const util = require('./util') -const waterfall = require('run-waterfall') function createDefaultAppAsarTest (opts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) opts.name = 'el0374Test' @@ -19,24 +18,19 @@ function createDefaultAppAsarTest (opts) { var resourcesPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { resourcesPath = path.join(paths[0], util.generateResourcesPath(opts)) - fs.exists(path.join(resourcesPath, 'default_app.asar'), function (exists) { - t.false(exists, 'The output directory should not contain the Electron default_app.asar file') - cb() - }) - } - ], function (err) { - t.end(err) - }) + return fs.pathExists(path.join(resourcesPath, 'default_app.asar')) + }).then(exists => { + t.false(exists, 'The output directory should not contain the Electron default_app.asar file') + return t.end() + }).catch(t.end) } } function createAsarTest (opts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) opts.name = 'basicTest' @@ -45,65 +39,50 @@ function createAsarTest (opts) { 'unpack': '*.pac', 'unpackDir': 'dir_to_unpack' } - var finalPath - var resourcesPath + let finalPath + let resourcesPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { finalPath = paths[0] - fs.stat(finalPath, cb) - }, function (stats, cb) { + return fs.stat(finalPath) + }).then(stats => { t.true(stats.isDirectory(), 'The expected output directory should exist') resourcesPath = path.join(finalPath, util.generateResourcesPath(opts)) - fs.stat(resourcesPath, cb) - }, function (stats, cb) { + return fs.stat(resourcesPath) + }).then(stats => { t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory') - fs.stat(path.join(resourcesPath, 'app.asar'), cb) - }, function (stats, cb) { + return fs.stat(path.join(resourcesPath, 'app.asar')) + }).then(stats => { t.true(stats.isFile(), 'app.asar should exist under the resources subdirectory when opts.asar is true') - fs.exists(path.join(resourcesPath, 'app'), function (exists) { - t.false(exists, 'app subdirectory should NOT exist when app.asar is built') - }) - fs.stat(path.join(resourcesPath, 'app.asar.unpacked'), cb) - }, function (stats, cb) { + return fs.pathExists(path.join(resourcesPath, 'app')) + }).then(exists => { + t.false(exists, 'app subdirectory should NOT exist when app.asar is built') + return fs.stat(path.join(resourcesPath, 'app.asar.unpacked')) + }).then(stats => { t.true(stats.isDirectory(), 'app.asar.unpacked should exist under the resources subdirectory when opts.asar_unpack is set some expression') - fs.stat(path.join(resourcesPath, 'app.asar.unpacked', 'dir_to_unpack'), cb) - }, function (stats, cb) { + return fs.stat(path.join(resourcesPath, 'app.asar.unpacked', 'dir_to_unpack')) + }).then(stats => { t.true(stats.isDirectory(), 'dir_to_unpack should exist under app.asar.unpacked subdirectory when opts.asar-unpack-dir is set dir_to_unpack') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } -test('asar argument test: asar is not set', function (t) { - var opts = {} - - var asarOpts = common.createAsarOpts(opts) +test('asar argument test: asar is not set', (t) => { + const asarOpts = common.createAsarOpts({}) t.false(asarOpts, 'createAsarOpts returns false') t.end() }) -test('asar argument test: asar is true', function (t) { - var opts = { - asar: true - } - - var asarOpts = common.createAsarOpts(opts) +test('asar argument test: asar is true', (t) => { + const asarOpts = common.createAsarOpts({asar: true}) t.same(asarOpts, {}) t.end() }) -test('asar argument test: asar is not an Object or a bool', function (t) { - var opts = { - asar: 'string' - } - - var asarOpts = common.createAsarOpts(opts) +test('asar argument test: asar is not an Object or a bool', (t) => { + const asarOpts = common.createAsarOpts({asar: 'string'}) t.false(asarOpts, 'createAsarOpts returns false') t.end() }) diff --git a/test/basic.js b/test/basic.js index 5cb2e5d4..2d41f262 100644 --- a/test/basic.js +++ b/test/basic.js @@ -8,7 +8,6 @@ const path = require('path') const targets = require('../targets') const test = require('tape') const util = require('./util') -const waterfall = require('run-waterfall') // Generates a path to the generated app that reflects the name given in the options. // Returns the Helper.app location on darwin since the top-level .app is already tested for the @@ -22,7 +21,7 @@ function generateNamePath (opts) { } function createDefaultsTest (opts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) opts.name = 'defaultsTest' @@ -39,55 +38,46 @@ function createDefaultsTest (opts) { var finalPath var resourcesPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { t.true(Array.isArray(paths), 'packager call should resolve to an array') t.equal(paths.length, 1, 'Single-target run should resolve to a 1-item array') finalPath = paths[0] t.equal(finalPath, path.join(util.getWorkCwd(), common.generateFinalBasename(defaultOpts)), 'Path should follow the expected format and be in the cwd') - fs.stat(finalPath, cb) - }, function (stats, cb) { + return fs.stat(finalPath) + }).then(stats => { t.true(stats.isDirectory(), 'The expected output directory should exist') resourcesPath = path.join(finalPath, util.generateResourcesPath(defaultOpts)) - fs.stat(path.join(finalPath, generateNamePath(defaultOpts)), cb) - }, function (stats, cb) { + return fs.stat(path.join(finalPath, generateNamePath(defaultOpts))) + }).then(stats => { if (common.isPlatformMac(defaultOpts.platform)) { t.true(stats.isDirectory(), 'The Helper.app should reflect opts.name') } else { t.true(stats.isFile(), 'The executable should reflect opts.name') } - fs.stat(resourcesPath, cb) - }, function (stats, cb) { + return fs.stat(resourcesPath) + }).then(stats => { t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory') - fs.exists(path.join(resourcesPath, 'app', 'node_modules', 'run-waterfall'), (exists) => { - t.false(exists, 'The output directory should NOT contain devDependencies by default (prune=true)') - util.areFilesEqual(path.join(opts.dir, 'main.js'), path.join(resourcesPath, 'app', 'main.js'), cb) - }) - }, function (equal, cb) { + return fs.pathExists(path.join(resourcesPath, 'app', 'node_modules', 'run-waterfall')) + }).then(exists => { + t.false(exists, 'The output directory should NOT contain devDependencies by default (prune=true)') + return util.areFilesEqual(path.join(opts.dir, 'main.js'), path.join(resourcesPath, 'app', 'main.js')) + }).then(equal => { t.true(equal, 'File under packaged app directory should match source file') - util.areFilesEqual(path.join(opts.dir, 'ignore', 'this.txt'), - path.join(resourcesPath, 'app', 'ignore', 'this.txt'), - cb) - }, function (equal, cb) { - t.true(equal, - 'File under subdirectory of packaged app directory should match source file and not be ignored by default') - fs.exists(path.join(resourcesPath, 'default_app'), function (exists) { - t.false(exists, 'The output directory should not contain the Electron default app directory') - cb() - }) - }, function (cb) { - fs.exists(path.join(resourcesPath, 'default_app.asar'), function (exists) { - t.false(exists, 'The output directory should not contain the Electron default app asar file') - cb() - }) - } - ], function (err) { - t.end(err) - }) + return util.areFilesEqual(path.join(opts.dir, 'ignore', 'this.txt'), + path.join(resourcesPath, 'app', 'ignore', 'this.txt')) + }).then(equal => { + t.true(equal, 'File under subdirectory of packaged app directory should match source file and not be ignored by default') + return fs.pathExists(path.join(resourcesPath, 'default_app')) + }).then(exists => { + t.false(exists, 'The output directory should not contain the Electron default app directory') + return fs.pathExists(path.join(resourcesPath, 'default_app.asar')) + }).then(exists => { + t.false(exists, 'The output directory should not contain the Electron default app asar file') + return t.end() + }).catch(t.end) } } @@ -101,29 +91,24 @@ function createOutTest (opts) { var finalPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { finalPath = paths[0] t.equal(finalPath, path.join('dist', common.generateFinalBasename(opts)), 'Path should follow the expected format and be under the folder specified in `out`') - fs.stat(finalPath, cb) - }, function (stats, cb) { + return fs.stat(finalPath) + }).then(stats => { t.true(stats.isDirectory(), 'The expected output directory should exist') - fs.stat(path.join(finalPath, util.generateResourcesPath(opts)), cb) - }, function (stats, cb) { + return fs.stat(path.join(finalPath, util.generateResourcesPath(opts))) + }).then(stats => { t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createOverwriteTest (opts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout * 2) // Multiplied since this test packages the application twice opts.name = 'overwriteTest' @@ -132,41 +117,32 @@ function createOverwriteTest (opts) { var finalPath var testPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { finalPath = paths[0] - fs.stat(finalPath, cb) - }, function (stats, cb) { + return fs.stat(finalPath) + }).then(stats => { t.true(stats.isDirectory(), 'The expected output directory should exist') // Create a dummy file to detect whether the output directory is replaced in subsequent runs testPath = path.join(finalPath, 'test.txt') - fs.writeFile(testPath, 'test', cb) - }, function (cb) { - // Run again, defaulting to overwrite false - packager(opts, cb) - }, function (paths, cb) { - fs.stat(testPath, cb) - }, function (stats, cb) { + return fs.writeFile(testPath, 'test') + }).then(() => packager(opts)) // Run again, defaulting to overwrite false + .then(paths => fs.stat(testPath)) + .then(stats => { t.true(stats.isFile(), 'The existing output directory should exist as before (skipped by default)') // Run a third time, explicitly setting overwrite to true opts.overwrite = true - packager(opts, cb) - }, function (paths, cb) { - fs.exists(testPath, function (exists) { - t.false(exists, 'The output directory should be regenerated when overwrite is true') - cb() - }) - } - ], function (err) { - t.end(err) - }) + return packager(opts) + }).then(paths => fs.pathExists(testPath)) + .then(exists => { + t.false(exists, 'The output directory should be regenerated when overwrite is true') + return t.end() + }).catch(t.end) } } function createTmpdirTest (opts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) opts.name = 'tmpdirTest' @@ -174,24 +150,17 @@ function createTmpdirTest (opts) { opts.out = 'dist' opts.tmpdir = path.join(util.getWorkCwd(), 'tmp') - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { - fs.stat(path.join(opts.tmpdir, 'electron-packager'), cb) - }, - function (stats, cb) { + packager(opts) + .then(paths => fs.stat(path.join(opts.tmpdir, 'electron-packager'))) + .then(stats => { t.true(stats.isDirectory(), 'The expected temp directory should exist') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createDisableTmpdirUsingTest (opts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) opts.name = 'disableTmpdirTest' @@ -199,24 +168,17 @@ function createDisableTmpdirUsingTest (opts) { opts.out = 'dist' opts.tmpdir = false - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { - fs.stat(paths[0], cb) - }, - function (stats, cb) { + packager(opts) + .then(paths => fs.stat(paths[0])) + .then(stats => { t.true(stats.isDirectory(), 'The expected out directory should exist') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createDisableSymlinkDereferencingTest (opts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) opts.name = 'disableSymlinkDerefTest' @@ -225,28 +187,31 @@ function createDisableSymlinkDereferencingTest (opts) { opts.derefSymlinks = false opts.asar = false - var dst = path.join(opts.dir, 'main-link.js') - - waterfall([ - function (cb) { - var src = path.join(opts.dir, 'main.js') - fs.ensureSymlink(src, dst, cb) - }, function (cb) { - packager(opts, cb) - }, function (paths, cb) { - var dstLink = path.join(paths[0], 'resources', 'app', 'main-link.js') - fs.lstat(dstLink, cb) - }, - function (stats, cb) { + const dest = path.join(opts.dir, 'main-link.js') + + const src = path.join(opts.dir, 'main.js') + fs.ensureSymlink(src, dest) + .then(() => packager(opts)) + .then(paths => { + const destLink = path.join(paths[0], 'resources', 'app', 'main-link.js') + return fs.lstat(destLink) + }).then(stats => { t.true(stats.isSymbolicLink(), 'The expected file should still be a symlink.') - cb() - }, - function (cb) { - fs.remove(dst, cb) - } - ], function (err) { - t.end(err) - }) + return fs.remove(dest) + }).then(t.end).catch(t.end) + } +} + +function invalidOptionTest (opts) { + return (t) => { + return packager(opts) + .then( + paths => t.end('no paths returned'), + (err) => { + t.ok(err, 'error thrown') + return t.end() + } + ) } } @@ -324,10 +289,14 @@ test('cannot build apps where the name ends in " Helper"', (t) => { platform: 'linux' } - packager(opts, (err) => { - t.equal('Application names cannot end in " Helper" due to limitations on macOS', err.message) - t.end() - }) + return packager(opts) + .then( + () => t.end('should not finish'), + (err) => { + t.equal(err.message, 'Application names cannot end in " Helper" due to limitations on macOS') + t.end() + } + ) }) util.testSinglePlatform('defaults test', createDefaultsTest) @@ -338,7 +307,7 @@ util.testSinglePlatform('disable tmpdir test', createDisableTmpdirUsingTest) util.testSinglePlatform('deref symlink test', createDisableSymlinkDereferencingTest) util.packagerTest('building for Linux target sanitizes binary name', (t) => { - let opts = { + const opts = { name: '@username/package-name', dir: path.join(__dirname, 'fixtures', 'el-0374'), electronVersion: '0.37.4', @@ -346,62 +315,34 @@ util.packagerTest('building for Linux target sanitizes binary name', (t) => { platform: 'linux' } - waterfall([ - (cb) => { - packager(opts, cb) - }, (paths, cb) => { + packager(opts) + .then(paths => { t.equal(1, paths.length, '1 bundle created') - fs.stat(path.join(paths[0], '@username-package-name'), cb) - }, (stats, cb) => { + return fs.stat(path.join(paths[0], '@username-package-name')) + }).then(stats => { t.true(stats.isFile(), 'The sanitized binary filename should exist') - cb() - } - ], (err) => { - t.end(err) - }) + return t.end() + }).catch(t.end) }) -util.packagerTest('fails with invalid arch', (t) => { - let opts = { - arch: 'z80', - platform: 'linux' - } - packager(opts, (err, paths) => { - t.equal(undefined, paths, 'no paths returned') - t.ok(err, 'error thrown') - t.end() - }) -}) - -util.packagerTest('fails with invalid platform', (t) => { - let opts = { - arch: 'ia32', - platform: 'dos' +util.packagerTest('fails with invalid arch', invalidOptionTest({ + arch: 'z80', + platform: 'linux' +})) +util.packagerTest('fails with invalid platform', invalidOptionTest({ + arch: 'ia32', + platform: 'dos' +})) +util.packagerTest('fails with invalid version', invalidOptionTest({ + name: 'invalidElectronTest', + dir: path.join(__dirname, 'fixtures', 'el-0374'), + electronVersion: '0.0.1', + arch: 'x64', + platform: 'linux', + download: { + quiet: !!process.env.CI } - packager(opts, (err, paths) => { - t.equal(undefined, paths, 'no paths returned') - t.ok(err, 'error thrown') - t.end() - }) -}) - -util.packagerTest('fails with invalid version', (t) => { - let opts = { - name: 'invalidElectronTest', - dir: path.join(__dirname, 'fixtures', 'el-0374'), - electronVersion: '0.0.1', - arch: 'x64', - platform: 'linux', - download: { - quiet: !!process.env.CI - } - } - packager(opts, (err, paths) => { - t.equal(undefined, paths, 'no paths returned') - t.ok(err, 'error thrown') - t.end() - }) -}) +})) util.packagerTest('electronVersion overrides deprecated version', (t) => { const opts = { @@ -429,15 +370,17 @@ util.packagerTest('version used if electronVersion not set', (t) => { }) util.packagerTest('dir argument test: should work with relative path', (t) => { - let opts = { + const opts = { name: 'ElectronTest', dir: path.join('..', 'fixtures', 'el-0374'), electronVersion: '0.37.4', arch: 'x64', platform: 'linux' } - packager(opts, (err, paths) => { - t.equal(path.join(__dirname, 'work', 'ElectronTest-linux-x64'), paths[0], 'paths returned') - t.end(err) - }) + + packager(opts) + .then(paths => { + t.equal(path.join(__dirname, 'work', 'ElectronTest-linux-x64'), paths[0], 'paths returned') + return t.end() + }).catch(t.end) }) diff --git a/test/hooks.js b/test/hooks.js index 8b2a3123..5f164b1a 100644 --- a/test/hooks.js +++ b/test/hooks.js @@ -3,7 +3,6 @@ const config = require('./config.json') const packager = require('..') const util = require('./util') -const waterfall = require('run-waterfall') function createHookTest (hookName) { util.packagerTest('platform=all test (one arch) (' + hookName + ' hook)', (t) => { @@ -25,20 +24,15 @@ function createHookTest (hookName) { callback() }] - waterfall([ - function (cb) { - packager(opts, cb) - }, function (finalPaths, cb) { + packager(opts) + .then(finalPaths => { t.equal(finalPaths.length, 2, 'packager call should resolve with expected number of paths') - t.true(hookCalled, hookName + ' methods should have been called') - util.verifyPackageExistence(finalPaths, cb) - }, function (exists, cb) { + t.true(hookCalled, `${hookName} methods should have been called`) + return util.verifyPackageExistence(finalPaths) + }).then(exists => { t.true(exists, 'Packages should be generated for both 32-bit platforms') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) }) } diff --git a/test/ignore.js b/test/ignore.js index 8ee62a17..29d3b08b 100644 --- a/test/ignore.js +++ b/test/ignore.js @@ -6,10 +6,8 @@ const fs = require('fs-extra') const ignore = require('../ignore') const path = require('path') const packager = require('..') -const series = require('run-series') const test = require('tape') const util = require('./util') -const waterfall = require('run-waterfall') function createIgnoreTest (opts, ignorePattern, ignoredFile) { return (t) => { @@ -21,24 +19,19 @@ function createIgnoreTest (opts, ignorePattern, ignoredFile) { opts.ignore = ignorePattern } - var appPath + let appPath - waterfall([ - (cb) => { - packager(opts, cb) - }, (paths, cb) => { + packager(opts) + .then(paths => { appPath = path.join(paths[0], util.generateResourcesPath(opts), 'app') - fs.stat(path.join(appPath, 'package.json'), cb) - }, (stats, cb) => { - t.true(stats.isFile(), 'The expected output directory should exist and contain files') - fs.exists(path.join(appPath, ignoredFile), exists => { - t.false(exists, 'Ignored file should not exist in output app directory') - cb() - }) - } - ], (err) => { - t.end(err) - }) + return fs.pathExists(path.join(appPath, 'package.json')) + }).then(exists => { + t.true(exists, 'The expected output directory should exist and contain files') + return fs.pathExists(path.join(appPath, ignoredFile)) + }).then(exists => { + t.false(exists, 'Ignored file should not exist in output app directory') + return t.end() + }).catch(t.end) } } @@ -54,37 +47,23 @@ function createIgnoreOutDirTest (opts, distPath) { var outDir = opts.dir + path.sep + distPath opts.out = outDir - series([ - (cb) => { - fs.copy(util.fixtureSubdir('basic'), appDir, { - dereference: true, - stopOnErr: true, - filter: file => { return path.basename(file) !== 'node_modules' } - }, cb) - }, - (cb) => { - // create out dir before packager (real world issue - when second run includes unignored out dir) - fs.mkdirp(outDir, cb) - }, - (cb) => { - // create file to ensure that directory will be not ignored because empty - fs.open(path.join(outDir, 'ignoreMe'), 'w', (err, fd) => { - if (err) return cb(err) - fs.close(fd, cb) - }) - }, - (cb) => { - packager(opts, cb) - }, - (cb) => { - fs.exists(path.join(outDir, common.generateFinalBasename(opts), util.generateResourcesPath(opts), 'app', path.basename(outDir)), (exists) => { - t.false(exists, 'Out dir must not exist in output app directory') - cb() - }) - } - ], (err) => { - t.end(err) - }) + fs.copy(util.fixtureSubdir('basic'), appDir, { + dereference: true, + stopOnErr: true, + filter: file => { return path.basename(file) !== 'node_modules' } + }).then(() => { + // create out dir before packager (real world issue - when second run includes unignored out dir) + return fs.mkdirp(outDir) + }).then(() => { + // create file to ensure that directory will be not ignored because empty + return fs.open(path.join(outDir, 'ignoreMe'), 'w') + }).then(fd => fs.close(fd)) + .then(() => packager(opts)) + .then(() => fs.pathExists(path.join(outDir, common.generateFinalBasename(opts), util.generateResourcesPath(opts), 'app', path.basename(outDir)))) + .then(exists => { + t.false(exists, 'Out dir must not exist in output app directory') + return t.end() + }).catch(t.end) } } @@ -101,37 +80,23 @@ function createIgnoreImplicitOutDirTest (opts) { var testFilename = 'ignoreMe' var previousPackedResultDir - series([ - (cb) => { - fs.copy(util.fixtureSubdir('basic'), appDir, { - dereference: true, - stopOnErr: true, - filter: file => { return path.basename(file) !== 'node_modules' } - }, cb) - }, - (cb) => { - previousPackedResultDir = path.join(outDir, `${common.sanitizeAppName(opts.name)}-linux-ia32`) - fs.mkdirp(previousPackedResultDir, cb) - }, - (cb) => { - // create file to ensure that directory will be not ignored because empty - fs.open(path.join(previousPackedResultDir, testFilename), 'w', (err, fd) => { - if (err) return cb(err) - fs.close(fd, cb) - }) - }, - (cb) => { - packager(opts, cb) - }, - (cb) => { - fs.exists(path.join(outDir, common.generateFinalBasename(opts), util.generateResourcesPath(opts), 'app', testFilename), (exists) => { - t.false(exists, 'Out dir must not exist in output app directory') - cb() - }) - } - ], (err) => { - t.end(err) - }) + fs.copy(util.fixtureSubdir('basic'), appDir, { + dereference: true, + stopOnErr: true, + filter: file => { return path.basename(file) !== 'node_modules' } + }).then(() => { + previousPackedResultDir = path.join(outDir, `${common.sanitizeAppName(opts.name)}-linux-ia32`) + return fs.mkdirp(previousPackedResultDir) + }).then(() => { + // create file to ensure that directory will be not ignored because empty + return fs.open(path.join(previousPackedResultDir, testFilename), 'w') + }).then(fd => fs.close(fd)) + .then(() => packager(opts)) + .then(() => fs.pathExists(path.join(outDir, common.generateFinalBasename(opts), util.generateResourcesPath(opts), 'app', testFilename))) + .then(exists => { + t.false(exists, 'Out dir must not exist in output app directory') + return t.end() + }).catch(t.end) } } diff --git a/test/index.js b/test/index.js index 3f51dd30..c443d643 100644 --- a/test/index.js +++ b/test/index.js @@ -1,8 +1,7 @@ 'use strict' const config = require('./config.json') -const exec = require('child_process').exec -const series = require('run-series') +const exec = require('mz/child_process').exec const util = require('./util') // Download all Electron distributions before running tests to avoid timing out due to network @@ -14,51 +13,47 @@ function preDownloadElectron () { '0.37.4', '1.3.1' ] - return versions.map((version) => { - return (cb) => { - console.log(`Calling electron-download for ${version} before running tests...`) - util.downloadAll(version, cb) - } - }) + return Promise.all(versions.map(util.downloadAll)) +} + +function npmInstallForFixture (fixture) { + console.log(`Running npm install in fixtures/${fixture}...`) + return exec('npm install --no-bin-links', {cwd: util.fixtureSubdir(fixture)}) + .catch((err) => console.error(err)) } -function npmInstallforFixtures () { +function npmInstallForFixtures () { const fixtures = [ 'basic', 'basic-renamed-to-electron', 'infer-missing-version-only', 'el-0374' ] - return fixtures.map((fixture) => { - return (cb) => { - console.log(`Running npm install in fixtures/${fixture}...`) - exec('npm install --no-bin-links', {cwd: util.fixtureSubdir(fixture)}, cb) - } - }) + return Promise.all(fixtures.map(npmInstallForFixture)) } -let setupFuncs = preDownloadElectron().concat(npmInstallforFixtures()) +preDownloadElectron() + .then(npmInstallForFixtures) + .then(() => { + console.log('Running tests...') + require('./basic') + require('./asar') + require('./cli') + require('./ignore') + require('./infer') + require('./hooks') + require('./multitarget') + require('./prune') + require('./win32') -series(setupFuncs, (error) => { - if (error) { + if (process.platform !== 'win32') { + // Perform additional tests specific to building for OS X + require('./darwin') + require('./mas') + } + + return true + }).catch((error) => { console.error(error.stack || error) return process.exit(1) - } - - console.log('Running tests...') - require('./basic') - require('./asar') - require('./cli') - require('./ignore') - require('./infer') - require('./hooks') - require('./multitarget') - require('./prune') - require('./win32') - - if (process.platform !== 'win32') { - // Perform additional tests specific to building for OS X - require('./darwin') - require('./mas') - } -}) + }) diff --git a/test/infer.js b/test/infer.js index 66a61108..f58ceeab 100644 --- a/test/infer.js +++ b/test/infer.js @@ -6,9 +6,9 @@ const getMetadataFromPackageJSON = require('../infer') const os = require('os') const packager = require('..') const path = require('path') +const pify = require('pify') const pkgUp = require('pkg-up') const util = require('./util') -const waterfall = require('run-waterfall') function createInferElectronVersionTest (fixture, packageName) { return (opts) => { @@ -19,77 +19,64 @@ function createInferElectronVersionTest (fixture, packageName) { delete opts.electronVersion opts.dir = path.join(__dirname, 'fixtures', fixture) - waterfall([ - (cb) => { - getMetadataFromPackageJSON(opts, opts.dir, cb) - }, (cb) => { - fs.readFile(path.join(opts.dir, 'package.json'), cb) - }, (pkg, cb) => { - const packageJSON = JSON.parse(pkg) + pify(getMetadataFromPackageJSON)(opts, opts.dir) + .then((pkg) => { + const packageJSON = require(path.join(opts.dir, 'package.json')) t.equal(opts.electronVersion, packageJSON.devDependencies[packageName], `The version should be inferred from installed ${packageName} version`) - cb() - } - ], (err) => { - t.end(err) - }) + return t.end() + }).catch(t.end) } } } -function copyFixtureToTempDir (fixtureSubdir, cb) { +function copyFixtureToTempDir (fixtureSubdir) { let tmpdir = path.join(os.tmpdir(), fixtureSubdir) let fixtureDir = path.join(__dirname, 'fixtures', fixtureSubdir) - waterfall([ - cb => { - let tmpdirPkg = pkgUp.sync(path.join(tmpdir, '..')) - if (tmpdirPkg) return cb(new Error(`Found package.json in parent of temp directory, which will interfere with test results. Please remove package.json at ${tmpdirPkg}`)) - cb() - }, - cb => fs.emptyDir(tmpdir, cb), - (cb1, cb2) => fs.copy(fixtureDir, tmpdir, cb2 || cb1), // inconsistent cb arguments from fs.emptyDir - cb => cb(null, tmpdir) - ], cb) + let tmpdirPkg = pkgUp.sync(path.join(tmpdir, '..')) + + if (tmpdirPkg) { + throw new Error(`Found package.json in parent of temp directory, which will interfere with test results. Please remove package.json at ${tmpdirPkg}`) + } + + return fs.emptyDir(tmpdir) + .then(() => fs.copy(fixtureDir, tmpdir)) + .then(() => tmpdir) } function createInferFailureTest (opts, fixtureSubdir) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) - copyFixtureToTempDir(fixtureSubdir, (err, dir) => { - if (err) return t.end(err) - - delete opts.electronVersion - opts.dir = dir + copyFixtureToTempDir(fixtureSubdir) + .then((dir) => { + delete opts.electronVersion + opts.dir = dir - packager(opts, function (err, paths) { - t.ok(err, 'error thrown') - t.end() - }) - }) + return packager(opts) + }).then( + paths => t.end('expected error'), + err => { + t.ok(err, 'error thrown') + return t.end() + } + ).catch(t.end) } } function createInferMissingVersionTest (opts) { return (t) => { t.timeoutAfter(config.timeout) - waterfall([ - (cb) => { - copyFixtureToTempDir('infer-missing-version-only', cb) - }, (dir, cb) => { + copyFixtureToTempDir('infer-missing-version-only') + .then((dir) => { delete opts.electronVersion opts.dir = dir - getMetadataFromPackageJSON(opts, dir, cb) - }, (cb) => { - fs.readFile(path.join(opts.dir, 'package.json'), cb) - }, (pkg, cb) => { - const packageJSON = JSON.parse(pkg) + return pify(getMetadataFromPackageJSON)(opts, dir) + }).then(() => { + const packageJSON = require(path.join(opts.dir, 'package.json')) t.equal(opts.electronVersion, packageJSON.devDependencies['electron'], 'The version should be inferred from installed electron module version') - cb() - } - ], (err) => { - t.end(err) - }) + return t.end() + }).catch(t.end) } } diff --git a/test/mac.js b/test/mac.js index a235ab97..d1cfe472 100644 --- a/test/mac.js +++ b/test/mac.js @@ -1,15 +1,14 @@ 'use strict' const config = require('./config.json') -const exec = require('child_process').exec -const fs = require('fs') +const exec = require('mz/child_process').exec +const fs = require('fs-extra') const mac = require('../mac') const packager = require('..') const path = require('path') const plist = require('plist') const test = require('tape') const util = require('./util') -const waterfall = require('run-waterfall') function getHelperExecutablePath (helperName) { return path.join(`${helperName}.app`, 'Contents', 'MacOS', helperName) @@ -26,37 +25,32 @@ function createHelperAppPathsTest (baseOpts, expectedName) { expectedName = opts.name } - waterfall([ - (cb) => { - packager(opts, cb) - }, (paths, cb) => { + packager(opts) + .then(paths => { frameworksPath = path.join(paths[0], `${expectedName}.app`, 'Contents', 'Frameworks') // main Helper.app is already tested in basic test suite; test its executable and the other helpers - fs.stat(path.join(frameworksPath, getHelperExecutablePath(`${expectedName} Helper`)), cb) - }, (stats, cb) => { + return fs.stat(path.join(frameworksPath, getHelperExecutablePath(`${expectedName} Helper`))) + }).then(stats => { t.true(stats.isFile(), 'The Helper.app executable should reflect sanitized opts.name') - fs.stat(path.join(frameworksPath, `${expectedName} Helper EH.app`), cb) - }, (stats, cb) => { + return fs.stat(path.join(frameworksPath, `${expectedName} Helper EH.app`)) + }).then(stats => { t.true(stats.isDirectory(), 'The Helper EH.app should reflect sanitized opts.name') - fs.stat(path.join(frameworksPath, getHelperExecutablePath(`${expectedName} Helper EH`)), cb) - }, (stats, cb) => { + return fs.stat(path.join(frameworksPath, getHelperExecutablePath(`${expectedName} Helper EH`))) + }).then(stats => { t.true(stats.isFile(), 'The Helper EH.app executable should reflect sanitized opts.name') - fs.stat(path.join(frameworksPath, `${expectedName} Helper NP.app`), cb) - }, (stats, cb) => { + return fs.stat(path.join(frameworksPath, `${expectedName} Helper NP.app`)) + }).then(stats => { t.true(stats.isDirectory(), 'The Helper NP.app should reflect sanitized opts.name') - fs.stat(path.join(frameworksPath, getHelperExecutablePath(`${expectedName} Helper NP`)), cb) - }, (stats, cb) => { + return fs.stat(path.join(frameworksPath, getHelperExecutablePath(`${expectedName} Helper NP`))) + }).then(stats => { t.true(stats.isFile(), 'The Helper NP.app executable should reflect sanitized opts.name') - cb() - } - ], (err) => { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createIconTest (baseOpts, icon, iconPath) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var opts = Object.create(baseOpts) @@ -65,34 +59,29 @@ function createIconTest (baseOpts, icon, iconPath) { var resourcesPath var plistPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { resourcesPath = path.join(paths[0], util.generateResourcesPath(opts)) plistPath = path.join(paths[0], opts.name + '.app', 'Contents', 'Info.plist') - fs.stat(resourcesPath, cb) - }, function (stats, cb) { + return fs.stat(resourcesPath) + }).then(stats => { t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) - util.areFilesEqual(iconPath, path.join(resourcesPath, obj.CFBundleIconFile), cb) - }, function (equal, cb) { + return util.areFilesEqual(iconPath, path.join(resourcesPath, obj.CFBundleIconFile)) + }).then(equal => { t.true(equal, 'installed icon file should be identical to the specified icon file') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createExtraResourceTest (baseOpts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var extra1Base = 'data1.txt' @@ -103,27 +92,22 @@ function createExtraResourceTest (baseOpts) { var resourcesPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { resourcesPath = path.join(paths[0], util.generateResourcesPath(opts)) - fs.stat(resourcesPath, cb) - }, function (stats, cb) { + return fs.stat(resourcesPath) + }).then(stats => { t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory') - util.areFilesEqual(extra1Path, path.join(resourcesPath, extra1Base), cb) - }, function (equal, cb) { + return util.areFilesEqual(extra1Path, path.join(resourcesPath, extra1Base)) + }).then(equal => { t.true(equal, 'resource file data1.txt should match') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createExtraResource2Test (baseOpts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var extra1Base = 'data1.txt' @@ -136,30 +120,25 @@ function createExtraResource2Test (baseOpts) { var resourcesPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { resourcesPath = path.join(paths[0], util.generateResourcesPath(opts)) - fs.stat(resourcesPath, cb) - }, function (stats, cb) { + return fs.stat(resourcesPath) + }).then(stats => { t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory') - util.areFilesEqual(extra1Path, path.join(resourcesPath, extra1Base), cb) - }, function (equal, cb) { + return util.areFilesEqual(extra1Path, path.join(resourcesPath, extra1Base)) + }).then(equal => { t.true(equal, 'resource file data1.txt should match') - util.areFilesEqual(extra2Path, path.join(resourcesPath, extra2Base), cb) - }, function (equal, cb) { + return util.areFilesEqual(extra2Path, path.join(resourcesPath, extra2Base)) + }).then(equal => { t.true(equal, 'resource file extrainfo.plist should match') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createExtendInfoTest (baseOpts, extraPathOrParams) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var opts = Object.create(baseOpts) @@ -170,16 +149,14 @@ function createExtendInfoTest (baseOpts, extraPathOrParams) { var plistPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { plistPath = path.join(paths[0], opts.name + '.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) t.equal(obj.TestKeyString, 'String data', 'TestKeyString should come from extendInfo') t.equal(obj.TestKeyInt, 12345, 'TestKeyInt should come from extendInfo') @@ -190,11 +167,8 @@ function createExtendInfoTest (baseOpts, extraPathOrParams) { t.equal(obj.CFBundleIdentifier, 'com.electron.extratest', 'CFBundleIdentifier should reflect appBundleId argument') t.equal(obj.LSApplicationCategoryType, 'public.app-category.music', 'LSApplicationCategoryType should reflect appCategoryType argument') t.equal(obj.CFBundlePackageType, 'APPL', 'CFBundlePackageType should be Electron default') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } @@ -206,24 +180,19 @@ function createBinaryNameTest (baseOpts, expectedAppName) { let binaryPath let appName = expectedAppName || opts.name - waterfall([ - (cb) => { - packager(opts, cb) - }, (paths, cb) => { + packager(opts) + .then(paths => { binaryPath = path.join(paths[0], `${appName}.app`, 'Contents', 'MacOS') - fs.stat(path.join(binaryPath, appName), cb) - }, (stats, cb) => { + return fs.stat(path.join(binaryPath, appName)) + }).then(stats => { t.true(stats.isFile(), 'The binary should reflect a sanitized opts.name') - cb() - } - ], (err) => { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createAppVersionTest (baseOpts, appVersion, buildVersion) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var plistPath @@ -234,86 +203,71 @@ function createAppVersionTest (baseOpts, appVersion, buildVersion) { opts.buildVersion = buildVersion } - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { plistPath = path.join(paths[0], opts.name + '.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) t.equal(obj.CFBundleVersion, '' + opts.buildVersion, 'CFBundleVersion should reflect buildVersion') t.equal(obj.CFBundleShortVersionString, '' + opts.appVersion, 'CFBundleShortVersionString should reflect appVersion') t.equal(typeof obj.CFBundleVersion, 'string', 'CFBundleVersion should be a string') t.equal(typeof obj.CFBundleShortVersionString, 'string', 'CFBundleShortVersionString should be a string') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createAppVersionInferenceTest (baseOpts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var plistPath - waterfall([ - function (cb) { - packager(baseOpts, cb) - }, function (paths, cb) { + packager(baseOpts) + .then(paths => { plistPath = path.join(paths[0], baseOpts.name + '.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) t.equal(obj.CFBundleVersion, '4.99.101', 'CFBundleVersion should reflect package.json version') t.equal(obj.CFBundleShortVersionString, '4.99.101', 'CFBundleShortVersionString should reflect package.json version') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createAppCategoryTypeTest (baseOpts, appCategoryType) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var plistPath var opts = Object.create(baseOpts) opts.appCategoryType = appCategoryType - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { plistPath = path.join(paths[0], opts.name + '.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) t.equal(obj.LSApplicationCategoryType, opts.appCategoryType, 'LSApplicationCategoryType should reflect opts.appCategoryType') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createAppBundleTest (baseOpts, appBundleId) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var plistPath @@ -324,16 +278,14 @@ function createAppBundleTest (baseOpts, appBundleId) { var defaultBundleName = 'com.electron.' + opts.name.toLowerCase() var appBundleIdentifier = mac.filterCFBundleIdentifier(opts.appBundleId || defaultBundleName) - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { plistPath = path.join(paths[0], opts.name + '.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) t.equal(obj.CFBundleDisplayName, opts.name, 'CFBundleDisplayName should reflect opts.name') t.equal(obj.CFBundleName, opts.name, 'CFBundleName should reflect opts.name') @@ -342,44 +294,36 @@ function createAppBundleTest (baseOpts, appBundleId) { t.equal(typeof obj.CFBundleName, 'string', 'CFBundleName should be a string') t.equal(typeof obj.CFBundleIdentifier, 'string', 'CFBundleIdentifier should be a string') t.equal(/^[a-zA-Z0-9-.]*$/.test(obj.CFBundleIdentifier), true, 'CFBundleIdentifier should allow only alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.)') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createAppBundleFrameworkTest (baseOpts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var frameworkPath - waterfall([ - function (cb) { - packager(baseOpts, cb) - }, function (paths, cb) { + packager(baseOpts) + .then(paths => { frameworkPath = path.join(paths[0], `${baseOpts.name}.app`, 'Contents', 'Frameworks', 'Electron Framework.framework') - fs.stat(frameworkPath, cb) - }, function (stats, cb) { + return fs.stat(frameworkPath) + }).then(stats => { t.true(stats.isDirectory(), 'Expected Electron Framework.framework to be a directory') - fs.lstat(path.join(frameworkPath, 'Electron Framework'), cb) - }, function (stats, cb) { + return fs.lstat(path.join(frameworkPath, 'Electron Framework')) + }).then(stats => { t.true(stats.isSymbolicLink(), 'Expected Electron Framework.framework/Electron Framework to be a symlink') - fs.lstat(path.join(frameworkPath, 'Versions', 'Current'), cb) - }, function (stats, cb) { + return fs.lstat(path.join(frameworkPath, 'Versions', 'Current')) + }).then(stats => { t.true(stats.isSymbolicLink(), 'Expected Electron Framework.framework/Versions/Current to be a symlink') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createAppHelpersBundleTest (baseOpts, helperBundleId, appBundleId) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var tempPath, plistPath @@ -394,17 +338,15 @@ function createAppHelpersBundleTest (baseOpts, helperBundleId, appBundleId) { var appBundleIdentifier = mac.filterCFBundleIdentifier(opts.appBundleId || defaultBundleName) var helperBundleIdentifier = mac.filterCFBundleIdentifier(opts.helperBundleId || appBundleIdentifier + '.helper') - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { tempPath = paths[0] plistPath = path.join(tempPath, opts.name + '.app', 'Contents', 'Frameworks', opts.name + ' Helper.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist in helper app') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) t.equal(obj.CFBundleName, opts.name, 'CFBundleName should reflect opts.name in helper app') t.equal(obj.CFBundleIdentifier, helperBundleIdentifier, 'CFBundleIdentifier should reflect opts.helperBundleId, opts.appBundleId or fallback to default in helper app') @@ -413,11 +355,11 @@ function createAppHelpersBundleTest (baseOpts, helperBundleId, appBundleId) { t.equal(/^[a-zA-Z0-9-.]*$/.test(obj.CFBundleIdentifier), true, 'CFBundleIdentifier should allow only alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.)') // check helper EH plistPath = path.join(tempPath, opts.name + '.app', 'Contents', 'Frameworks', opts.name + ' Helper EH.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist in helper EH app') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) t.equal(obj.CFBundleName, opts.name + ' Helper EH', 'CFBundleName should reflect opts.name in helper EH app') t.equal(obj.CFBundleDisplayName, opts.name + ' Helper EH', 'CFBundleDisplayName should reflect opts.name in helper EH app') @@ -430,11 +372,11 @@ function createAppHelpersBundleTest (baseOpts, helperBundleId, appBundleId) { t.equal(/^[a-zA-Z0-9-.]*$/.test(obj.CFBundleIdentifier), true, 'CFBundleIdentifier should allow only alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.)') // check helper NP plistPath = path.join(tempPath, opts.name + '.app', 'Contents', 'Frameworks', opts.name + ' Helper NP.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist in helper NP app') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) t.equal(obj.CFBundleName, opts.name + ' Helper NP', 'CFBundleName should reflect opts.name in helper NP app') t.equal(obj.CFBundleDisplayName, opts.name + ' Helper NP', 'CFBundleDisplayName should reflect opts.name in helper NP app') @@ -445,44 +387,36 @@ function createAppHelpersBundleTest (baseOpts, helperBundleId, appBundleId) { t.equal(typeof obj.CFBundleExecutable, 'string', 'CFBundleExecutable should be a string in helper NP app') t.equal(typeof obj.CFBundleIdentifier, 'string', 'CFBundleIdentifier should be a string in helper NP app') t.equal(/^[a-zA-Z0-9-.]*$/.test(obj.CFBundleIdentifier), true, 'CFBundleIdentifier should allow only alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.)') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createAppHumanReadableCopyrightTest (baseOpts, humanReadableCopyright) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var plistPath var opts = Object.create(baseOpts) opts.appCopyright = humanReadableCopyright - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { plistPath = path.join(paths[0], opts.name + '.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { var obj = plist.parse(file) t.equal(obj.NSHumanReadableCopyright, opts.appCopyright, 'NSHumanReadableCopyright should reflect opts.appCopyright') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } function createProtocolTest (baseOpts) { - return function (t) { + return (t) => { t.timeoutAfter(config.timeout) var plistPath @@ -495,16 +429,14 @@ function createProtocolTest (baseOpts) { schemes: ['bar', 'baz'] }] - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { plistPath = path.join(paths[0], opts.name + '.app', 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, function (stats, cb) { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist') - fs.readFile(plistPath, 'utf8', cb) - }, function (file, cb) { + return fs.readFile(plistPath, 'utf8') + }).then(file => { t.deepEqual(plist.parse(file).CFBundleURLTypes, [{ CFBundleURLName: 'Foo', CFBundleURLSchemes: ['foo'] @@ -512,11 +444,8 @@ function createProtocolTest (baseOpts) { CFBundleURLName: 'Bar', CFBundleURLSchemes: ['bar', 'baz'] }], 'CFBundleURLTypes did not contain specified protocol schemes and names') - cb() - } - ], function (err) { - t.end(err) - }) + return t.end() + }).catch(t.end) } } @@ -550,74 +479,72 @@ module.exports = (baseOpts) => { util.packagerTest('protocol/protocol-name argument test', createProtocolTest(baseOpts)) - test('osxSign argument test: default args', function (t) { + test('osxSign argument test: default args', (t) => { var args = true var signOpts = mac.createSignOpts(args, 'darwin', 'out', 'version') t.same(signOpts, {identity: null, app: 'out', platform: 'darwin', version: 'version'}) - t.end() + return t.end() }) - test('osxSign argument test: identity=true sets autodiscovery mode', function (t) { + test('osxSign argument test: identity=true sets autodiscovery mode', (t) => { var args = {identity: true} var signOpts = mac.createSignOpts(args, 'darwin', 'out', 'version') t.same(signOpts, {identity: null, app: 'out', platform: 'darwin', version: 'version'}) - t.end() + return t.end() }) - test('osxSign argument test: entitlements passed to electron-osx-sign', function (t) { + test('osxSign argument test: entitlements passed to electron-osx-sign', (t) => { var args = {entitlements: 'path-to-entitlements'} var signOpts = mac.createSignOpts(args, 'darwin', 'out', 'version') t.same(signOpts, {app: 'out', platform: 'darwin', version: 'version', entitlements: args.entitlements}) - t.end() + return t.end() }) - test('osxSign argument test: app not overwritten', function (t) { + test('osxSign argument test: app not overwritten', (t) => { var args = {app: 'some-other-path'} var signOpts = mac.createSignOpts(args, 'darwin', 'out', 'version') t.same(signOpts, {app: 'out', platform: 'darwin', version: 'version'}) - t.end() + return t.end() }) - test('osxSign argument test: platform not overwritten', function (t) { + test('osxSign argument test: platform not overwritten', (t) => { var args = {platform: 'mas'} var signOpts = mac.createSignOpts(args, 'darwin', 'out', 'version') t.same(signOpts, {app: 'out', platform: 'darwin', version: 'version'}) - t.end() + return t.end() }) test('osxSign argument test: binaries not set', (t) => { let args = {binaries: ['binary1', 'binary2']} let signOpts = mac.createSignOpts(args, 'darwin', 'out', 'version') t.same(signOpts, {app: 'out', platform: 'darwin', version: 'version'}) - t.end() + return t.end() }) util.packagerTest('codesign test', (t) => { t.timeoutAfter(config.macExecTimeout) - var opts = Object.create(baseOpts) - opts.osxSign = {identity: 'Developer CodeCert'} - - var appPath + const opts = Object.assign({}, baseOpts, {osxSign: {identity: 'Developer CodeCert'}}) + let appPath - waterfall([ - function (cb) { - packager(opts, cb) - }, function (paths, cb) { + packager(opts) + .then(paths => { appPath = path.join(paths[0], opts.name + '.app') - fs.stat(appPath, cb) - }, function (stats, cb) { + return fs.stat(appPath) + }).then(stats => { t.true(stats.isDirectory(), 'The expected .app directory should exist') - exec('codesign -v ' + appPath, cb) - }, function (stdout, stderr, cb) { - t.pass('codesign should verify successfully') - cb() - } - ], function (err) { - var notFound = err && err.code === 127 - if (notFound) console.log('codesign not installed; skipped') - t.end(notFound ? null : err) - }) + return exec(`codesign -v ${appPath}`) + }).then( + (stdout, stderr) => { + t.pass('codesign should verify successfully') + return t.end() + }, + (err) => { + const notFound = err && err.code === 127 + if (notFound) console.log('codesign not installed; skipped') + return t.end(notFound ? null : err) + } + ).catch(t.end) }) util.packagerTest('binary naming test', createBinaryNameTest(baseOpts)) @@ -627,21 +554,19 @@ module.exports = (baseOpts) => { t.timeoutAfter(config.timeout) let plistPath - let opts = Object.assign({}, baseOpts, {name: '@username/package-name'}) - let appBundleIdentifier = 'com.electron.username-package-name' - let expectedSanitizedName = '@username-package-name' - - waterfall([ - (cb) => { - packager(opts, cb) - }, (paths, cb) => { + const opts = Object.assign({}, baseOpts, {name: '@username/package-name'}) + const appBundleIdentifier = 'com.electron.username-package-name' + const expectedSanitizedName = '@username-package-name' + + packager(opts) + .then(paths => { plistPath = path.join(paths[0], `${expectedSanitizedName}.app`, 'Contents', 'Info.plist') - fs.stat(plistPath, cb) - }, (stats, cb) => { + return fs.stat(plistPath) + }).then(stats => { t.true(stats.isFile(), 'The expected Info.plist file should exist') - fs.readFile(plistPath, 'utf8', cb) - }, (file, cb) => { - let obj = plist.parse(file) + return fs.readFile(plistPath, 'utf8') + }).then(file => { + const obj = plist.parse(file) t.equal(typeof obj.CFBundleDisplayName, 'string', 'CFBundleDisplayName should be a string') t.equal(obj.CFBundleDisplayName, opts.name, 'CFBundleDisplayName should reflect opts.name') t.equal(typeof obj.CFBundleName, 'string', 'CFBundleName should be a string') @@ -649,11 +574,8 @@ module.exports = (baseOpts) => { t.equal(typeof obj.CFBundleIdentifier, 'string', 'CFBundleIdentifier should be a string') t.equal(/^[a-zA-Z0-9-.]*$/.test(obj.CFBundleIdentifier), true, 'CFBundleIdentifier should allow only alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.)') t.equal(obj.CFBundleIdentifier, appBundleIdentifier, 'CFBundleIdentifier should reflect the sanitized opts.name') - cb() - } - ], (err) => { - t.end(err) - }) + return t.end() + }).catch(t.end) }) util.packagerTest('app and build version test', createAppVersionTest(baseOpts, '1.1.0', '1.1.0.1234')) @@ -677,25 +599,19 @@ module.exports = (baseOpts) => { util.packagerTest('app humanReadableCopyright test', createAppHumanReadableCopyrightTest(baseOpts, 'Copyright © 2003–2015 Organization. All rights reserved.')) util.packagerTest('app named Electron packaged successfully', (t) => { - let opts = Object.create(baseOpts) - opts.name = 'Electron' + const opts = Object.assign({}, baseOpts, {name: 'Electron'}) let appPath - waterfall([ - (cb) => { - packager(opts, cb) - }, (paths, cb) => { + packager(opts) + .then(paths => { appPath = path.join(paths[0], 'Electron.app') - fs.stat(appPath, cb) - }, (stats, cb) => { + return fs.stat(appPath) + }).then(stats => { t.true(stats.isDirectory(), 'The Electron.app folder exists') - fs.stat(path.join(appPath, 'Contents', 'MacOS', 'Electron'), cb) - }, (stats, cb) => { + return fs.stat(path.join(appPath, 'Contents', 'MacOS', 'Electron')) + }).then(stats => { t.true(stats.isFile(), 'The Electron.app/Contents/MacOS/Electron binary exists') - cb() - } - ], (err) => { - t.end(err) - }) + return t.end() + }).catch(t.end) }) } diff --git a/test/multitarget.js b/test/multitarget.js index a1b68543..57dc8507 100644 --- a/test/multitarget.js +++ b/test/multitarget.js @@ -3,118 +3,52 @@ const config = require('./config.json') const packager = require('..') const util = require('./util') -const waterfall = require('run-waterfall') -function createMultiTest (arch, platform) { - return (t) => { - // 4 packages will be built during this test - t.timeoutAfter(config.timeout * 4) - - var opts = { - name: 'basicTest', - dir: util.fixtureSubdir('basic'), - electronVersion: config.version, - arch: arch, - platform: platform - } - - waterfall([ - (cb) => { - packager(opts, cb) - }, (finalPaths, cb) => { - t.equal(finalPaths.length, 4, 'packager call should resolve with expected number of paths') - util.verifyPackageExistence(finalPaths, cb) - }, (exists, cb) => { - t.true(exists, 'Packages should be generated for all combinations of specified archs and platforms') - cb() - } - ], (err) => { - t.end(err) - }) - } -} - -util.packagerTest('all test', (t) => { - t.timeoutAfter(config.timeout * 7) // 7 packages will be built during this test - - var opts = { +function createMultiTargetOptions (extraOpts) { + return Object.assign({ name: 'basicTest', dir: util.fixtureSubdir('basic'), - electronVersion: config.version, - all: true - } - - var expectedAppCount = 7 + electronVersion: config.version + }, extraOpts) +} - waterfall([ - function (cb) { - packager(opts, cb) - }, function (finalPaths, cb) { - // OS X only has 64-bit releases - t.equal(finalPaths.length, expectedAppCount, - 'packager call should resolve with expected number of paths') - util.verifyPackageExistence(finalPaths, cb) - }, function (exists, cb) { - t.true(exists, 'Packages should be generated for all possible platforms') - cb() - } - ], function (err) { - t.end(err) - }) -}) +function createMultiTargetPromise (t, opts, expectedPackageCount, packageExistenceMessage) { + packager(opts) + .then(finalPaths => { + t.equal(finalPaths.length, expectedPackageCount, + 'packager call should resolve with expected number of paths') + return util.verifyPackageExistence(finalPaths) + }).then(exists => { + t.true(exists, packageExistenceMessage) + return t.end() + }).catch(t.end) +} -util.packagerTest('platform=all test (one arch)', function (t) { - t.timeoutAfter(config.timeout * 2) // 2 packages will be built during this test +function createMultiTargetTest (extraOpts, expectedPackageCount, packageExistenceMessage) { + return (t) => { + t.timeoutAfter(config.timeout * expectedPackageCount) - var opts = { - name: 'basicTest', - dir: util.fixtureSubdir('basic'), - electronVersion: config.version, - arch: 'ia32', - platform: 'all' + const opts = createMultiTargetOptions(extraOpts) + createMultiTargetPromise(t, opts, expectedPackageCount, packageExistenceMessage) } +} - waterfall([ - function (cb) { - packager(opts, cb) - }, function (finalPaths, cb) { - t.equal(finalPaths.length, 2, 'packager call should resolve with expected number of paths') - util.verifyPackageExistence(finalPaths, cb) - }, function (exists, cb) { - t.true(exists, 'Packages should be generated for both 32-bit platforms') - cb() - } - ], function (err) { - t.end(err) - }) -}) - -util.packagerTest('arch=all test (one platform)', (t) => { - const LINUX_ARCH_COUNT = 3 - t.timeoutAfter(config.timeout * LINUX_ARCH_COUNT) +function createMultiTest (arch, platform) { + return createMultiTargetTest({arch: arch, platform: platform}, 4, + 'Packages should be generated for all combinations of specified archs and platforms') +} - var opts = { - name: 'basicTest', - dir: util.fixtureSubdir('basic'), - electronVersion: config.version, - arch: 'all', - platform: 'linux' - } +util.packagerTest('all test', (t) => { + const EXPECTED_PACKAGES = 7 + const opts = createMultiTargetOptions({all: true}) + const message = 'Packages should be generated for all possible platforms' - waterfall([ - function (cb) { - packager(opts, cb) - }, function (finalPaths, cb) { - t.equal(finalPaths.length, LINUX_ARCH_COUNT, 'packager call should resolve with expected number of paths') - util.verifyPackageExistence(finalPaths, cb) - }, function (exists, cb) { - t.true(exists, 'Packages should be generated for all expected architectures') - cb() - } - ], function (err) { - t.end(err) - }) + return createMultiTargetPromise(t, opts, EXPECTED_PACKAGES, message) }) +util.packagerTest('platform=all test (one arch)', + createMultiTargetTest({arch: 'ia32', platform: 'all'}, 2, 'Packages should be generated for both 32-bit platforms')) +util.packagerTest('arch=all test (one platform)', + createMultiTargetTest({arch: 'all', platform: 'linux'}, 3, 'Packages should be generated for all expected architectures')) util.packagerTest('multi-platform / multi-arch test, from arrays', createMultiTest(['ia32', 'x64'], ['linux', 'win32'])) util.packagerTest('multi-platform / multi-arch test, from strings', createMultiTest('ia32,x64', 'linux,win32')) diff --git a/test/prune.js b/test/prune.js index 5dcc95be..3355836f 100644 --- a/test/prune.js +++ b/test/prune.js @@ -7,7 +7,6 @@ const path = require('path') const prune = require('../prune') const test = require('tape') const util = require('./util') -const waterfall = require('run-waterfall') const which = require('which') function createPruneOptionTest (baseOpts, prune, testMessage) { @@ -22,29 +21,24 @@ function createPruneOptionTest (baseOpts, prune, testMessage) { let finalPath let resourcesPath - waterfall([ - (cb) => { - packager(opts, cb) - }, (paths, cb) => { + packager(opts) + .then(paths => { finalPath = paths[0] - fs.stat(finalPath, cb) - }, (stats, cb) => { + return fs.stat(finalPath) + }).then(stats => { t.true(stats.isDirectory(), 'The expected output directory should exist') resourcesPath = path.join(finalPath, util.generateResourcesPath(opts)) - fs.stat(resourcesPath, cb) - }, (stats, cb) => { + return fs.stat(resourcesPath) + }).then(stats => { t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory') - fs.stat(path.join(resourcesPath, 'app', 'node_modules', 'run-series'), cb) - }, (stats, cb) => { - t.true(stats.isDirectory(), 'package.json dependency should exist under app/node_modules') - fs.exists(path.join(resourcesPath, 'app', 'node_modules', 'run-waterfall'), (exists) => { - t.equal(!prune, exists, testMessage) - cb() - }) - } - ], (err) => { - t.end(err) - }) + return fs.stat(path.join(resourcesPath, 'app', 'node_modules', 'run-series')) + }).then(stats => { + t.true(stats.isDirectory(), 'npm dependency should exist under app/node_modules') + return fs.pathExists(path.join(resourcesPath, 'app', 'node_modules', 'run-waterfall')) + }).then(exists => { + t.equal(!prune, exists, testMessage) + return t.end() + }).catch(t.end) } } diff --git a/test/util.js b/test/util.js index 150de587..56163ce4 100644 --- a/test/util.js +++ b/test/util.js @@ -1,45 +1,67 @@ 'use strict' +// Keeping this module because it handles non-buffers gracefully const bufferEqual = require('buffer-equal') const common = require('../common') const config = require('./config.json') const fs = require('fs-extra') +const os = require('os') const path = require('path') -const series = require('run-series') -const slice = Array.prototype.slice +const pify = require('pify') const test = require('tape') const ORIGINAL_CWD = process.cwd() const WORK_CWD = path.join(__dirname, 'work') -exports.areFilesEqual = function areFilesEqual (file1, file2, callback) { - series([ - function (cb) { - fs.readFile(file1, cb) - }, - function (cb) { - fs.readFile(file2, cb) - } - ], function (err, buffers) { - callback(err, bufferEqual(buffers[0], buffers[1])) +// tape doesn't seem to have a provision for before/beforeEach/afterEach/after, +// so run setup/teardown and cleanup tasks as additional "tests" to put them in sequence +// and run them irrespective of test failures + +function setup () { + test('setup', (t) => { + fs.mkdirp(WORK_CWD) + .then(() => { + process.chdir(WORK_CWD) + return t.end() + }).catch(t.end) + }) +} + +function teardown () { + test('teardown', (t) => { + process.chdir(ORIGINAL_CWD) + fs.remove(WORK_CWD).then(t.end).catch(t.end) }) } -exports.downloadAll = function downloadAll (version, callback) { - let combinations = common.createDownloadCombos({electronVersion: config.version}, common.platforms, common.archs, (platform, arch) => { +exports.areFilesEqual = function areFilesEqual (file1, file2) { + let buffer1, buffer2 + + return fs.readFile(file1) + .then((data) => { + buffer1 = data + return fs.readFile(file2) + }).then((data) => { + buffer2 = data + return bufferEqual(buffer1, buffer2) + }) +} + +exports.downloadAll = function downloadAll (version) { + console.log(`Calling electron-download for ${version} before running tests...`) + const combinations = common.createDownloadCombos({electronVersion: config.version}, common.platforms, common.archs, (platform, arch) => { // Skip testing darwin/mas target on Windows since electron-packager itself skips it // (see https://github.com/electron-userland/electron-packager/issues/71) return common.isPlatformMac(platform) && process.platform === 'win32' }) - series(combinations.map(combination => { - return (cb) => { - var downloadOpts = Object.assign({}, combination) - downloadOpts.version = version - downloadOpts.quiet = !!process.env.CI - common.downloadElectronZip(downloadOpts, cb) - } - }), callback) + return Promise.all(combinations.map(combination => { + return pify(common.downloadElectronZip)(Object.assign({}, combination, { + cache: path.join(os.homedir(), '.electron'), + quiet: !!process.env.CI, + version: version + })) + })) } exports.fixtureSubdir = function fixtureSubdir (subdir) { @@ -56,52 +78,24 @@ exports.getWorkCwd = function getWorkCwd () { return WORK_CWD } -// tape doesn't seem to have a provision for before/beforeEach/afterEach/after, -// so run setup/teardown and cleanup tasks as additional "tests" to put them in sequence -// and run them irrespective of test failures - -exports.setup = function setup () { - test('setup', function (t) { - fs.mkdirp(WORK_CWD, function (err) { - if (!err) { - process.chdir(WORK_CWD) - } - t.end(err) - }) - }) -} - -exports.teardown = function teardown () { - test('teardown', function (t) { - process.chdir(ORIGINAL_CWD) - fs.remove(WORK_CWD, function (err) { - t.end(err) - }) - }) -} - exports.packagerTest = function packagerTest (name, testFunction) { - exports.setup() + setup() test(name, testFunction) // eslint-disable-line tape/test-ended - exports.teardown() + teardown() } // Rest parameters are added (not behind a feature flag) in Node 6 exports.testSinglePlatform = function testSinglePlatform (name, createTest /*, ...createTestArgs */) { - var args = slice.call(arguments, 2) + var args = Array.prototype.slice.call(arguments, 2) exports.packagerTest(name, createTest.apply(null, [{platform: 'linux', arch: 'x64', electronVersion: config.version}].concat(args))) } -exports.verifyPackageExistence = function verifyPackageExistence (finalPaths, callback) { - series(finalPaths.map((finalPath) => { - return (cb) => { - fs.stat(finalPath, cb) - } - }), (err, statsResults) => { - if (err) return callback(null, false) - - callback(null, statsResults.every((stats) => { - return stats.isDirectory() - })) - }) +exports.verifyPackageExistence = function verifyPackageExistence (finalPaths) { + return Promise.all(finalPaths.map((finalPath) => { + return fs.stat(finalPath) + .then( + stats => stats.isDirectory(), + () => false + ) + })) } diff --git a/test/win32.js b/test/win32.js index 5a033b26..9f8697d4 100644 --- a/test/win32.js +++ b/test/win32.js @@ -1,12 +1,11 @@ 'use strict' const config = require('./config.json') -const fs = require('fs') +const fs = require('fs-extra') const packager = require('..') const path = require('path') const test = require('tape') const util = require('./util') -const waterfall = require('run-waterfall') const win32 = require('../win32') const baseOpts = { @@ -165,22 +164,17 @@ test('error message unchanged when error not about wine', (t) => { }) util.packagerTest('win32 executable name is based on sanitized app name', (t) => { - let opts = Object.assign({}, baseOpts, {name: '@username/package-name'}) + const opts = Object.assign({}, baseOpts, {name: '@username/package-name'}) - waterfall([ - (cb) => { - packager(opts, cb) - }, (paths, cb) => { + packager(opts) + .then(paths => { t.equal(1, paths.length, '1 bundle created') - let appExePath = path.join(paths[0], '@username-package-name.exe') - fs.stat(appExePath, cb) - }, (stats, cb) => { - t.true(stats.isFile(), 'The sanitized EXE filename should exist') - cb() - } - ], (err) => { - t.end(err) - }) + const appExePath = path.join(paths[0], '@username-package-name.exe') + return fs.pathExists(appExePath) + }).then(exists => { + t.true(exists, 'The sanitized EXE filename should exist') + return t.end() + }).catch(t.end) }) util.packagerTest('win32 build version sets FileVersion test', setFileVersionTest('2.3.4.5'))