diff --git a/.babelrc b/.babelrc
index 6306550..30366ba 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,3 +1,3 @@
{
- "presets": ['es2015', 'react', 'stage-0']
+ "presets": ['es2015', 'react']
}
\ No newline at end of file
diff --git a/.eslintrc b/.eslintrc
index e89ba50..d6a0709 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,3 +1,21 @@
{
- "extends": "airbnb"
+ "env": {
+ "browser": true,
+ "node": true,
+ "es6": true
+ },
+ "parserOptions": {
+ "ecmaVersion": 6,
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "jsx": true
+ }
+ },
+ "plugins": [
+ "react"
+ ],
+ "extends": [
+ "eslint:recommended",
+ "plugin:react/recommended"
+ ]
}
\ No newline at end of file
diff --git a/examples/basic/client.js b/examples/basic/client.js
index 80a5a79..8a2129e 100644
--- a/examples/basic/client.js
+++ b/examples/basic/client.js
@@ -1,162 +1,167 @@
-var React = require('react');
-var ReactDOM = require('react-dom');
-var FileUploader = require('../../lib');
-var _ = require('lodash');
-
-var MyComponent = React.createClass({
- getInitialState: function() {
- return {
- isPanelOpen: false,
- isDragOver: false,
- files: []
- }
- },
-
- openPanel: function() {
- this.setState({ isPanelOpen: true });
- },
-
- closePanel: function() {
- this.setState({ isPanelOpen: false });
- },
-
- onDragOver: function(e) {
- // your codes here:
- // if you want to check if the files are dragged over
- // a specific DOM node
- //if (e.target === node) {
- // this.setState({
- // isDragOver: true
- // });
- //}
- },
-
- onFileDrop: function({ target }, files) {
- let node = ReactDOM.findDOMNode(this.refs.uploadPanel);
- if (target != node) {
- return false;
- }
-
- files.map(function(_file) {
- if (_file.size > 1000 * 1000) {
- _file.status = FileUploader.status.FAILED;
- _file.error = 'file size exceeded maximum'
- }
- });
+import React, { Component } from 'react';
+import ReactDOM from 'react-dom';
+import _ from 'lodash';
+import * as FileUploader from '../../src/index';
+
+class MyComponent extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ isPanelOpen: false,
+ isDragOver: false,
+ files: [],
+ };
+
+ this.uploadPanel = undefined;
+ this.openPanel = this.openPanel.bind(this);
+ this.closePanel = this.closePanel.bind(this);
+ this.onDragOver = this.onDragOver.bind(this);
+ this.onFileDrop = this.onFileDrop.bind(this);
+ this.onFileProgress = this.onFileProgress.bind(this);
+ this.onFileUpdate = this.onFileUpdate.bind(this);
+ this.setUploadPanelRef = this.setUploadPanelRef.bind(this);
+ }
+
+ openPanel() {
+ this.setState({ isPanelOpen: true });
+ }
+
+ closePanel() {
+ this.setState({ isPanelOpen: false });
+ }
+
+ onDragOver(e) {
+ // your codes here:
+ // if you want to check if the files are dragged over
+ // a specific DOM node
+ const node = ReactDOM.findDOMNode(this.uploadPanel);
+
+ if (this.state.isDragOver !== (e.target === node)) {
+ this.setState({
+ isDragOver: e.target === node,
+ });
+ }
+ }
- this.setState({
- files: this.state.files.concat(files)
- });
+ onFileDrop({ target }, files) {
+ const node = ReactDOM.findDOMNode(this.uploadPanel);
- // if you want to close the panel upon file drop
- this.closePanel();
- },
+ if (target !== node) {
+ this.closePanel();
+ return false;
+ }
- onFileProgress: function(file) {
- var files = this.state.files;
+ files.forEach(item => {
+ if (item.size > 1000 * 1000) {
+ item.status = FileUploader.status.FAILED;
+ item.error = 'file size exceeded maximum';
+ }
+ });
- files.map(function(_file) {
- if (_file.id === file.id) {
- _file = file;
- }
+ this.setState({
+ files: this.state.files.concat(files),
});
+ // if you want to close the panel upon file drop
+ this.closePanel();
+ }
+
+ onFileProgress(file) {
+ const { files = [] } = this.state;
+ const newFiles = files.map(item => item.id === file.id ? file : item);
+
this.setState({
- files: files
+ files: newFiles,
});
-},
+ }
- onFileUpdate: function(file) {
- var files = this.state.files;
+ onFileUpdate(file) {
+ const { files = [] } = this.state;
+ const newFiles = files.map(item => item.id === file.id ? file : item);
- files.map(function(_file) {
- if (_file.id === file.id) {
- _file = file;
- }
- });
-
- this.setState({
- files: files
- });
- },
-
- _getStatusString: function(status) {
- switch (status) {
- case -1:
- return 'failed';
- break;
-
- case 0:
- return 'pending';
- break;
-
- case 1:
- return 'uploading';
- break;
-
- case 2:
- return 'uploaded';
- break;
- }
- },
-
- render: function() {
- var _this = this;
-
- return (
-
-
{ this.props.title }
-
You can upload files with size with 1 MB at maximum
-
-
- {
- !this.state.isDragOver ? 'Drop here' : 'Files detected'
- }
-
-
-
-
Upload List
-
- {
- this.state.files.map(function(file, index) {
- return (
-
-
- { file.name }
- -
- { file.id }
- { file.type }
- { file.size / 1000 / 1000 } MB
- { file.progress }%
-
- {_this._getStatusString(file.status)}
-
- { file.error }
-
-
-
- )
- })
- }
-
-
-
- );
- }
-});
+ this.setState({
+ files: newFiles,
+ });
+ }
+
+ setUploadPanelRef(ref) {
+ this.uploadPanel = ref;
+ }
+
+ getStatusString(status) {
+ switch (status) {
+ case -1:
+ return 'failed';
+
+ case 0:
+ return 'pending';
+
+ case 1:
+ return 'uploading';
-ReactDOM.render(, document.getElementById('app'));
\ No newline at end of file
+ case 2:
+ return 'uploaded';
+
+ default:
+ return '';
+ }
+ }
+
+ render() {
+ return (
+
+
{ this.props.title }
+
You can upload files with size with 1 MB at maximum
+
+
+ {
+ !this.state.isDragOver ? 'Drop here' : 'Files detected'
+ }
+
+
+
+
Upload List
+
+ {
+ this.state.files.map((file, index) => (
+
+
+ - {file.name}
+ -
+ {file.id}
+ {file.type}
+ {file.size / 1000 / 1000} MB
+ {file.progress}%
+
+ {this.getStatusString(file.status)}
+
+ {file.error}
+
+
+
+ ))
+ }
+
+
+
+ );
+ }
+}
+
+ReactDOM.render(, document.getElementById('app'));
diff --git a/examples/basic/package.json b/examples/basic/package.json
index dbfed66..5846c8d 100644
--- a/examples/basic/package.json
+++ b/examples/basic/package.json
@@ -1,15 +1,15 @@
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
- "build": "browserify client.js -o bundle.js -t [ babelify --presets [ es2015 react] ]"
+ "build": "browserify client.js -o bundle.js -t [ babelify --presets [ es2015 react ] ]"
},
"dependencies": {
"connect-multiparty": "^2.0.0",
"express": "^4.13.3",
"forever": "^0.15.1",
"lodash": "^4.13.1",
- "react": "^0.14.3",
- "react-dom": "^0.14.3"
+ "react": "^16.0.0",
+ "react-dom": "^16.0.0"
},
"devDependencies": {
"babel-preset-es2015": "^6.1.18",
diff --git a/package.json b/package.json
index 0cb7986..ca26bc0 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,12 @@
{
"name": "react-file-uploader",
- "version": "0.3.7",
+ "version": "0.4.0",
"description": "A set of file-upload-components with React.js.",
"main": "lib/index.js",
"scripts": {
"clean": "rm -rf lib",
- "test": "jest fake-test.js",
- "test:report": "jest fake-test.js --coverage",
+ "test": "jest",
+ "test:report": "jest --coverage",
"build:lib": "babel src --out-dir lib",
"build": "npm run eslint && npm run test && npm run clean && npm run build:lib",
"eslint": "eslint ./src/*.js ./src/**/*.js",
@@ -36,22 +36,17 @@
"babel-jest": "*",
"babel-preset-es2015": "^6.1.18",
"babel-preset-react": "^6.1.18",
- "babel-preset-stage-0": "^6.1.18",
- "eslint": "^2.11.1",
- "eslint-config-airbnb": "^9.0.1",
- "eslint-plugin-import": "^1.8.1",
- "eslint-plugin-jsx-a11y": "^1.3.0",
- "eslint-plugin-react": "^5.1.1",
+ "enzyme": "^3.3.0",
+ "enzyme-adapter-react-16": "^1.1.1",
+ "eslint": "^4.19.1",
+ "eslint-plugin-react": "^7.7.0",
"jest-cli": "*",
"jsdom": "^7.0.2",
"nock": "^8.0.0",
- "prop-types": "^15.5.10",
- "react": "^0.14.8 || ^15.0.0",
- "react-addons-test-utils": "^0.14.8 || ^15.0.0",
- "react-dom": "^0.14.8 || ^15.0.0"
+ "react-dom": "^15.0.0 || ^16.0.0"
},
"peerDependencies": {
- "react": "^0.14.8 || ^15.0.0"
+ "react": "^15.0.0 || ^16.0.0"
},
"jest": {
"scriptPreprocessor": "/node_modules/babel-jest",
@@ -74,6 +69,8 @@
"debug": "^2.2.0",
"invariant": "^2.2.0",
"lodash": ">=3.10.1",
+ "prop-types": "^15.5.10",
+ "react": "^15.0.0 || ^16.0.0",
"shortid": "^2.2.6",
"superagent": "^1.4.0"
}
diff --git a/src/Receiver.js b/src/Receiver.js
index 64fafc1..fcf10e8 100644
--- a/src/Receiver.js
+++ b/src/Receiver.js
@@ -9,7 +9,16 @@ class Receiver extends Component {
constructor(props) {
super(props);
- this.wrapper = document.getElementById(this.props.wrapperId) || window;
+ this.wrapper = window;
+
+ if (props.wrapperId) {
+ this.wrapper = document.getElementById(props.wrapperId);
+ }
+
+ if (!this.wrapper) {
+ throw new Error(`wrapper element with Id ${props.wrapperId} not found.`);
+ }
+
this.onDragEnter = this.onDragEnter.bind(this);
this.onDragOver = this.onDragOver.bind(this);
this.onDragLeave = this.onDragLeave.bind(this);
@@ -24,8 +33,8 @@ class Receiver extends Component {
componentDidMount() {
invariant(
- !!window.DragEvent && !!window.DataTransfer,
- 'Upload end point must be provided to upload files'
+ (window.DragEvent || window.Event) && window.DataTransfer,
+ 'Browser does not support DnD events or File API.'
);
this.wrapper.addEventListener('dragenter', this.onDragEnter);
@@ -34,6 +43,13 @@ class Receiver extends Component {
this.wrapper.addEventListener('drop', this.onFileDrop);
}
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.wrapperId !== this.props.wrapperId) {
+ // eslint-disable-next-line no-console
+ console.warn('[Receiver.js] Change in props.wrapperId is unexpected, no new event listeners will be created.');
+ }
+ }
+
componentWillUnmount() {
this.wrapper.removeEventListener('dragenter', this.onDragEnter);
this.wrapper.removeEventListener('dragleave', this.onDragLeave);
@@ -42,6 +58,10 @@ class Receiver extends Component {
}
onDragEnter(e) {
+ if (!e.dataTransfer.types.includes('Files')) {
+ return;
+ }
+
const dragLevel = this.state.dragLevel + 1;
this.setState({ dragLevel });
@@ -73,10 +93,10 @@ class Receiver extends Component {
const files = [];
- if (!!e.dataTransfer) {
+ if (e.dataTransfer) {
const fileList = e.dataTransfer.files || [];
- for (let i = 0; i < fileList.length; i ++) {
+ for (let i = 0; i < fileList.length; i++) {
fileList[i].id = shortid.generate();
fileList[i].status = status.PENDING;
fileList[i].progress = 0;
@@ -122,4 +142,8 @@ Receiver.propTypes = {
wrapperId: PropTypes.string,
};
+Receiver.defaultProps = {
+ isOpen: false
+};
+
export default Receiver;
diff --git a/src/UploadHandler.js b/src/UploadHandler.js
index bb015ae..8183b37 100644
--- a/src/UploadHandler.js
+++ b/src/UploadHandler.js
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import uploadStatus from './constants/status';
-const debug = require('debug')('react-file-upload:UploadHandler');
+const debug = require('debug')('react-file-uploader:UploadHandler');
class UploadHandler extends Component {
componentDidMount() {
@@ -35,11 +35,11 @@ class UploadHandler extends Component {
}
render() {
- const { component, key, customClass, style } = this.props;
+ const { component, customClass, style } = this.props;
return React.createElement(
component,
- { key, className: classNames(customClass), style },
+ { className: classNames(customClass), style },
this.props.children
);
}
@@ -57,7 +57,6 @@ UploadHandler.propTypes = {
PropTypes.arrayOf(PropTypes.string),
]),
file: PropTypes.object.isRequired,
- key: PropTypes.string,
style: PropTypes.object,
upload: PropTypes.func,
};
diff --git a/src/UploadManager.js b/src/UploadManager.js
index fffb115..7778c12 100644
--- a/src/UploadManager.js
+++ b/src/UploadManager.js
@@ -2,13 +2,12 @@ import React, { Component, cloneElement } from 'react';
import PropTypes from 'prop-types';
import invariant from 'invariant';
import classNames from 'classnames';
-import assign from 'lodash/assign';
import bindKey from 'lodash/bindKey';
import clone from 'lodash/clone';
-import request from 'superagent';
+import superagent from 'superagent';
import uploadStatus from './constants/status';
-const debug = require('debug')('react-file-upload:UploadManager');
+const debug = require('debug')('react-file-uploader:UploadManager');
class UploadManager extends Component {
constructor(props) {
@@ -31,30 +30,46 @@ class UploadManager extends Component {
upload(url, file) {
const {
+ reqConfigs: {
+ accept = 'application/json',
+ method = 'post',
+ timeout,
+ withCredentials = false,
+ },
onUploadStart,
onUploadEnd,
onUploadProgress,
+ formDataParser,
uploadErrorHandler,
uploadHeader = {},
} = this.props;
if (typeof onUploadStart === 'function') {
- onUploadStart(assign(file, { status: uploadStatus.UPLOADING }));
+ onUploadStart(Object.assign(file, { status: uploadStatus.UPLOADING }));
}
- const formData = new FormData();
- formData.append('file', file);
+ let formData = new FormData();
+ formData = formDataParser(formData, file);
+
+ const request = superagent[method.toLowerCase()](url)
+ .accept(accept)
+ .set(uploadHeader);
+
+ if (timeout) {
+ request.timeout(timeout);
+ }
+
+ if (withCredentials) {
+ request.withCredentials();
+ }
debug(`start uploading file#${file.id} to ${url}`, file);
request
- .post(url)
- .accept('application/json')
- .set(uploadHeader)
.send(formData)
.on('progress', ({ percent }) => {
if (typeof onUploadProgress === 'function') {
- onUploadProgress(assign(file, {
+ onUploadProgress(Object.assign(file, {
progress: percent,
status: uploadStatus.UPLOADING,
}));
@@ -65,18 +80,17 @@ class UploadManager extends Component {
if (error) {
debug('failed to upload file', error);
-
- if (typeof onUploadEnd === 'function') {
- onUploadEnd(assign(file, { error, status: uploadStatus.FAILED }));
- }
-
- return;
+ } else {
+ debug('succeeded to upload file', result);
}
- debug('succeeded to upload file', res);
-
if (typeof onUploadEnd === 'function') {
- onUploadEnd(assign(file, { result, status: uploadStatus.UPLOADED }));
+ onUploadEnd(Object.assign(file, {
+ progress: error && 0 || 100,
+ error,
+ result: error && undefined || result,
+ status: error && uploadStatus.FAILED || uploadStatus.UPLOADED
+ }));
}
});
}
@@ -87,7 +101,7 @@ class UploadManager extends Component {
return React.createElement(
component,
{ className: classNames(customClass), style },
- React.Children.map(children, child => cloneElement(child, assign({
+ React.Children.map(children, child => cloneElement(child, Object.assign({
upload: bindKey(this, 'upload', uploadUrl, child.props.file),
}, child.props)))
);
@@ -104,18 +118,33 @@ UploadManager.propTypes = {
PropTypes.string,
PropTypes.arrayOf(PropTypes.string),
]),
+ formDataParser: PropTypes.func,
onUploadStart: PropTypes.func,
onUploadProgress: PropTypes.func,
onUploadEnd: PropTypes.func.isRequired,
+ reqConfigs: PropTypes.shape({
+ accept: PropTypes.string,
+ method: PropTypes.string,
+ timeout: PropTypes.shape({
+ response: PropTypes.number,
+ deadline: PropTypes.number,
+ }),
+ withCredentials: PropTypes.bool,
+ }),
style: PropTypes.object,
- uploadErrorHandler: PropTypes.func.isRequired,
+ uploadErrorHandler: PropTypes.func,
uploadUrl: PropTypes.string.isRequired,
uploadHeader: PropTypes.object,
};
UploadManager.defaultProps = {
component: 'ul',
- uploadErrorHandler: (err, res) => {
+ formDataParser: (formData, file) => {
+ formData.append('file', file);
+ return formData;
+ },
+ reqConfigs: {},
+ uploadErrorHandler: (err, res = {}) => {
let error = null;
const body = clone(res.body);
diff --git a/src/__tests__/Receiver-test.js b/src/__tests__/Receiver-test.js
index 1b9ffc0..84a36dc 100644
--- a/src/__tests__/Receiver-test.js
+++ b/src/__tests__/Receiver-test.js
@@ -4,212 +4,254 @@ jest.dontMock('../index');
jest.dontMock('classnames');
import React from 'react';
-import ReactDOM from 'react-dom';
+import { shallow, configure } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
import { jsdom } from 'jsdom';
const FileUploader = require('../index');
const { PENDING } = FileUploader.status;
const Receiver = FileUploader.Receiver;
-const createComponent = (onDragEnter, onDragOver, onDragLeave, onFileDrop) => (
-
-);
-
-// eslint-disable-next-line react/prefer-es6-class
-const createTemplate = (initialState, props, Component) => React.createClass({
- getInitialState() { return initialState; },
- render() {
- return (
-
- Test
-
- );
- },
-});
+configure({ adapter: new Adapter() });
+
+const testFile = {
+ lastModified: 1465229147840,
+ lastModifiedDate: 'Tue Jun 07 2016 00:05:47 GMT+0800 (HKT)',
+ name: 'test.jpg',
+ size: 1024,
+ type: 'image/jpg',
+ webkitRelativePath: '',
+};
+
+const files = [testFile];
+
+const createEvent = (eventName) => {
+ const event = document.createEvent('HTMLEvents');
+ event.initEvent(eventName, false, true);
+ event.preventDefault = jest.genMockFn();
+ event.dataTransfer = {
+ files,
+ setData: jest.genMockFunction(),
+ types: ['Files']
+ };
+
+ return event;
+};
describe('Receiver', () => {
- beforeEach(function setting() {
+ let dragEnterEvent,
+ dragOverEvent,
+ dragLeaveEvent,
+ dropEvent,
+ stringClass = 'receiver',
+ arrayClass = ['react', 'receiver'];
+
+ beforeEach(() => {
global.document = jsdom();
global.window = document.parentWindow;
+ global.window.DragEvent = 'DragEvent';
+ global.window.DataTransfer = 'DataTransfer';
+
+ dragEnterEvent = createEvent('dragenter');
+ dragOverEvent = createEvent('dragover');
+ dragLeaveEvent = createEvent('dragleave');
+ dropEvent = createEvent('drop');
+ });
+
+ describe('constructor', () => {
+ let emptyFn = () => {},
+ component = (
+
+ );
+
+ it('should throw an error if DnD or File API is not supported', () => {
+ global.window.DragEvent = undefined;
+ global.window.DataTransfer = undefined;
+
+ expect(() => shallow(component)).toThrow('Browser does not support DnD events or File API.');
+ });
- const testFile = {
- lastModified: 1465229147840,
- lastModifiedDate: 'Tue Jun 07 2016 00:05:47 GMT+0800 (HKT)',
- name: 'test.jpg',
- size: 1024,
- type: 'image/jpg',
- webkitRelativePath: '',
- };
- const files = [testFile];
-
- const mockDT = {
- files,
- setData: jest.genMockFunction(),
- };
-
- this.stringClass = 'receiver';
- this.arrayClass = ['react', 'receiver'];
-
- this.dragEnterEvent = document.createEvent('HTMLEvents');
- this.dragEnterEvent.initEvent('dragenter', false, true);
-
- this.dragOverEvent = document.createEvent('HTMLEvents');
- this.dragOverEvent.initEvent('dragover', false, true);
- this.dragOverEvent.preventDefault = jest.genMockFn();
-
- this.dragLeaveEvent = document.createEvent('HTMLEvents');
- this.dragLeaveEvent.initEvent('dragleave', false, true);
-
- this.dropEvent = document.createEvent('HTMLEvents');
- this.dropEvent.initEvent('drop', false, true);
- this.dropEvent.preventDefault = jest.genMockFn();
- this.dropEvent.dataTransfer = mockDT;
+ it('should assign window to this.wrapper if no wrapperId is provided', () => {
+ const receiver = shallow(component);
+ expect(receiver.instance().wrapper).toEqual(global.window);
+ });
+
+ it('should throw an error if wrapperId is given but element is not found', () => {
+ expect(() => shallow(
+
+ )).toThrow();
+ });
});
describe('state of dragLevel', () => {
- beforeEach(function setting() {
- const onDragEnter = jest.genMockFn();
- const onDragOver = jest.genMockFn();
- const onDragLeave = jest.genMockFn();
- const onFileDrop = jest.genMockFn();
-
- this.onDragEnter = onDragEnter;
- this.onDragOver = onDragOver;
- this.onDragLeave = onDragLeave;
- this.onFileDrop = onFileDrop;
-
- const Component = createComponent(onDragEnter, onDragOver, onDragLeave, onFileDrop);
- const template = createTemplate({ isOpen: false, files: [] }, {}, Component);
-
- this.createTestParent = React.createFactory(template);
- this.ParentComponent = this.createTestParent();
- this.container = document.createElement('div');
- this.instance = ReactDOM.render(this.ParentComponent, this.container);
- this.receiver = this.instance.refs.receiver;
+ let receiver,
+ onDragEnter,
+ onDragOver,
+ onDragLeave,
+ onFileDrop;
+
+ beforeEach(() => {
+ const mockOnDragEnter = jest.genMockFn();
+ const mockOnDragOver = jest.genMockFn();
+ const mockOnDragLeave = jest.genMockFn();
+ const mockOnFileDrop = jest.genMockFn();
+
+ onDragEnter = mockOnDragEnter;
+ onDragOver = mockOnDragOver;
+ onDragLeave = mockOnDragLeave;
+ onFileDrop = mockOnFileDrop;
+
+ const component = (
+
+ );
+
+ receiver = shallow(component);
});
- it('should increase state of dragLevel by 1 with dragEnter event', function test() {
- const oldDragLevel = this.receiver.state.dragLevel;
- window.dispatchEvent(this.dragEnterEvent);
- const newDragLevel = this.receiver.state.dragLevel;
+ it('should increase state of dragLevel by 1 with dragEnter event', () => {
+ const oldDragLevel = receiver.state().dragLevel;
+ window.dispatchEvent(dragEnterEvent);
+ const newDragLevel = receiver.state().dragLevel;
expect(newDragLevel).toEqual(oldDragLevel + 1);
});
- it('should call onDragEnter with dragEnter event if isOpen is false', function test() {
- window.dispatchEvent(this.dragEnterEvent);
- expect(this.onDragEnter).toBeCalled();
+ it('should call onDragEnter with dragEnter event if isOpen is false', () => {
+ window.dispatchEvent(dragEnterEvent);
+ expect(onDragEnter).toBeCalled();
});
- it('should not call onDragEnter with dragEnter event if isOpen is true', function test() {
- this.instance.setState({ isOpen: true });
- window.dispatchEvent(this.dragEnterEvent);
- expect(this.onDragEnter).not.toBeCalled();
+ it('should not call onDragEnter with dragEnter event if isOpen is true', () => {
+ receiver.setProps({ isOpen: true });
+ window.dispatchEvent(dragEnterEvent);
+ expect(onDragEnter).not.toBeCalled();
});
- it('should call event.preventDefault with dragOver event', function test() {
- window.dispatchEvent(this.dragOverEvent);
- expect(this.dragOverEvent.preventDefault).toBeCalled();
+ it('should not call onDragEnter callback with dragEnter event if dataTransfer.types does not include `Files`', () => {
+ onDragEnter = jest.genMockFn();
+ dragEnterEvent.dataTransfer.types = ['HTMLElement'];
+
+ receiver.setProps({ onDragEnter });
+
+ window.dispatchEvent(dragEnterEvent);
+ expect(onDragEnter).not.toBeCalled();
+ });
+
+ it('should call event.preventDefault with dragOver event', () => {
+ window.dispatchEvent(dragOverEvent);
+ expect(dragOverEvent.preventDefault).toBeCalled();
});
- it('should call onDragOver with dragOver event', function test() {
- window.dispatchEvent(this.dragOverEvent);
- expect(this.onDragOver).toBeCalled();
+ it('should call onDragOver with dragOver event', () => {
+ window.dispatchEvent(dragOverEvent);
+ expect(onDragOver).toBeCalled();
});
- it('should decrease state of dragLevel by 1 with dragLeave event', function test() {
- const oldDragLevel = this.receiver.state.dragLevel;
- window.dispatchEvent(this.dragEnterEvent);
- const newDragLevel = this.receiver.state.dragLevel;
+ it('should decrease state of dragLevel by 1 with dragLeave event', () => {
+ const oldDragLevel = receiver.state().dragLevel;
+ window.dispatchEvent(dragEnterEvent);
+ const newDragLevel = receiver.state().dragLevel;
expect(newDragLevel).toEqual(oldDragLevel + 1);
- window.dispatchEvent(this.dragLeaveEvent);
- const finalDragLevel = this.receiver.state.dragLevel;
+ window.dispatchEvent(dragLeaveEvent);
+ const finalDragLevel = receiver.state().dragLevel;
expect(finalDragLevel).toEqual(newDragLevel - 1);
- expect(this.onDragLeave).toBeCalled();
+ expect(onDragLeave).toBeCalled();
});
- it('should call onDragLeave if state of dragLevel is not 0', function test() {
- const oldDragLevel = this.receiver.state.dragLevel;
- window.dispatchEvent(this.dragEnterEvent);
- const newDragLevel = this.receiver.state.dragLevel;
+ it('should call onDragLeave if state of dragLevel is not 0', () => {
+ const oldDragLevel = receiver.state().dragLevel;
+ window.dispatchEvent(dragEnterEvent);
+ const newDragLevel = receiver.state().dragLevel;
expect(newDragLevel).toEqual(oldDragLevel + 1);
- window.dispatchEvent(this.dragEnterEvent);
- const newerDragLevel = this.receiver.state.dragLevel;
+ window.dispatchEvent(dragEnterEvent);
+ const newerDragLevel = receiver.state().dragLevel;
expect(newerDragLevel).toEqual(newDragLevel + 1);
- window.dispatchEvent(this.dragLeaveEvent);
- const finalDragLevel = this.receiver.state.dragLevel;
+ window.dispatchEvent(dragLeaveEvent);
+ const finalDragLevel = receiver.state().dragLevel;
expect(finalDragLevel).toEqual(newerDragLevel - 1);
- expect(this.onDragLeave).not.toBeCalled();
+ expect(onDragLeave).not.toBeCalled();
- window.dispatchEvent(this.dragLeaveEvent);
- const endDragLevel = this.receiver.state.dragLevel;
+ window.dispatchEvent(dragLeaveEvent);
+ const endDragLevel = receiver.state().dragLevel;
expect(endDragLevel).toEqual(finalDragLevel - 1);
- expect(this.onDragLeave).toBeCalled();
+ expect(onDragLeave).toBeCalled();
});
- it('should call event.preventDefault with drop event', function test() {
- window.dispatchEvent(this.dropEvent);
+ it('should call event.preventDefault with drop event', () => {
+ window.dispatchEvent(dropEvent);
// eslint-disable-next-line no-undef
- expect(this.dropEvent.preventDefault).toBeCalled();
+ expect(dropEvent.preventDefault).toBeCalled();
});
- it('should call onFileDrop with drop event', function test() {
- window.dispatchEvent(this.dropEvent);
- expect(this.onFileDrop).toBeCalled();
+ it('should call onFileDrop with drop event', () => {
+ window.dispatchEvent(dropEvent);
+ expect(onFileDrop).toBeCalled();
});
- it('should set state of dragLevel to 0 with dragEnter event', function test() {
- const oldDragLevel = this.receiver.state.dragLevel;
- window.dispatchEvent(this.dragEnterEvent);
- const newDragLevel = this.receiver.state.dragLevel;
+ it('should set state of dragLevel to 0 with dragEnter event', () => {
+ const oldDragLevel = receiver.state().dragLevel;
+ window.dispatchEvent(dragEnterEvent);
+ const newDragLevel = receiver.state().dragLevel;
expect(newDragLevel).toEqual(oldDragLevel + 1);
- window.dispatchEvent(this.dropEvent);
- const finalDragLevel = this.receiver.state.dragLevel;
+ window.dispatchEvent(dropEvent);
+ const finalDragLevel = receiver.state().dragLevel;
expect(finalDragLevel).toEqual(0);
});
- it('should not call any callback after Receiver did unmount', function test() {
- ReactDOM.unmountComponentAtNode(this.container);
- window.dispatchEvent(this.dragEnterEvent);
- expect(this.onDragEnter).not.toBeCalled();
+ it('should not call any callback after Receiver did unmount', () => {
+ receiver.unmount();
+ window.dispatchEvent(dragEnterEvent);
+ expect(onDragEnter).not.toBeCalled();
- window.dispatchEvent(this.dragOverEvent);
- expect(this.onDragOver).not.toBeCalled();
+ window.dispatchEvent(dragOverEvent);
+ expect(onDragOver).not.toBeCalled();
- window.dispatchEvent(this.dragLeaveEvent);
- expect(this.onDragLeave).not.toBeCalled();
+ window.dispatchEvent(dragLeaveEvent);
+ expect(onDragLeave).not.toBeCalled();
- window.dispatchEvent(this.dropEvent);
- expect(this.onFileDrop).not.toBeCalled();
+ window.dispatchEvent(dropEvent);
+ expect(onFileDrop).not.toBeCalled();
});
});
describe('callbacks and callback arguments', () => {
- beforeEach(function setting() {
- const onDragEnter = (e) => {
+ let onDragEnter,
+ onDragOver,
+ onDragLeave,
+ onFileDrop;
+
+ beforeEach(() => {
+ const mockOnDragEnter = (e) => {
expect(e.type).toBe('dragenter');
};
- const onDragOver = (e) => {
+ const mockOnDragOver = (e) => {
expect(e.type).toBe('dragover');
};
- const onDragLeave = (e) => {
+ const mockOnDragLeave = (e) => {
expect(e.type).toBe('dragleave');
};
- const onFileDrop = (e, _files) => {
+ const mockOnFileDrop = (e, _files) => {
expect(e.type).toBe('drop');
const file = _files[0];
expect(file.lastModified).toBe(testFile.lastModified);
@@ -223,90 +265,90 @@ describe('Receiver', () => {
expect(file.src).toBe(null);
};
- this.onDragEnter = onDragEnter;
- this.onDragOver = onDragOver;
- this.onDragLeave = onDragLeave;
- this.onFileDrop = onFileDrop;
-
- const Component = createComponent(onDragEnter, onDragOver, onDragLeave, onFileDrop);
- const template = createTemplate({ isOpen: false, files: [] }, {}, Component);
-
- this.createTestParent = React.createFactory(template);
- this.ParentComponent = this.createTestParent();
- this.container = document.createElement('div');
- this.instance = ReactDOM.render(this.ParentComponent, this.container);
- this.receiver = this.instance.refs.receiver;
+ onDragEnter = mockOnDragEnter;
+ onDragOver = mockOnDragOver;
+ onDragLeave = mockOnDragLeave;
+ onFileDrop = mockOnFileDrop;
+
+ const component = (
+
+ );
+
+ shallow(component);
});
- it('should execute the onDragEnter callback with a DragEvent with type `dragenter` as argument', function test() {
- window.dispatchEvent(this.dragEnterEvent);
+ it('should execute the onDragEnter callback with a DragEvent with type `dragenter` as argument', () => {
+ window.dispatchEvent(dragEnterEvent);
});
- it('should execute the onDragOver callback with a DragEvent with type `dragover` as argument', function test() {
- window.dispatchEvent(this.dragOverEvent);
+ it('should execute the onDragOver callback with a DragEvent with type `dragover` as argument', () => {
+ window.dispatchEvent(dragOverEvent);
});
- it('should execute the onDragLeave callback with a DragEvent with type `dragleave` as argument', function test() {
- window.dispatchEvent(this.dragLeaveEvent);
+ it('should execute the onDragLeave callback with a DragEvent with type `dragleave` as argument', () => {
+ window.dispatchEvent(dragLeaveEvent);
});
- it('should execute the onFileDrop callback with a DragEvent with type `drop` as argument', function test() {
- window.dispatchEvent(this.dropEvent);
+ it('should execute the onFileDrop callback with a DragEvent with type `drop` as argument', () => {
+ window.dispatchEvent(dropEvent);
});
});
- describe('#render', () => {
- beforeEach(function setting() {
- const Component = createComponent();
- const template = createTemplate({ isOpen: false, files: [] }, {}, Component);
-
- this.createTestParent = React.createFactory(template);
- this.ParentComponent = this.createTestParent();
- this.container = document.createElement('div');
- this.instance = ReactDOM.render(this.ParentComponent, this.container);
- this.receiver = this.instance.refs.receiver;
+ describe('render()', () => {
+ let receiver,
+ childrenItems = Array(5).fill().map((value, index) => ({index}));
+
+ beforeEach(() => {
+ const mockOnDragEnter = jest.genMockFn();
+ const mockOnDragOver = jest.genMockFn();
+ const mockOnDragLeave = jest.genMockFn();
+ const mockOnFileDrop = jest.genMockFn();
+
+ const component = (
+
+ {childrenItems}
+
+ );
+
+ receiver = shallow(component);
});
- it('should render nothing if isOpen is false', function test() {
- const receiver = ReactDOM.findDOMNode(this.receiver);
- expect(receiver).toBeNull();
- this.instance.setState({ isOpen: true });
+ it('should render nothing if isOpen is false', () => {
+ expect(receiver.type()).toEqual(null);
+ expect(receiver.children().exists()).toBe(false);
});
- it('should render a div wrapper with children if isOpen is true', function test() {
- this.instance.setState({ isOpen: true });
- const receiver = ReactDOM.findDOMNode(this.receiver);
- expect(receiver).toEqual(jasmine.any(HTMLDivElement));
- expect(receiver.firstElementChild).toEqual(jasmine.any(HTMLHeadingElement));
+ it('should render a div wrapper with children if isOpen is true', () => {
+ receiver.setProps({ isOpen: true });
+ expect(receiver.type()).toEqual('div');
+ expect(receiver.children().length).toEqual(childrenItems.length);
});
- it('should render a div wrapper with customClass in string', function test() {
- const Component = createComponent();
- const template = createTemplate({ isOpen: true, files: [] }, { customClass: this.stringClass }, Component);
-
- this.createTestParent = React.createFactory(template);
- this.ParentComponent = this.createTestParent();
- this.container = document.createElement('div');
- this.instance = ReactDOM.render(this.ParentComponent, this.container);
- this.receiver = this.instance.refs.receiver;
-
- const receiver = ReactDOM.findDOMNode(this.receiver);
- expect(receiver.className).toEqual(this.stringClass);
+ it('should render a div wrapper with customClass in string', () => {
+ receiver.setProps({ isOpen: true, customClass: stringClass });
+ expect(receiver.hasClass(stringClass)).toBe(true);
});
- it('should render a div wrapper with customClass in array', function test() {
- const Component = createComponent();
- const template = createTemplate({ isOpen: true, files: [] }, { customClass: this.arrayClass }, Component);
-
- this.createTestParent = React.createFactory(template);
- this.ParentComponent = this.createTestParent();
- this.container = document.createElement('div');
- this.instance = ReactDOM.render(this.ParentComponent, this.container);
- this.receiver = this.instance.refs.receiver;
-
- const receiver = ReactDOM.findDOMNode(this.receiver);
- expect(receiver.className).toEqual(this.arrayClass.join(' '));
+ it('should render a div wrapper with customClass in array', () => {
+ receiver.setProps({ isOpen: true, customClass: arrayClass });
+ arrayClass.forEach((classname) => {
+ expect(receiver.hasClass(classname)).toBe(true);
+ });
});
});
});
-/* eslint-enable no-undef */
+/* eslint-enable no-undef, max-len */
diff --git a/src/__tests__/UploadManager-test.js b/src/__tests__/UploadManager-test.js
index f610cf7..4097477 100644
--- a/src/__tests__/UploadManager-test.js
+++ b/src/__tests__/UploadManager-test.js
@@ -5,7 +5,8 @@ jest.dontMock('classnames');
jest.dontMock('lodash');
import React from 'react';
-import ReactDOM from 'react-dom';
+import { shallow, configure } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
import { jsdom } from 'jsdom';
import nock from 'nock';
@@ -13,142 +14,110 @@ const FileUploader = require('../index');
const UploadManager = FileUploader.UploadManager;
const uploadStatus = FileUploader.status;
+configure({ adapter: new Adapter() });
+
describe('UploadManager', () => {
- beforeEach(function setting() {
+ let stringClass = 'receiver',
+ arrayClass = ['react', 'receiver'],
+ uploadPath = 'http://localhost:3000/api/upload',
+ children = (children
),
+ uploadManager,
+ onUploadStart,
+ onUploadProgress,
+ onUploadEnd,
+ formDataParser,
+ err,
+ errorResponse,
+ successResponse,
+ errorHandler;
+
+ beforeEach(() => {
global.document = jsdom();
global.window = document.parentWindow;
- this.stringClass = 'receiver';
- this.arrayClass = ['react', 'receiver'];
- this.style = { display: 'block' };
- this.uploadPath = 'http://localhost:3000/api/upload';
- this.onUploadStart = jest.fn();
- this.onUploadProgress = jest.fn();
- this.onUploadEnd = jest.fn();
-
- this.children = children
;
- this.container = document.createElement('div');
- this.instance = ReactDOM.render(
+ onUploadStart = jest.genMockFn();
+ onUploadProgress = jest.genMockFn();
+ onUploadEnd = jest.genMockFn();
+ formDataParser = jest.genMockFn();
+
+ err = new Error('not found');
+ errorResponse = { body: { success: false, errors: { message: 'not found' } } };
+ successResponse = { body: { success: true } };
+ errorHandler = UploadManager.defaultProps.uploadErrorHandler;
+
+ uploadManager = shallow(
- {this.children}
- ,
- this.container
- );
+ {children}
+
+ )
});
- afterEach(function setting() {
- this.container = null;
- this.instance = null;
+ afterEach(() => {
+ uploadManager = null;
});
- describe('#render()', () => {
- it('should render ul element by default', function test() {
- const node = ReactDOM.findDOMNode(this.instance);
- expect(node).toEqual(jasmine.any(HTMLUListElement));
- expect(node.firstElementChild).toEqual(jasmine.any(HTMLParagraphElement));
+ describe('render()', () => {
+ it('should render ul element by default', () => {
+ expect(uploadManager.type()).toEqual('ul');
+ expect(uploadManager.childAt(0).type()).toEqual('p');
});
- it('should render wrapper element according to component props', function test() {
- this.instance = ReactDOM.render(
-
- {this.children}
- ,
- this.container
- );
- const node = ReactDOM.findDOMNode(this.instance);
- expect(node).toEqual(jasmine.any(HTMLDivElement));
+ it('should render wrapper element according to component props', () => {
+ uploadManager.setProps({ component: 'div' });
+ expect(uploadManager.type()).toEqual('div');
});
- it('should render a wrapper with customClass in string', function test() {
- this.instance = ReactDOM.render(
-
- {this.children}
- ,
- this.container
- );
- const node = ReactDOM.findDOMNode(this.instance);
- expect(node.className).toEqual(this.stringClass);
+ it('should render a wrapper with customClass in string', () => {
+ expect(uploadManager.hasClass(stringClass)).toBe(true);
});
- it('should render a wrapper with customClass in array', function test() {
- this.instance = ReactDOM.render(
-
- {this.children}
- ,
- this.container
- );
- const node = ReactDOM.findDOMNode(this.instance);
- expect(node.className).toEqual(this.arrayClass.join(' '));
- });
- });
+ it('should render a wrapper with customClass in array', () => {
+ uploadManager.setProps({ customClass: arrayClass });
- describe('#uploadErrorHandler()', () => {
- beforeEach(function setting() {
- this.err = new Error('not found');
- this.errorResponse = { body: { success: false, errors: { message: 'not found' } } };
- this.successResponse = { body: { success: true } };
- this.errorHandler = this.instance.props.uploadErrorHandler;
+ arrayClass.forEach((classname) => {
+ expect(uploadManager.hasClass(classname)).toBe(true);
+ });
});
+ });
- it('should return an object contains key of `error` and `result`', function test() {
- const result = this.errorHandler(null, this.successResponse);
+ describe('uploadErrorHandler()', () => {
+ it('should return an object contains key of `error` and `result`', () => {
+ const result = errorHandler(null, successResponse);
expect(result.error).toBeNull();
- expect(result.result).toEqual(this.successResponse.body);
+ expect(result.result).toEqual(successResponse.body);
});
- it('should return an object with key of `error` with value equals to the first argument if it is not empty', function test() {
- const result = this.errorHandler(this.err, this.successResponse);
- expect(result.error).toEqual(this.err.message);
- expect(result.result).toEqual(this.successResponse.body);
+ it('should return an object with key of `error` with value equals to the first argument if it is not empty', () => {
+ const result = errorHandler(err, successResponse);
+ expect(result.error).toEqual(err.message);
+ expect(result.result).toEqual(successResponse.body);
});
- it('should return an object with key of `error` with value equals to the value of `body.error` of the second argument if it is not empty', function test() {
- const result = this.errorHandler(null, this.errorResponse);
- expect(result.error).toEqual(this.errorResponse.body.errors);
- delete this.errorResponse.body.errors;
- expect(result.result).toEqual(this.errorResponse.body);
+ it('should return an object with key of `error` with value equals to the value of `body.error` of the second argument if it is not empty', () => {
+ const result = errorHandler(null, errorResponse);
+ expect(result.error).toEqual(errorResponse.body.errors);
+ delete errorResponse.body.errors;
+ expect(result.result).toEqual(errorResponse.body);
});
});
- describe('#upload()', () => {
- beforeEach(function setting() {
+ describe('upload()', () => {
+ let file;
+
+ beforeEach(() => {
+ file = {};
+
nock('http://localhost:3000')
.filteringRequestBody(() => '*')
.post('/api/upload', '*')
- .reply(200, this.successResponse);
-
- this.instance = ReactDOM.render(
-
- {this.children}
- ,
- this.container
- );
- this.errorResponse = { success: false, errors: { message: 'not found' } };
- this.successResponse = { success: true };
+ .reply(200, successResponse);
});
afterEach(() => {
@@ -156,9 +125,18 @@ describe('UploadManager', () => {
nock.enableNetConnect();
});
- it('should call onUploadStart prop functions if it is given', function test() {
- this.instance.upload(this.instance.props.uploadUrl, {});
- expect(this.onUploadStart).toBeCalledWith({ status: uploadStatus.UPLOADING });
+ it('should call `props.onUploadStart` function if it is given', () => {
+ const instance = uploadManager.instance();
+ instance.upload(instance.props.uploadUrl, file);
+ expect(onUploadStart).toBeCalledWith(Object.assign({}, file, { status: uploadStatus.UPLOADING }));
+ expect(file).toEqual({ status: uploadStatus.UPLOADING });
+ });
+
+ it('should call `props.formDataParser` function if it is given', () => {
+ const instance = uploadManager.instance();
+ instance.upload(instance.props.uploadUrl, {});
+ expect(formDataParser).toBeCalledWith(new FormData(), { status: uploadStatus.UPLOADING });
});
});
});
+/* eslint-enable no-undef, max-len */
diff --git a/src/__tests__/fake-test.js b/src/__tests__/fake-test.js
deleted file mode 100644
index 3c8eb54..0000000
--- a/src/__tests__/fake-test.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/* eslint-disable no-undef */
-describe('Fake Test', () => {
- it('should pass this fake test', () => {
- expect(1).toEqual(1);
- });
-});