Integration test: create torrents

This commit is contained in:
DC
2016-09-14 06:52:49 -07:00
parent 051c1516a0
commit 75a4655a0f
17 changed files with 151 additions and 66 deletions

View File

@@ -215,11 +215,41 @@ module.exports = class TorrentListController {
menu.append(new electron.remote.MenuItem({ menu.append(new electron.remote.MenuItem({
label: 'Save Torrent File As...', label: 'Save Torrent File As...',
click: () => saveTorrentFileAs(torrentSummary) click: () => dispatch('saveTorrentFileAs', torrentSummary.torrentKey)
})) }))
menu.popup(electron.remote.getCurrentWindow()) menu.popup(electron.remote.getCurrentWindow())
} }
// Takes a torrentSummary or torrentKey
// Shows a Save File dialog, then saves the .torrent file wherever the user requests
saveTorrentFileAs (torrentKey) {
const torrentSummary = TorrentSummary.getByKey(this.state, torrentKey)
if (!torrentSummary) throw new Error('Missing torrentKey: ' + torrentKey)
const downloadPath = this.state.saved.prefs.downloadPath
const newFileName = path.parse(torrentSummary.name).name + '.torrent'
const win = electron.remote.getCurrentWindow()
const opts = {
title: 'Save Torrent File',
defaultPath: path.join(downloadPath, newFileName),
filters: [
{ name: 'Torrent Files', extensions: ['torrent'] },
{ name: 'All Files', extensions: ['*'] }
]
}
electron.remote.dialog.showSaveDialog(win, opts, function (savePath) {
console.log('Saving torrent ' + torrentKey + ' to ' + savePath)
if (!savePath) return // They clicked Cancel
const torrentPath = TorrentSummary.getTorrentPath(torrentSummary)
fs.readFile(torrentPath, function (err, torrentFile) {
if (err) return dispatch('error', err)
fs.writeFile(savePath, torrentFile, function (err) {
if (err) return dispatch('error', err)
})
})
})
}
} }
// Recursively finds {name, path, size} for all files in a folder // Recursively finds {name, path, size} for all files in a folder
@@ -280,27 +310,3 @@ function moveItemToTrash (torrentSummary) {
function showItemInFolder (torrentSummary) { function showItemInFolder (torrentSummary) {
ipcRenderer.send('showItemInFolder', TorrentSummary.getFileOrFolder(torrentSummary)) ipcRenderer.send('showItemInFolder', TorrentSummary.getFileOrFolder(torrentSummary))
} }
function saveTorrentFileAs (torrentSummary) {
const downloadPath = this.state.saved.prefs.downloadPath
const newFileName = path.parse(torrentSummary.name).name + '.torrent'
const opts = {
title: 'Save Torrent File',
defaultPath: path.join(downloadPath, newFileName),
filters: [
{ name: 'Torrent Files', extensions: ['torrent'] },
{ name: 'All Files', extensions: ['*'] }
]
}
const win = electron.remote.getCurrentWindow()
electron.remote.dialog.showSaveDialog(win, opts, function (savePath) {
if (!savePath) return // They clicked Cancel
const torrentPath = TorrentSummary.getTorrentPath(torrentSummary)
fs.readFile(torrentPath, function (err, torrentFile) {
if (err) return dispatch('error', err)
fs.writeFile(savePath, torrentFile, function (err) {
if (err) return dispatch('error', err)
})
})
})
}

View File

@@ -194,6 +194,8 @@ const dispatchHandlers = {
controllers.torrentList.openTorrentContextMenu(infoHash), controllers.torrentList.openTorrentContextMenu(infoHash),
'startTorrentingSummary': (torrentKey) => 'startTorrentingSummary': (torrentKey) =>
controllers.torrentList.startTorrentingSummary(torrentKey), controllers.torrentList.startTorrentingSummary(torrentKey),
'saveTorrentFileAs': (torrentKey) =>
controllers.torrentList.saveTorrentFileAs(torrentKey),
// Playback // Playback
'playFile': (infoHash, index) => controllers.playback.playFile(infoHash, index), 'playFile': (infoHash, index) => controllers.playback.playFile(infoHash, index),

View File

@@ -99,14 +99,14 @@ class CreateTorrentPage extends React.Component {
</ShowMore> </ShowMore>
<div className='float-right'> <div className='float-right'>
<FlatButton <FlatButton
className='control' className='control cancel'
label='Cancel' label='Cancel'
style={{ style={{
marginRight: 10 marginRight: 10
}} }}
onClick={dispatcher('cancel')} /> onClick={dispatcher('cancel')} />
<RaisedButton <RaisedButton
className='control' className='control create-torrent'
label='Create Torrent' label='Create Torrent'
primary primary
onClick={this.handleSubmit} /> onClick={this.handleSubmit} />

14
test/config.js Normal file
View File

@@ -0,0 +1,14 @@
const path = require('path')
const TEST_DIR = path.join(__dirname, 'tempTestData')
const TEST_DIR_DOWNLOAD = path.join(TEST_DIR, 'Downloads')
const TEST_DIR_DESKTOP = path.join(TEST_DIR, 'Desktop')
module.exports = {
TORRENT_FILES: [path.join(__dirname, 'resources', '1.torrent')],
SEED_FILES: [path.join(TEST_DIR_DESKTOP, 'tmp.jpg')],
SAVED_TORRENT_FILE: path.join(TEST_DIR_DESKTOP, 'saved.torrent'),
TEST_DIR,
TEST_DIR_DOWNLOAD,
TEST_DIR_DESKTOP
}

View File

@@ -1,17 +1,11 @@
const test = require('tape') const test = require('tape')
const fs = require('fs-extra')
const setup = require('./setup') const setup = require('./setup')
console.log('Creating download dir: ' + setup.TEST_DOWNLOAD_DIR) test.onFinish(setup.deleteTestDataDir)
fs.mkdirpSync(setup.TEST_DOWNLOAD_DIR)
test.onFinish(function () {
console.log('Removing test dir: ' + setup.TEST_DATA_DIR)
fs.removeSync(setup.TEST_DATA_DIR) // includes download dir
})
test('app runs', function (t) { test('app runs', function (t) {
t.timeoutAfter(10e3) t.timeoutAfter(10e3)
setup.resetTestDataDir()
const app = setup.createApp() const app = setup.createApp()
setup.waitForLoad(app, t) setup.waitForLoad(app, t)
.then(() => setup.wait()) .then(() => setup.wait())
@@ -20,12 +14,11 @@ test('app runs', function (t) {
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })
// require('./test-torrent-list') require('./test-torrent-list')
require('./test-add-torrent') require('./test-add-torrent')
// TODO: // TODO:
// require('./test-create-torrent')
// require('./test-prefs')
// require('./test-video') // require('./test-video')
// require('./test-audio') // require('./test-audio')
// require('./test-cast') // require('./test-cast')
// require('./test-prefs')

View File

@@ -1,9 +1,15 @@
const path = require('path')
const electron = require('electron') const electron = require('electron')
const config = require('./config')
const MOCK_OPEN_TORRENTS = [path.join(__dirname, 'resources', '1.torrent')] console.log('Mocking electron.dialog.showOpenDialog...')
console.log('Mocking electron native integrations...')
electron.dialog.showOpenDialog = function (win, opts, cb) { electron.dialog.showOpenDialog = function (win, opts, cb) {
cb(MOCK_OPEN_TORRENTS) const ret = /select.*torrent file/i.test(opts.title)
? config.TORRENT_FILES
: config.SEED_FILES
cb(ret)
}
console.log('Mocking electron.remote.dialog.showSaveDialog...')
electron.dialog.showSaveDialog = function (win, opts, cb) {
cb(config.SAVED_TORRENT_FILE)
} }

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -1,20 +1,20 @@
const path = require('path') const path = require('path')
const Application = require('spectron').Application const Application = require('spectron').Application
const fs = require('fs-extra') const fs = require('fs-extra')
const parseTorrent = require('parse-torrent')
const TEST_DATA_DIR = path.join(__dirname, 'tempTestData') const config = require('./config')
const TEST_DOWNLOAD_DIR = path.join(TEST_DATA_DIR, 'Downloads')
module.exports = { module.exports = {
TEST_DATA_DIR,
TEST_DOWNLOAD_DIR,
createApp, createApp,
endTest, endTest,
screenshotCreateOrCompare, screenshotCreateOrCompare,
compareDownloadFolder, compareDownloadFolder,
compareFiles,
compareTorrentFiles,
waitForLoad, waitForLoad,
wait, wait,
wipeTestDataDir resetTestDataDir,
deleteTestDataDir
} }
// Runs WebTorrent Desktop. // Runs WebTorrent Desktop.
@@ -92,16 +92,22 @@ function screenshotCreateOrCompare (app, t, name) {
} }
// Resets the test directory, containing config.json, torrents, downloads, etc // Resets the test directory, containing config.json, torrents, downloads, etc
function wipeTestDataDir () { function resetTestDataDir () {
fs.removeSync(TEST_DATA_DIR) fs.removeSync(config.TEST_DIR)
fs.mkdirpSync(TEST_DOWNLOAD_DIR) // Downloads/ is inside of TEST_DATA_DIR // Create TEST_DIR as well as /Downloads and /Desktop
fs.mkdirpSync(config.TEST_DIR_DOWNLOAD)
fs.mkdirpSync(config.TEST_DIR_DESKTOP)
}
function deleteTestDataDir () {
fs.removeSync(config.TEST_DIR)
} }
// Checks a given folder under Downloads. // Checks a given folder under Downloads.
// Makes sure that the filenames match exactly. // Makes sure that the filenames match exactly.
// If `filenames` is null, asserts that the folder doesn't exist. // If `filenames` is null, asserts that the folder doesn't exist.
function compareDownloadFolder (t, dirname, filenames) { function compareDownloadFolder (t, dirname, filenames) {
const dirpath = path.join(TEST_DOWNLOAD_DIR, dirname) const dirpath = path.join(config.TEST_DIR_DOWNLOAD, dirname)
try { try {
const actualFilenames = fs.readdirSync(dirpath) const actualFilenames = fs.readdirSync(dirpath)
const expectedSorted = filenames.slice().sort() const expectedSorted = filenames.slice().sort()
@@ -116,3 +122,26 @@ function compareDownloadFolder (t, dirname, filenames) {
} }
} }
} }
// Makes sure two files have identical contents
function compareFiles (t, pathActual, pathExpected) {
const bufActual = fs.readFileSync(pathActual)
const bufExpected = fs.readFileSync(pathExpected)
const match = Buffer.compare(bufActual, bufExpected) === 0
t.ok(match, 'correct contents: ' + pathActual)
}
// Makes sure two torrents have the same infohash and flags
function compareTorrentFiles (t, pathActual, pathExpected) {
const bufActual = fs.readFileSync(pathActual)
const bufExpected = fs.readFileSync(pathExpected)
const fieldsActual = extractImportantFields(parseTorrent(bufActual))
const fieldsExpected = extractImportantFields(parseTorrent(bufExpected))
t.deepEqual(fieldsActual, fieldsExpected, 'torrent contents: ' + pathActual)
}
function extractImportantFields (parsedTorrent) {
const { infoHash, name, announce, urlList, comment } = parsedTorrent
const priv = parsedTorrent.private
return { infoHash, name, announce, urlList, comment, 'private': priv }
}

View File

@@ -2,34 +2,68 @@ const test = require('tape')
const fs = require('fs-extra') const fs = require('fs-extra')
const path = require('path') const path = require('path')
const setup = require('./setup') const setup = require('./setup')
const config = require('./config')
test('add-torrent', function (t) { test('add-torrent', function (t) {
setup.wipeTestDataDir() setup.resetTestDataDir()
t.timeoutAfter(100e3) t.timeoutAfter(30e3)
const app = setup.createApp() const app = setup.createApp()
setup.waitForLoad(app, t) setup.waitForLoad(app, t)
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny')) .then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny'))
// Add an existing torrent. The corresponding file is not present. Should be at 0% // Add an existing torrent. The corresponding file is not present. Should be at 0%
.then(() => app.client.click('.icon.add')) .then(() => app.electron.ipcRenderer.send('openTorrentFile'))
// The call to dialog.openFiles() is mocked. See mocks.js // The call to dialog.openFiles() is mocked. See mocks.js
.then(() => app.client.waitUntilTextExists('m3.jpg')) .then(() => app.client.waitUntilTextExists('m3.jpg'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'add-torrent-existing-1')) .then(() => setup.screenshotCreateOrCompare(app, t, 'add-torrent-0-percent'))
// Delete the torrent. // Delete the torrent.
.then(() => app.client.moveToObject('.torrent')) .then(() => app.client.moveToObject('.torrent'))
.then(() => setup.wait()) .then(() => setup.wait())
.then(() => app.client.click('.icon.delete')) .then(() => app.client.click('.icon.delete'))
.then(() => app.client.waitUntilTextExists('REMOVE')) .then(() => app.client.waitUntilTextExists('REMOVE'))
.then(() => app.client.click('.control.ok')) .then(() => app.client.click('.control.ok'))
.then(() => setup.wait())
// Add the same existing torrent, this time with the file present. Should be at 100% // Add the same existing torrent, this time with the file present. Should be at 100%
.then(() => fs.copySync( .then(() => fs.copySync(
path.join(__dirname, 'resources', 'm3.jpg'), path.join(__dirname, 'resources', 'm3.jpg'),
path.join(setup.TEST_DOWNLOAD_DIR, 'm3.jpg'))) path.join(config.TEST_DIR_DOWNLOAD, 'm3.jpg')))
.then(() => app.client.click('.icon.add')) .then(() => app.electron.ipcRenderer.send('openTorrentFile'))
.then(() => app.client.waitUntilTextExists('m3.jpg')) .then(() => app.client.waitUntilTextExists('m3.jpg'))
.then(() => setup.wait()) .then(() => setup.wait())
.then(() => setup.screenshotCreateOrCompare(app, t, 'add-torrent-existing-2')) .then(() => setup.screenshotCreateOrCompare(app, t, 'add-torrent-100-percent'))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
})
test('create-torrent', function (t) {
setup.resetTestDataDir()
// Set up the files to seed
fs.copySync(path.join(__dirname, 'resources', 'm3.jpg'), config.SEED_FILES[0])
t.timeoutAfter(30e3)
const app = setup.createApp()
setup.waitForLoad(app, t)
.then(() => app.client.waitUntilTextExists('.torrent-list', 'Big Buck Bunny'))
// Click the + button, open a non-torrent file to seed
.then(() => app.client.click('.icon.add'))
.then(() => app.client.waitUntilTextExists('Create'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'create-torrent-simple'))
// Click to show advanced settings
.then(() => app.client.click('.show-more .control'))
.then(() => app.client.waitUntilTextExists('Comment'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'create-torrent-advanced'))
// Click OK to create the torrent
.then(() => app.client.click('.control.create-torrent'))
.then(() => app.client.waitUntilTextExists('tmp.jpg'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'create-torrent-100-percent'))
// Click "Save Torrent File As..." on the new torrent
.then(() => app.webContents.executeJavaScript(
'dispatch("saveTorrentFileAs", 6)'))
.then(() => setup.wait())
// Mock saves to <temp folder>/Desktop/saved.torrent
.then(() => setup.compareTorrentFiles(t,
config.SAVED_TORRENT_FILE,
path.join(__dirname, 'resources', 'expected-single-file.torrent')))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })

View File

@@ -1,10 +1,11 @@
const test = require('tape') const test = require('tape')
const fs = require('fs-extra') const fs = require('fs-extra')
const setup = require('./setup') const setup = require('./setup')
const config = require('./config')
test('torrent-list: show download path missing', function (t) { test('torrent-list: show download path missing', function (t) {
setup.wipeTestDataDir() setup.resetTestDataDir()
fs.removeSync(setup.TEST_DOWNLOAD_DIR) fs.removeSync(config.TEST_DIR_DOWNLOAD)
t.timeoutAfter(10e3) t.timeoutAfter(10e3)
const app = setup.createApp() const app = setup.createApp()
@@ -24,7 +25,7 @@ test('torrent-list: show download path missing', function (t) {
}) })
test('torrent-list: start, stop, and delete torrents', function (t) { test('torrent-list: start, stop, and delete torrents', function (t) {
setup.wipeTestDataDir() setup.resetTestDataDir()
const app = setup.createApp() const app = setup.createApp()
setup.waitForLoad(app, t, {offline: true}) setup.waitForLoad(app, t, {offline: true})
@@ -35,7 +36,7 @@ test('torrent-list: start, stop, and delete torrents', function (t) {
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-hover')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-hover'))
// Click download on the first torrent, start downloading // Click download on the first torrent, start downloading
.then(() => app.client.click('.icon.download')) .then(() => app.client.click('.icon.download'))
.then(() => app.client.waitUntilTextExists('.torrent-list', '276 MB')) .then(() => app.client.waitUntilTextExists('.torrent-list', 'peer'))
.then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-start-download')) .then(() => setup.screenshotCreateOrCompare(app, t, 'torrent-list-start-download'))
// Click download on the first torrent again, stop downloading // Click download on the first torrent again, stop downloading
.then(() => app.client.click('.icon.download')) .then(() => app.client.click('.icon.download'))
@@ -62,7 +63,7 @@ test('torrent-list: start, stop, and delete torrents', function (t) {
}) })
test('torrent-list: expand torrent, unselect file', function (t) { test('torrent-list: expand torrent, unselect file', function (t) {
setup.wipeTestDataDir() setup.resetTestDataDir()
const app = setup.createApp() const app = setup.createApp()
setup.waitForLoad(app, t, {offline: true}) setup.waitForLoad(app, t, {offline: true})