├── test
├── mocha.opts
├── files
│ ├── upload.txt
│ └── upload-again.txt
├── fixtures
│ └── upload
│ │ └── index.html
├── server.js
└── index.js
├── .gitignore
├── package.json
├── README.md
└── nightmare-upload.js
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --slow 3s
2 | --timeout 10s
3 |
--------------------------------------------------------------------------------
/test/files/upload.txt:
--------------------------------------------------------------------------------
1 | this is an upload test!
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | test/tmp
4 |
--------------------------------------------------------------------------------
/test/files/upload-again.txt:
--------------------------------------------------------------------------------
1 | this is an upload test! again!
2 |
--------------------------------------------------------------------------------
/test/fixtures/upload/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Uploade
5 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var multer = require('multer');
3 | var path = require('path');
4 | var serve = require('serve-static');
5 | var debug= require('debug')('nightmare:server');
6 |
7 | var app = module.exports = express();
8 |
9 | app.use(multer({ inMemory: true }).any());
10 |
11 | app.post('/upload', function (req, res) {
12 | debug('uploading', req.files);
13 | res.send(req.files);
14 | });
15 |
16 | app.use(serve(path.resolve(__dirname, 'fixtures')));
17 |
18 | if (!module.parent) app.listen(7500);
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nightmare-upload",
3 | "version": "0.1.1",
4 | "license": "MIT",
5 | "main": "./nightmare-upload.js",
6 | "description":"upload files using NightmareJS",
7 | "scripts": {
8 | "test": "mocha test"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/rosshinkley/nightmare-upload.git"
13 | },
14 | "author": "Ross Hinkley",
15 | "keywords": [
16 | "nightmare",
17 | "upload"
18 | ],
19 | "peerDependencies":{
20 | "nightmare": "^2.3.0"
21 | },
22 | "dependencies": {
23 | "debug": "^2.2.0"
24 | },
25 | "devDependencies": {
26 | "rimraf": "^2.4.3",
27 | "mkdirp": "^0.5.1",
28 | "basic-auth": "^1.0.3",
29 | "basic-auth-connect": "^1.0.0",
30 | "chai": "^3.4.1",
31 | "express": "^4.13.3",
32 | "mocha-generators": "^1.2.0",
33 | "mocha": "^2.3.0",
34 | "multer": "1.1.0",
35 | "serve-static": "^1.10.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nightmare-upload
2 | Grants the ability to add files to file inputs for Nightmare 2.x, just like the good ol' days of Nightmare 1.x.
3 |
4 | ## Usage
5 | Require the library, passing Nightmare as a reference to add the plugin actions:
6 |
7 | ```js
8 | var Nightmare = require('nightmare');
9 | require('nightmare-upload')(Nightmare);
10 | ```
11 | ### .upload(selector, files)
12 | 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).
13 |
14 | ## Important note about setting file upload inputs
15 | This plugin will not work if the Chromium devtools panel is open as Chromium allows only one attachment to the debugger at a time.
16 |
17 | ## Example
18 |
19 | ```js
20 | var Nightmare = require('nightmare');
21 | require('nightmare-upload')(Nightmare);
22 | var nightmare = Nightmare();
23 | nightmare
24 | .goto('http://some-url.tld')
25 | .upload('#some_file_input', '/path/to/my/upload.ext')
26 | .click('#button_that_submits_form_for_upload')
27 | ...
28 | ```
29 |
30 | ## References/Credits
31 |
32 | * @Zn4rk for [bringing this method to my attention](https://github.com/segmentio/nightmare/issues/235#issuecomment-214226205)
33 | * @svbatalov for the [original implementation](https://github.com/electron/electron/issues/749#issuecomment-213822739)
34 |
--------------------------------------------------------------------------------
/nightmare-upload.js:
--------------------------------------------------------------------------------
1 | var debug = require('debug')('nightmare:upload');
2 |
3 | module.exports = exports = function(Nightmare) {
4 | Nightmare.action('upload',
5 | function(ns, options, parent, win, renderer, done) {
6 | parent.respondTo('upload', function(selector, pathsToUpload, done) {
7 | parent.emit('log', 'paths', pathsToUpload);
8 | try {
9 | //attach the debugger
10 | //NOTE: this will fail if devtools is open
11 | win.webContents.debugger.attach('1.1');
12 | } catch (e) {
13 | parent.emit('log', 'problem attaching', e);
14 | return done(e);
15 | }
16 |
17 | win.webContents.debugger.sendCommand('DOM.getDocument', {}, function(err, domDocument) {
18 | win.webContents.debugger.sendCommand('DOM.querySelector', {
19 | nodeId: domDocument.root.nodeId,
20 | selector: selector
21 | }, function(err, queryResult) {
22 | //HACK: chromium errors appear to be unpopulated objects?
23 | if (Object.keys(err)
24 | .length > 0) {
25 | parent.emit('log', 'problem selecting', err);
26 | return done(err);
27 | }
28 | win.webContents.debugger.sendCommand('DOM.setFileInputFiles', {
29 | nodeId: queryResult.nodeId,
30 | files: pathsToUpload
31 | }, function(err, setFileResult) {
32 | if (Object.keys(err)
33 | .length > 0) {
34 | parent.emit('log', 'problem setting input', err);
35 | return done(err);
36 | }
37 | win.webContents.debugger.detach();
38 | done(null, pathsToUpload);
39 | });
40 | });
41 | });
42 | });
43 | done();
44 | },
45 | function(selector, pathsToUpload, done) {
46 | if(!Array.isArray(pathsToUpload)){
47 | pathsToUpload = [pathsToUpload];
48 | }
49 | this.child.call('upload', selector, pathsToUpload, (err, stuff) => {
50 | done(err, stuff);
51 | });
52 | })
53 | }
54 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Module dependencies.
3 | */
4 |
5 | require('mocha-generators')
6 | .install();
7 |
8 | var Nightmare = require('nightmare');
9 | var should = require('chai')
10 | .should();
11 | var url = require('url');
12 | var server = require('./server');
13 | var fs = require('fs');
14 | var mkdirp = require('mkdirp');
15 | var path = require('path');
16 | var rimraf = require('rimraf');
17 |
18 | /**
19 | * Temporary directory
20 | */
21 |
22 | var tmp_dir = path.join(__dirname, 'tmp')
23 |
24 | /**
25 | * Get rid of a warning.
26 | */
27 |
28 | process.setMaxListeners(0);
29 |
30 | /**
31 | * Locals.
32 | */
33 |
34 | var base = 'http://localhost:7500/';
35 |
36 | describe('Nightmare Upload', function() {
37 | before(function(done) {
38 | require('../nightmare-upload')(Nightmare);
39 | server.listen(7500, done);
40 | });
41 |
42 | it('should be constructable', function * () {
43 | var nightmare = Nightmare();
44 | nightmare.should.be.ok;
45 | yield nightmare.end();
46 | });
47 |
48 | describe('upload', function() {
49 | it('should upload a single file', function * () {
50 | var nightmare = new Nightmare();
51 | var files = yield nightmare.goto(fixture('upload'))
52 | .upload('input[type=file]', path.resolve(__dirname, 'files', 'upload.txt'))
53 | .click('button[type=submit]')
54 | .wait(1000)
55 | .evaluate(function() {
56 | return JSON.parse(document.body.querySelector('pre')
57 | .innerHTML)
58 | });
59 | files[0].originalname.should.equal('upload.txt');
60 | yield nightmare.end();
61 | });
62 |
63 | it('should upload more than one file', function * () {
64 | var nightmare = new Nightmare();
65 | var files = yield nightmare.goto(fixture('upload'))
66 | .upload('input[type=file]', [
67 | path.resolve(__dirname, 'files', 'upload.txt'),
68 | path.resolve(__dirname, 'files', 'upload-again.txt')
69 | ])
70 | .click('button[type=submit]')
71 | .wait(1000)
72 | .evaluate(function() {
73 | return JSON.parse(document.body.querySelector('pre')
74 | .innerHTML)
75 | });
76 | files.length.should.equal(2);
77 | files[0].originalname.should.equal('upload.txt');
78 | files[1].originalname.should.equal('upload-again.txt');
79 | yield nightmare.end();
80 | });
81 |
82 | it('should verify a file exists before upload', function(done) {
83 | new Nightmare()
84 | .goto(fixture('upload'))
85 | .upload('#uploaded_file', 'nope.jpg')
86 | .run(function(err) {
87 | err.should.exist;
88 | done();
89 | });
90 | });
91 | });
92 | });
93 |
94 | /**
95 | * Generate a URL to a specific fixture.
96 | * @param {String} path
97 | * @returns {String}
98 | */
99 |
100 | function fixture(path) {
101 | return url.resolve(base, path);
102 | }
103 |
--------------------------------------------------------------------------------