From 8b2787484a45e2d655e6831a98b8570e8ea971be Mon Sep 17 00:00:00 2001 From: Ross Date: Sun, 22 May 2016 17:11:50 -0400 Subject: [PATCH] initial commit --- .gitignore | 3 + README.md | 28 +++++++++ nightmare-upload.js | 53 +++++++++++++++++ package.json | 37 ++++++++++++ test/files/upload-again.txt | 1 + test/files/upload.txt | 1 + test/fixtures/upload/index.html | 12 ++++ test/index.js | 102 ++++++++++++++++++++++++++++++++ test/mocha.opts | 2 + test/server.js | 18 ++++++ 10 files changed, 257 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 nightmare-upload.js create mode 100644 package.json create mode 100644 test/files/upload-again.txt create mode 100644 test/files/upload.txt create mode 100644 test/fixtures/upload/index.html create mode 100644 test/index.js create mode 100644 test/mocha.opts create mode 100644 test/server.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e4050f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +.DS_Store +test/tmp diff --git a/README.md b/README.md new file mode 100644 index 0000000..c94efcd --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# nightmare-upload +Grants the ability to add files to file inputs for Nightmare 2.x, just like the good ol' days of Nightmare 1.x. + +## Usage +Require the library, passing Nightmare as a reference to add the plugin actions: + +```js +var Nightmare = require('nightmare'); +require('nightmare-upload')(Nightmare); +``` +### .upload(selector, files) +Specify the `files` to add to a `selector` file input. The `files` parameter can be a single string (for a single file) or an array of strings (for multiple files). + +## Important note about setting file upload inputs +This plugin will not work if the Chromium devtools panel is open as Chromium allows only one attachment to the debugger at a time. + +## Example + +```js +var Nightmare = require('nightmare'); +require('nightmare-upload')(Nightmare); +var nightmare = Nightmare(); +nightmare + .goto('http://some-url.tld') + .upload('#some_file_input', '/path/to/my/upload.ext') + .click('#button_that_submits_form_for_upload') + ... +``` diff --git a/nightmare-upload.js b/nightmare-upload.js new file mode 100644 index 0000000..467f6ff --- /dev/null +++ b/nightmare-upload.js @@ -0,0 +1,53 @@ +var debug = require('debug')('nightmare:upload'); + +module.exports = exports = function(Nightmare) { + Nightmare.action('upload', + function(ns, options, parent, win, renderer, done) { + parent.respondTo('upload', function(selector, pathsToUpload, done) { + parent.emit('log', 'paths', pathsToUpload); + try { + //attach the debugger + //NOTE: this will fail if devtools is open + win.webContents.debugger.attach('1.1'); + } catch (e) { + parent.emit('log', 'problem attaching', e); + return done(e); + } + + win.webContents.debugger.sendCommand('DOM.getDocument', {}, function(err, domDocument) { + win.webContents.debugger.sendCommand('DOM.querySelector', { + nodeId: domDocument.root.nodeId, + selector: selector + }, function(err, queryResult) { + //HACK: chromium errors appear to be unpopulated objects? + if (Object.keys(err) + .length > 0) { + parent.emit('log', 'problem selecting', err); + return done(err); + } + win.webContents.debugger.sendCommand('DOM.setFileInputFiles', { + nodeId: queryResult.nodeId, + files: pathsToUpload + }, function(err, setFileResult) { + if (Object.keys(err) + .length > 0) { + parent.emit('log', 'problem setting input', err); + return done(err); + } + win.webContents.debugger.detach(); + done(null, pathsToUpload); + }); + }); + }); + }); + done(); + }, + function(selector, pathsToUpload, done) { + if(!Array.isArray(pathsToUpload)){ + pathsToUpload = [pathsToUpload]; + } + this.child.call('upload', selector, pathsToUpload, (err, stuff) => { + done(err, stuff); + }); + }) +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..27763f0 --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "nightmare-upload", + "version": "0.1.0", + "license": "MIT", + "main": "./nightmare-upload.js", + "description":"upload files using NightmareJS", + "scripts": { + "test": "mocha test" + }, + "repository": { + "type": "git", + "url": "https://github.com/rosshinkley/nightmare-upload.git" + }, + "author": "Ross Hinkley", + "keywords": [ + "nightmare", + "upload" + ], + "peerDependencies":{ + "nightmare": "^2.3.0" + }, + "dependencies": { + "debug": "^2.2.0" + }, + "devDependencies": { + "rimraf": "^2.4.3", + "mkdirp": "^0.5.1", + "basic-auth": "^1.0.3", + "basic-auth-connect": "^1.0.0", + "chai": "^3.4.1", + "express": "^4.13.3", + "mocha-generators": "^1.2.0", + "mocha": "^2.3.0", + "multer": "1.1.0", + "serve-static": "^1.10.0" + } +} diff --git a/test/files/upload-again.txt b/test/files/upload-again.txt new file mode 100644 index 0000000..b35d34d --- /dev/null +++ b/test/files/upload-again.txt @@ -0,0 +1 @@ +this is an upload test! again! diff --git a/test/files/upload.txt b/test/files/upload.txt new file mode 100644 index 0000000..6c945ff --- /dev/null +++ b/test/files/upload.txt @@ -0,0 +1 @@ +this is an upload test! diff --git a/test/fixtures/upload/index.html b/test/fixtures/upload/index.html new file mode 100644 index 0000000..5ea8bb6 --- /dev/null +++ b/test/fixtures/upload/index.html @@ -0,0 +1,12 @@ + + + + Uploade + + +
+ + +
+ + diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..4902cb9 --- /dev/null +++ b/test/index.js @@ -0,0 +1,102 @@ +/** + * Module dependencies. + */ + +require('mocha-generators') + .install(); + +var Nightmare = require('nightmare'); +var should = require('chai') + .should(); +var url = require('url'); +var server = require('./server'); +var fs = require('fs'); +var mkdirp = require('mkdirp'); +var path = require('path'); +var rimraf = require('rimraf'); + +/** + * Temporary directory + */ + +var tmp_dir = path.join(__dirname, 'tmp') + +/** + * Get rid of a warning. + */ + +process.setMaxListeners(0); + +/** + * Locals. + */ + +var base = 'http://localhost:7500/'; + +describe('Nightmare Upload', function() { + before(function(done) { + require('../nightmare-upload')(Nightmare); + server.listen(7500, done); + }); + + it('should be constructable', function * () { + var nightmare = Nightmare(); + nightmare.should.be.ok; + yield nightmare.end(); + }); + + describe('upload', function() { + it('should upload a single file', function * () { + var nightmare = new Nightmare(); + var files = yield nightmare.goto(fixture('upload')) + .upload('input[type=file]', path.resolve(__dirname, 'files', 'upload.txt')) + .click('button[type=submit]') + .wait(1000) + .evaluate(function() { + return JSON.parse(document.body.querySelector('pre') + .innerHTML) + }); + files[0].originalname.should.equal('upload.txt'); + yield nightmare.end(); + }); + + it('should upload more than one file', function * () { + var nightmare = new Nightmare(); + var files = yield nightmare.goto(fixture('upload')) + .upload('input[type=file]', [ + path.resolve(__dirname, 'files', 'upload.txt'), + path.resolve(__dirname, 'files', 'upload-again.txt') + ]) + .click('button[type=submit]') + .wait(1000) + .evaluate(function() { + return JSON.parse(document.body.querySelector('pre') + .innerHTML) + }); + files.length.should.equal(2); + files[0].originalname.should.equal('upload.txt'); + files[1].originalname.should.equal('upload-again.txt'); + yield nightmare.end(); + }); + + it('should verify a file exists before upload', function(done) { + new Nightmare() + .goto(fixture('upload')) + .upload('#uploaded_file', 'nope.jpg') + .run(function(err) { + err.should.exist; + done(); + }); + }); + }); +}); + +/** + * Generate a URL to a specific fixture. + * @param {String} path + * @returns {String} + */ + +function fixture(path) { + return url.resolve(base, path); +} diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..1f57fab --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,2 @@ +--slow 3s +--timeout 10s diff --git a/test/server.js b/test/server.js new file mode 100644 index 0000000..de95e21 --- /dev/null +++ b/test/server.js @@ -0,0 +1,18 @@ +var express = require('express'); +var multer = require('multer'); +var path = require('path'); +var serve = require('serve-static'); +var debug= require('debug')('nightmare:server'); + +var app = module.exports = express(); + +app.use(multer({ inMemory: true }).any()); + +app.post('/upload', function (req, res) { + debug('uploading', req.files); + res.send(req.files); +}); + +app.use(serve(path.resolve(__dirname, 'fixtures'))); + +if (!module.parent) app.listen(7500);