mirror of https://github.com/ElemeFE/element
add test && fix ajax response in callback (#767)
parent
65a0250287
commit
23d4dbbce4
|
@ -11,6 +11,11 @@
|
||||||
- 更新 TimePicker 滚动条在 IE10+ 下隐藏
|
- 更新 TimePicker 滚动条在 IE10+ 下隐藏
|
||||||
- 新增 Dropdown 的 command api #432
|
- 新增 Dropdown 的 command api #432
|
||||||
- 修复 Slider 在 Form 中的显示问题
|
- 修复 Slider 在 Form 中的显示问题
|
||||||
|
- 修复 Upload 在 onSuccess、onError 钩子无法拿到服务端返回信息的问题
|
||||||
|
|
||||||
|
#### 非兼容性更新
|
||||||
|
|
||||||
|
- Upload on-error 钩子函数参数变更为 function(err, response, file), on-success 钩子函数参数变更为 function(response, file, fileList)
|
||||||
|
|
||||||
### 1.0.0-rc.8
|
### 1.0.0-rc.8
|
||||||
|
|
||||||
|
|
|
@ -145,9 +145,9 @@
|
||||||
| show-upload-list | 是否显示已上传文件列表 | boolean | — | true |
|
| show-upload-list | 是否显示已上传文件列表 | boolean | — | true |
|
||||||
| type | 上传控件类型 | string | select,drag | select |
|
| type | 上传控件类型 | string | select,drag | select |
|
||||||
| accept | 可选参数, 接受上传的[文件类型](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept)(thumbnail-mode 模式下此参数无效)| string | — | — |
|
| accept | 可选参数, 接受上传的[文件类型](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept)(thumbnail-mode 模式下此参数无效)| string | — | — |
|
||||||
| on-preview | 可选参数, 点击已上传的文件链接时的钩子 | function(file) | — | — |
|
| on-preview | 可选参数, 点击已上传的文件链接时的钩子, 可以通过 file.response 拿到服务端返回数据 | function(file) | — | — |
|
||||||
| on-remove | 可选参数, 文件列表移除文件时的钩子 | function(file, fileList) | — | — |
|
| on-remove | 可选参数, 文件列表移除文件时的钩子 | function(file, fileList) | — | — |
|
||||||
| on-success | 可选参数, 文件上传成功时的钩子 | function(file, fileList) | — | — |
|
| on-success | 可选参数, 文件上传成功时的钩子 | function(response, file, fileList) | — | — |
|
||||||
| on-error | 可选参数, 文件上传失败时的钩子 | function(err, file, fileList) | — | — |
|
| on-error | 可选参数, 文件上传失败时的钩子 | function(err, response, file) | — | — |
|
||||||
| before-upload | 可选参数, 上传文件之前的钩子,参数为上传的文件,若返回 false 或者 Promise 则停止上传。 | function(file) | — | — |
|
| before-upload | 可选参数, 上传文件之前的钩子,参数为上传的文件,若返回 false 或者 Promise 则停止上传。 | function(file) | — | — |
|
||||||
| thumbnail-mode | 是否设置为图片模式,该模式下会显示图片缩略图 | boolean | — | false |
|
| thumbnail-mode | 是否设置为图片模式,该模式下会显示图片缩略图 | boolean | — | false |
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
function getError(action, option, xhr) {
|
function getError(action, option, xhr) {
|
||||||
const msg = `cannot post ${action} ${xhr.status}'`;
|
const msg = `fail to post ${action} ${xhr.status}'`;
|
||||||
const err = new Error(msg);
|
const err = new Error(msg);
|
||||||
err.status = xhr.status;
|
err.status = xhr.status;
|
||||||
err.method = 'post';
|
err.method = 'post';
|
||||||
|
@ -20,12 +20,14 @@ function getBody(xhr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function upload(action, option) {
|
export default function upload(option) {
|
||||||
if (typeof XMLHttpRequest === 'undefined') {
|
if (typeof XMLHttpRequest === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
const action = option.action;
|
||||||
|
|
||||||
if (xhr.upload) {
|
if (xhr.upload) {
|
||||||
xhr.upload.onprogress = function progress(e) {
|
xhr.upload.onprogress = function progress(e) {
|
||||||
if (e.total > 0) {
|
if (e.total > 0) {
|
||||||
|
@ -65,6 +67,10 @@ export default function upload(action, option) {
|
||||||
|
|
||||||
const headers = option.headers || {};
|
const headers = option.headers || {};
|
||||||
|
|
||||||
|
if (headers['X-Requested-With'] !== null) {
|
||||||
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||||
|
}
|
||||||
|
|
||||||
for (let item in headers) {
|
for (let item in headers) {
|
||||||
if (headers.hasOwnProperty(item) && headers[item] !== null) {
|
if (headers.hasOwnProperty(item) && headers[item] !== null) {
|
||||||
xhr.setRequestHeader(item, headers[item]);
|
xhr.setRequestHeader(item, headers[item]);
|
||||||
|
|
|
@ -111,14 +111,14 @@ export default {
|
||||||
_file.status = 'finished';
|
_file.status = 'finished';
|
||||||
_file.response = res;
|
_file.response = res;
|
||||||
|
|
||||||
this.onSuccess(_file, this.fileList);
|
this.onSuccess(res, _file, this.fileList);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
_file.showProgress = false;
|
_file.showProgress = false;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleError(err, file) {
|
handleError(err, response, file) {
|
||||||
var _file = this.getFile(file);
|
var _file = this.getFile(file);
|
||||||
var fileList = this.fileList;
|
var fileList = this.fileList;
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ export default {
|
||||||
|
|
||||||
fileList.splice(fileList.indexOf(_file), 1);
|
fileList.splice(fileList.indexOf(_file), 1);
|
||||||
|
|
||||||
this.onError(err, _file, fileList);
|
this.onError(err, response, file);
|
||||||
},
|
},
|
||||||
handleRemove(file) {
|
handleRemove(file) {
|
||||||
var fileList = this.fileList;
|
var fileList = this.fileList;
|
||||||
|
@ -182,7 +182,8 @@ export default {
|
||||||
'on-error': this.handleError,
|
'on-error': this.handleError,
|
||||||
'on-preview': this.handlePreview,
|
'on-preview': this.handlePreview,
|
||||||
'on-remove': this.handleRemove
|
'on-remove': this.handleRemove
|
||||||
}
|
},
|
||||||
|
ref: 'upload-inner'
|
||||||
};
|
};
|
||||||
|
|
||||||
var uploadComponent = typeof FormData !== 'undefined'
|
var uploadComponent = typeof FormData !== 'undefined'
|
||||||
|
|
|
@ -130,20 +130,21 @@ export default {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append(this.name, file);
|
formData.append(this.name, file);
|
||||||
|
|
||||||
ajax(this.action, {
|
ajax({
|
||||||
headers: this.headers,
|
headers: this.headers,
|
||||||
withCredentials: this.withCredentials,
|
withCredentials: this.withCredentials,
|
||||||
file: file,
|
file: file,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
filename: this.name,
|
filename: this.name,
|
||||||
|
action: this.action,
|
||||||
onProgress: e => {
|
onProgress: e => {
|
||||||
this.onProgress(e, file);
|
this.onProgress(e, file);
|
||||||
},
|
},
|
||||||
onSuccess: res => {
|
onSuccess: res => {
|
||||||
this.onSuccess(res, file);
|
this.onSuccess(res, file);
|
||||||
},
|
},
|
||||||
onError: err => {
|
onError: (err, response) => {
|
||||||
this.onError(err, file);
|
this.onError(err, response, file);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,229 @@
|
||||||
|
import { createVue, destroyVM } from '../util.js';
|
||||||
|
import ajax from 'packages/upload/src/ajax';
|
||||||
|
const noop = () => {
|
||||||
|
};
|
||||||
|
const option = {
|
||||||
|
onSuccess: noop,
|
||||||
|
data: { a: 'abc', b: 'bcd' },
|
||||||
|
filename: 'file.png',
|
||||||
|
file: 'foo',
|
||||||
|
action: '/upload',
|
||||||
|
headers: { region: 'shanghai' }
|
||||||
|
};
|
||||||
|
let requests, xhr;
|
||||||
|
describe('ajax', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
xhr = sinon.useFakeXMLHttpRequest();
|
||||||
|
requests = [];
|
||||||
|
xhr.onCreate = req => requests.push(req);
|
||||||
|
option.onError = noop;
|
||||||
|
option.onSuccess = noop;
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
xhr.restore();
|
||||||
|
});
|
||||||
|
it('request success', done => {
|
||||||
|
option.onError = done;
|
||||||
|
option.onSuccess = ret => {
|
||||||
|
expect(ret).to.eql({ success: true });
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
ajax(option);
|
||||||
|
requests[0].respond(200, {}, '{"success": true}');
|
||||||
|
});
|
||||||
|
it('request width header', done => {
|
||||||
|
ajax(option);
|
||||||
|
expect(requests[0].requestHeaders).to.eql({
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
region: 'shanghai'
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
it('40x code should be error', done => {
|
||||||
|
option.onError = e => {
|
||||||
|
expect(e.toString()).to.contain('404');
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
option.onSuccess = () => done('404 should throw error');
|
||||||
|
ajax(option);
|
||||||
|
requests[0].respond(404, {}, 'Not found');
|
||||||
|
});
|
||||||
|
it('2xx code should be success', done => {
|
||||||
|
option.onError = done;
|
||||||
|
option.onSuccess = ret => {
|
||||||
|
expect(ret).to.equal('');
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
ajax(option);
|
||||||
|
requests[0].respond(204, {});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Upload', () => {
|
||||||
|
let requests;
|
||||||
|
let xhr;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
xhr = sinon.useFakeXMLHttpRequest();
|
||||||
|
requests = [];
|
||||||
|
xhr.onCreate = req => requests.push(req);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
xhr.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ajax upload', () => {
|
||||||
|
if (typeof FormData === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uploader;
|
||||||
|
let handlers = {};
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
props: {
|
||||||
|
action: '/upload',
|
||||||
|
onSuccess(res, file, fileList) {
|
||||||
|
console.log('onSuccess', res);
|
||||||
|
if (handlers.onSuccess) {
|
||||||
|
handlers.onSuccess(res, file, fileList);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError(err, response, file) {
|
||||||
|
console.log('onError', err, response);
|
||||||
|
if (handlers.onError) {
|
||||||
|
handlers.onError(err, response, file);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onPreview(file) {
|
||||||
|
console.log('onPreview', file);
|
||||||
|
if (handlers.onPreview) {
|
||||||
|
handlers.onPreview(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
uploader = createVue({
|
||||||
|
render(h) {
|
||||||
|
return (
|
||||||
|
<el-upload {...props} ref="upload">
|
||||||
|
<el-button size="small" type="primary">点击上传</el-button>
|
||||||
|
</el-upload>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, true).$refs.upload;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
destroyVM(uploader);
|
||||||
|
handlers = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('upload success', done => {
|
||||||
|
const files = [{
|
||||||
|
name: 'success.png',
|
||||||
|
type: 'xml'
|
||||||
|
}];
|
||||||
|
|
||||||
|
handlers.onSuccess = (res, file, fileList) => {
|
||||||
|
expect(file.name).to.equal('success.png');
|
||||||
|
expect(fileList.length).to.equal(1);
|
||||||
|
expect(res).to.equal('success.png');
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
uploader.$refs['upload-inner'].handleChange({ target: { files }});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
requests[0].respond(200, {}, `${files[0].name}`);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('upload fail', done => {
|
||||||
|
const files = [{
|
||||||
|
name: 'fail.png',
|
||||||
|
type: 'xml'
|
||||||
|
}];
|
||||||
|
|
||||||
|
handlers.onError = (err, response, file) => {
|
||||||
|
expect(err instanceof Error).to.equal(true);
|
||||||
|
expect(response).to.equal('error 400');
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
uploader.$refs['upload-inner'].handleChange({ target: { files }});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
requests[0].respond(400, {}, 'error 400');
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
it('preview file', done => {
|
||||||
|
const files = [{
|
||||||
|
name: 'success.png',
|
||||||
|
type: 'xml'
|
||||||
|
}];
|
||||||
|
|
||||||
|
handlers.onPreview = (file) => {
|
||||||
|
expect(file.response).to.equal('success.png');
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
handlers.onSuccess = (res, file, fileList) => {
|
||||||
|
uploader.$nextTick(_ => {
|
||||||
|
uploader.$el.querySelector('.el-upload__files .is-finished a').click();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
uploader.$refs['upload-inner'].handleChange({ target: { files }});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
requests[0].respond(200, {}, `${files[0].name}`);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
it('file remove', done => {
|
||||||
|
const files = [{
|
||||||
|
name: 'success.png',
|
||||||
|
type: 'xml'
|
||||||
|
}];
|
||||||
|
|
||||||
|
handlers.onSuccess = (res, file, fileList) => {
|
||||||
|
uploader.$el.querySelector('.el-upload__files .el-upload__btn-delete').click();
|
||||||
|
uploader.$nextTick(_ => {
|
||||||
|
expect(uploader.fileList.length).to.equal(0);
|
||||||
|
expect(uploader.$el.querySelector('.el-upload__files')).to.not.exist;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
uploader.$refs['upload-inner'].handleChange({ target: { files }});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
requests[0].respond(200, {}, `${files[0].name}`);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
it('clear files', done => {
|
||||||
|
const files = [{
|
||||||
|
name: 'success.png',
|
||||||
|
type: 'xml'
|
||||||
|
}];
|
||||||
|
|
||||||
|
handlers.onSuccess = (res, file, fileList) => {
|
||||||
|
uploader.clearFiles();
|
||||||
|
uploader.$nextTick(_ => {
|
||||||
|
expect(uploader.fileList.length).to.equal(0);
|
||||||
|
expect(uploader.$el.querySelector('.el-upload__files')).to.not.exist;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
uploader.$refs['upload-inner'].handleChange({ target: { files }});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
requests[0].respond(200, {}, `${files[0].name}`);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue