diff --git a/package.json b/package.json index d9abba90..daa92d46 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,9 @@ "plist": "^2.0.1", "rimraf": "^2.5.2", "run-series": "^1.1.4", + "spectron": "^3.3.0", "standard": "*", + "tape": "^4.6.0", "walk-sync": "^0.3.1" }, "engines": { @@ -100,6 +102,7 @@ "package": "node ./bin/package.js", "prepublish": "npm run build", "start": "npm run build && electron .", + "integration-test": "npm run build && node ./test", "test": "standard && depcheck --ignores=babel-cli,nodemon,gh-release --ignore-dirs=build,dist && node ./bin/extra-lint.js", "gh-release": "gh-release", "update-authors": "./bin/update-authors.sh", diff --git a/src/config.js b/src/config.js index 2b16077e..30f5e05a 100644 --- a/src/config.js +++ b/src/config.js @@ -1,12 +1,22 @@ const appConfig = require('application-config')('WebTorrent') const fs = require('fs') const path = require('path') +const electron = require('electron') const APP_NAME = 'WebTorrent' const APP_TEAM = 'WebTorrent, LLC' const APP_VERSION = require('../package.json').version -const PORTABLE_PATH = path.join(path.dirname(process.execPath), 'Portable Settings') +const IS_TEST = isTest() +const PORTABLE_PATH = IS_TEST + ? path.join(__dirname, '../test/tempTestData') + : path.join(path.dirname(process.execPath), 'Portable Settings') +const IS_PORTABLE = isPortable() +const IS_PRODUCTION = isProduction() + +console.log('Production: %s portable: %s test: %s', + IS_PRODUCTION, IS_PORTABLE, IS_TEST) +if (IS_PORTABLE) console.log('Portable path: %s', PORTABLE_PATH) module.exports = { ANNOUNCEMENT_URL: 'https://webtorrent.io/desktop/announcement', @@ -62,8 +72,9 @@ module.exports = { HOME_PAGE_URL: 'https://webtorrent.io', - IS_PORTABLE: isPortable(), - IS_PRODUCTION: isProduction(), + IS_PORTABLE: IS_PORTABLE, + IS_PRODUCTION: IS_PRODUCTION, + IS_TEST: IS_TEST, POSTER_PATH: path.join(getConfigPath(), 'Posters'), ROOT_PATH: path.join(__dirname, '..'), @@ -79,7 +90,7 @@ module.exports = { } function getConfigPath () { - if (isPortable()) { + if (IS_PORTABLE) { return PORTABLE_PATH } else { return path.dirname(appConfig.filePath) @@ -89,22 +100,31 @@ function getConfigPath () { function getDefaultDownloadPath () { if (!process || !process.type) { return '' - } - - if (isPortable()) { + } else if (IS_PORTABLE) { return path.join(getConfigPath(), 'Downloads') + } else { + return getPath('downloads') } +} - const electron = require('electron') +function getPath (key) { + if (process.type === 'renderer') { + return electron.remote.app.getPath(key) + } else { + electron.app.getPath(key) + } +} - return process.type === 'renderer' - ? electron.remote.app.getPath('downloads') - : electron.app.getPath('downloads') +function isTest () { + return process.env.NODE_ENV === 'test' } function isPortable () { + if (IS_TEST) { + return true + } try { - return process.platform === 'win32' && isProduction() && !!fs.statSync(PORTABLE_PATH) + return process.platform === 'win32' && IS_PRODUCTION && !!fs.statSync(PORTABLE_PATH) } catch (err) { return false } diff --git a/src/main/index.js b/src/main/index.js index e3778701..d10a3b56 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,7 +1,6 @@ console.time('init') const electron = require('electron') - const app = electron.app const ipcMain = electron.ipcMain @@ -159,6 +158,10 @@ function processArgv (argv) { } else if (arg.startsWith('-psn')) { // Ignore Mac launchd "process serial number" argument // Issue: https://github.com/feross/webtorrent-desktop/issues/214 + } else if (arg.startsWith('--')) { + // Ignore Spectron flags + } else if (arg === 'data:,') { + // Ignore weird Spectron argument } else if (arg !== '.') { // Ignore '.' argument, which gets misinterpreted as a torrent id, when a // development copy of WebTorrent is started while a production version is diff --git a/src/renderer/pages/torrent-list-page.js b/src/renderer/pages/torrent-list-page.js index 82933702..102f9fb2 100644 --- a/src/renderer/pages/torrent-list-page.js +++ b/src/renderer/pages/torrent-list-page.js @@ -14,7 +14,7 @@ module.exports = class TorrentList extends React.Component { if (state.downloadPathStatus === 'missing') { contents.push(
Download path missing: {state.saved.prefs.downloadPath}
+Download path missing: {state.saved.prefs.downloadPath}
Check that all drives are connected?
Alternatively, choose a new download path in Preferences diff --git a/test/index.js b/test/index.js new file mode 100644 index 00000000..c8a4a361 --- /dev/null +++ b/test/index.js @@ -0,0 +1,41 @@ +const test = require('tape') +const fs = require('fs-extra') +const path = require('path') +const setup = require('./setup') + +console.log('Creating test dir: ' + setup.TEST_DATA_DIR) +const DOWNLOAD_DIR = path.join(setup.TEST_DATA_DIR, 'Downloads') +fs.mkdirpSync(DOWNLOAD_DIR) + +test.onFinish(function () { + console.log('Removing test dir...') + fs.removeSync(setup.TEST_DATA_DIR) +}) + +test('app runs', function (t) { + t.timeoutAfter(10e3) + const app = setup.createApp() + setup.waitForLoad(app, t) + .then(() => setup.wait()) + .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-basic')) + .then(() => setup.endTest(app, t), + (err) => setup.endTest(app, t, err || 'error')) +}) + +test('show download path missing', function (t) { + fs.removeSync(DOWNLOAD_DIR) + + t.timeoutAfter(10e3) + const app = setup.createApp() + setup.waitForLoad(app, t) + .then(() => app.client.getTitle()) + .then((text) => console.log('Title ' + text)) + .then(() => app.client.waitUntilTextExists('.torrent-list', 'Download path missing')) + .then((err) => t.notOk(err)) + .then(() => app.client.click('a')) + .then(() => setup.wait()) + .then(() => app.browserWindow.getTitle()) + .then((windowTitle) => t.equal(windowTitle, 'Preferences')) + .then(() => setup.endTest(app, t), + (err) => setup.endTest(app, t, err || 'error')) +}) diff --git a/test/screenshots/darwin/torrent-list-basic.png b/test/screenshots/darwin/torrent-list-basic.png new file mode 100644 index 00000000..cedf4187 Binary files /dev/null and b/test/screenshots/darwin/torrent-list-basic.png differ diff --git a/test/setup.js b/test/setup.js new file mode 100644 index 00000000..31e5e9b2 --- /dev/null +++ b/test/setup.js @@ -0,0 +1,69 @@ +const path = require('path') +const Application = require('spectron').Application +const fs = require('fs-extra') + +const TEST_DATA_DIR = path.join(__dirname, 'tempTestData') + +module.exports = { + TEST_DATA_DIR, + createApp, + endTest, + screenshotCreateOrCompare, + waitForLoad, + wait +} + +// Runs WebTorrent Desktop. +// Returns a promise that resolves to a Spectron Application once the app has loaded. +// Takes a Tape test. Makes some basic assertions to verify that the app loaded correctly. +function createApp (t) { + return new Application({ + path: path.join(__dirname, '..', 'node_modules', '.bin', + 'electron' + (process.platform === 'win32' ? '.cmd' : '')), + args: [path.join(__dirname, '..')], + env: {NODE_ENV: 'test'} + }) +} + +// Starts the app, waits for it to load, returns a promise +function waitForLoad (app, t) { + return app.start().then(function () { + return app.client.windowByIndex(1) + }).then(function () { + return app.client.waitUntilWindowLoaded() + }).then(function () { + return app.webContents.getTitle() + }).then(function (title) { + t.equal(title, 'WebTorrent Desktop', 'app title') + }) +} + +function wait (ms) { + if (ms === undefined) ms = 500 // Default: wait long enough for the UI to update + return new Promise(function (resolve, reject) { + setTimeout(resolve, ms) + }) +} + +function endTest (app, t, err) { + return app.stop().then(function () { + t.end(err) + }) +} + +function screenshotCreateOrCompare (app, t, name) { + const ssPath = path.join(__dirname, 'screenshots', process.platform, name + '.png') + console.log('Capturing ' + ssPath) + fs.ensureFileSync(ssPath) + const ssBuf = fs.readFileSync(ssPath) + return app.browserWindow.capturePage().then(function (buffer) { + if (ssBuf.length === 0) { + console.log('Saving screenshot ' + ssPath) + fs.writeFileSync(ssPath, buffer) + } else if (Buffer.compare(buffer, ssBuf) !== 0) { + return Promise.reject('Screenshot didn\'t match: ' + ssPath) + } else { + return Promise.resolve() + } + }) +}