ant-design/components/upload/__tests__/uploadlist.test.js
陈帅 785c132262
meger feature to master (#16421)
* use ul in list

* update snapshot

* update comment

* feat: TreeSelect support `showSearch` in multiple mode (#15933)

* update rc-tree-select

* typo

* update desc & snapshot

* update desc & snapshot

* check default showSearch

* feat: table customizing variable (#15971)

* feat: added table selected row color variable

* fix: @table-selected-row-color default is inherit

* feat: Upload support customize previewFile (#15984)

* support preview file

* use promise

* dealy load

* use canvas of render

* use domHook of test

* update demo

* add snapshot

* update types

* update testcase

* feat: form customizing variables (#15954)

* fix: added styling form input background-color

* feat: added '@form-warning-input-bg' variable

* feat: added '@form-error-input-bg' variable

* use li wrap with comment

* feat: Support append theme less file with less-variable (#16118)

* add override

* add override support

* update doc

* feat: dropdown support set right icon

* docs: update doc of dropdown component

* style: format dropdown-button.md

* test: update updateSnapshot

* style: format dropdown-button.md

* test: update updateSnapshot

* test: update updateSnapshot

* style: change style of dropdown-button demo

* fix: fix document table order

* feat: Support SkeletonAvatarProps.size accept number (#16078) (#16128)

* chore:update style of demo

* feat: Notification functions accept top, bottom and getContainer as arguments

* drawer: add afterVisibleChange

* rm onVisibleChange

* update

* feat: 🇭🇷 hr_HR locale (#16258)

* Added Croatian locale

* fixed lint error

*  Add test cases for hr_HR

* 📝 update i18n documentation

* feat:  add `htmlFor` in Form.Item (#16278)

* add htmlFor in Form.Item

* update doc

* feat: Button support `link` type (#16289)

close #15892

* feat: Add Timeline.Item.position (#16148) (#16193)

* fix: Timeline.pendingDot interface documentation there is a small problem (#16177)

* feat: Add Timeline.Item.position (#16148)

* doc: add version infomation for Timeline.Item.position

* refactor: Update Tree & TreeSelect deps (#16330)

* use CSSMotion

* update snapshot

* feat: Collapse support `expandIconPosition` (#16365)

* update doc

* support expandIconPosition

* update snapshot

* feat: Breadcrumb  support DropDown (#16315)

* breadcrumbs support drop down menu

* update doc

* add require less

* fix test

* fix md doc

* less code

* fix  style warning

* update snap

* add children render test

* feat: TreeNode support checkable

* feat: add optional to support top and left slick dots (#16186) (#16225)

* add optional to support top and left slick dots

* update carousel snapshot

* Update doc, add placement demo

* update carousel placement demo snapshots

* rename dots placement to position

* update vertical as deprecated

* rename dotsPosition to dotPosition

* refine code

* add warning testcase for vertical

* remove unused warning

* update expression

* Additional test case for dotPosition

* refactor: Upgrade `rc-tree-select` to support pure React motion (#16402)

* upgrade `rc-tree-select`

* update snapshot

* 3.17.0 changelog

* fix warning

* fix review warning
2019-05-06 12:04:39 +08:00

534 lines
15 KiB
JavaScript

import React from 'react';
import { mount } from 'enzyme';
import Upload from '..';
import UploadList from '../UploadList';
import Form from '../../form';
import { spyElementPrototypes } from '../../__tests__/util/domHook';
import { errorRequest, successRequest } from './requests';
import { setup, teardown } from './mock';
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
const fileList = [
{
uid: '-1',
name: 'xxx.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png',
},
{
uid: '-2',
name: 'yyy.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png',
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
];
describe('Upload List', () => {
// jsdom not support `createObjectURL` yet. Let's handle this.
const originCreateObjectURL = window.URL.createObjectURL;
window.URL.createObjectURL = jest.fn(() => '');
// Mock dom
let size = { width: 0, height: 0 };
function setSize(width, height) {
size = { width, height };
}
const imageSpy = spyElementPrototypes(Image, {
src: {
set() {
if (this.onload) {
this.onload();
}
},
},
width: {
get: () => size.width,
},
height: {
get: () => size.height,
},
});
let drawImageCallback = null;
function hookDrawImageCall(callback) {
drawImageCallback = callback;
}
const canvasSpy = spyElementPrototypes(HTMLCanvasElement, {
getContext: () => ({
drawImage: (...args) => {
if (drawImageCallback) drawImageCallback(...args);
},
}),
toDataURL: () => 'data:image/png;base64,',
});
// HTMLCanvasElement.prototype
beforeEach(() => setup());
afterEach(() => {
teardown();
drawImageCallback = null;
});
afterAll(() => {
window.URL.createObjectURL = originCreateObjectURL;
imageSpy.mockRestore();
canvasSpy.mockRestore();
});
// https://github.com/ant-design/ant-design/issues/4653
it('should use file.thumbUrl for <img /> in priority', () => {
const wrapper = mount(
<Upload defaultFileList={fileList} listType="picture">
<button type="button">upload</button>
</Upload>,
);
fileList.forEach((file, i) => {
const linkNode = wrapper.find('.ant-upload-list-item-thumbnail').at(i);
const imgNode = wrapper.find('.ant-upload-list-item-thumbnail img').at(i);
expect(linkNode.prop('href')).toBe(file.url);
expect(imgNode.prop('src')).toBe(file.thumbUrl);
});
});
// https://github.com/ant-design/ant-design/issues/7269
it('should remove correct item when uid is 0', async () => {
const list = [
{
uid: '0',
name: 'xxx.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png',
},
{
uid: '1',
name: 'xxx.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
thumbUrl: 'https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png',
},
];
const wrapper = mount(
<Upload defaultFileList={list}>
<button type="button">upload</button>
</Upload>,
);
expect(wrapper.find('.ant-upload-list-item').length).toBe(2);
wrapper
.find('.ant-upload-list-item')
.at(0)
.find('.anticon-close')
.simulate('click');
await delay(400);
wrapper.update();
expect(wrapper.find('.ant-upload-list-item').hostNodes().length).toBe(1);
});
it('should be uploading when upload a file', done => {
let wrapper;
const onChange = ({ file }) => {
if (file.status === 'uploading') {
expect(wrapper.render()).toMatchSnapshot();
}
if (file.status === 'done') {
expect(wrapper.render()).toMatchSnapshot();
done();
}
};
wrapper = mount(
<Upload
action="http://jsonplaceholder.typicode.com/posts/"
onChange={onChange}
customRequest={successRequest}
>
<button type="button">upload</button>
</Upload>,
);
wrapper.find('input').simulate('change', {
target: {
files: [{ name: 'foo.png' }],
},
});
});
it('handle error', done => {
let wrapper;
const onChange = ({ file }) => {
if (file.status !== 'uploading') {
expect(wrapper.render()).toMatchSnapshot();
done();
}
};
wrapper = mount(
<Upload
action="http://jsonplaceholder.typicode.com/posts/"
onChange={onChange}
customRequest={errorRequest}
>
<button type="button">upload</button>
</Upload>,
);
wrapper.find('input').simulate('change', {
target: {
files: [{ name: 'foo.png' }],
},
});
});
it('does concat filelist when beforeUpload returns false', () => {
const handleChange = jest.fn();
const wrapper = mount(
<Upload
listType="picture"
defaultFileList={fileList}
onChange={handleChange}
beforeUpload={() => false}
>
<button type="button">upload</button>
</Upload>,
);
wrapper.find('input').simulate('change', {
target: {
files: [{ name: 'foo.png' }],
},
});
expect(wrapper.state().fileList.length).toBe(fileList.length + 1);
expect(handleChange.mock.calls[0][0].fileList).toHaveLength(3);
});
it('should support onPreview', () => {
const handlePreview = jest.fn();
const wrapper = mount(
<Upload listType="picture-card" defaultFileList={fileList} onPreview={handlePreview}>
<button type="button">upload</button>
</Upload>,
);
wrapper
.find('.anticon-eye-o')
.at(0)
.simulate('click');
expect(handlePreview).toHaveBeenCalledWith(fileList[0]);
wrapper
.find('.anticon-eye-o')
.at(1)
.simulate('click');
expect(handlePreview).toHaveBeenCalledWith(fileList[1]);
});
it('should support onRemove', async () => {
const handleRemove = jest.fn();
const handleChange = jest.fn();
const wrapper = mount(
<Upload
listType="picture-card"
defaultFileList={fileList}
onRemove={handleRemove}
onChange={handleChange}
>
<button type="button">upload</button>
</Upload>,
);
wrapper
.find('.anticon-delete')
.at(0)
.simulate('click');
expect(handleRemove).toHaveBeenCalledWith(fileList[0]);
wrapper
.find('.anticon-delete')
.at(1)
.simulate('click');
expect(handleRemove).toHaveBeenCalledWith(fileList[1]);
await delay(0);
expect(handleChange.mock.calls.length).toBe(2);
});
describe('should generate thumbUrl from file', () => {
[
{ width: 100, height: 200, name: 'height large than width' },
{ width: 200, height: 100, name: 'width large than height' },
].forEach(({ width, height, name }) => {
it(name, async () => {
setSize(width, height);
const onDrawImage = jest.fn();
hookDrawImageCall(onDrawImage);
const handlePreview = jest.fn();
const newFileList = [...fileList];
const newFile = {
...fileList[0],
uid: '-3',
originFileObj: new File([], 'xxx.png', { type: 'image/png' }),
};
delete newFile.thumbUrl;
newFileList.push(newFile);
const wrapper = mount(
<Upload listType="picture-card" defaultFileList={newFileList} onPreview={handlePreview}>
<button type="button">upload</button>
</Upload>,
);
wrapper.setState({});
await delay(0);
expect(wrapper.state().fileList[2].thumbUrl).not.toBe(undefined);
expect(onDrawImage).toHaveBeenCalled();
// Offset check
const [, offsetX, offsetY] = onDrawImage.mock.calls[0];
if (width > height) {
expect(offsetX < 0).toBeTruthy();
} else {
expect(offsetY < 0).toBeTruthy();
}
});
});
});
it('should non-image format file preview', () => {
const list = [
{
name: 'not-image',
status: 'done',
uid: '-3',
url: 'https://cdn.xxx.com/aaa.zip',
thumbUrl: 'data:application/zip;base64,UEsDBAoAAAAAADYZYkwAAAAAAAAAAAAAAAAdAAk',
originFileObj: new File([], 'aaa.zip'),
},
{
name: 'image',
status: 'done',
uid: '-4',
url: 'https://cdn.xxx.com/aaa',
},
{
name: 'not-image',
status: 'done',
uid: '-5',
url: 'https://cdn.xxx.com/aaa.xx',
},
{
name: 'not-image',
status: 'done',
uid: '-6',
url: 'https://cdn.xxx.com/aaa.png/xx.xx',
},
{
name: 'image',
status: 'done',
uid: '-7',
url: 'https://cdn.xxx.com/xx.xx/aaa.png',
},
{
name: 'image',
status: 'done',
uid: '-8',
url: 'https://cdn.xxx.com/xx.xx/aaa.png',
thumbUrl: 'data:image/png;base64,UEsDBAoAAAAAADYZYkwAAAAAAAAAAAAAAAAdAAk',
},
{
name: 'image',
status: 'done',
uid: '-9',
url: 'https://cdn.xxx.com/xx.xx/aaa.png?query=123',
},
{
name: 'image',
status: 'done',
uid: '-10',
url: 'https://cdn.xxx.com/xx.xx/aaa.png#anchor',
},
{
name: 'image',
status: 'done',
uid: '-11',
url: 'https://cdn.xxx.com/xx.xx/aaa.png?query=some.query.with.dot',
},
{
name: 'image',
status: 'done',
uid: '-12',
url:
'https://publish-pic-cpu.baidu.com/1296beb3-50d9-4276-885f-52645cbb378e.jpeg@w_228%2ch_152',
type: 'image/png',
},
];
const wrapper = mount(
<Upload listType="picture" defaultFileList={list}>
<button type="button">upload</button>
</Upload>,
);
expect(wrapper.render()).toMatchSnapshot();
});
// https://github.com/ant-design/ant-design/issues/7762
it('work with form validation', () => {
let errors;
class TestForm extends React.Component {
handleSubmit = () => {
const {
form: { validateFields },
} = this.props;
validateFields(err => {
errors = err;
});
};
render() {
const {
form: { getFieldDecorator },
} = this.props;
return (
<Form onSubmit={this.handleSubmit}>
<Form.Item>
{getFieldDecorator('file', {
valuePropname: 'fileList',
getValueFromEvent: e => e.fileList,
rules: [
{
required: true,
validator: (rule, value, callback) => {
if (!value || value.length === 0) {
callback('file required');
} else {
callback();
}
},
},
],
})(
<Upload beforeUpload={() => false}>
<button type="button">upload</button>
</Upload>,
)}
</Form.Item>
</Form>
);
}
}
const App = Form.create()(TestForm);
const wrapper = mount(<App />);
wrapper.find(Form).simulate('submit');
expect(errors.file.errors).toEqual([{ message: 'file required', field: 'file' }]);
wrapper.find('input').simulate('change', {
target: {
files: [{ name: 'foo.png' }],
},
});
wrapper.find(Form).simulate('submit');
expect(errors).toBeNull();
});
it('return when prop onPreview not exists', () => {
const wrapper = mount(<UploadList />).instance();
expect(wrapper.handlePreview()).toBe(undefined);
});
it('previewFile should work correctly', async () => {
const file = new File([''], 'test.txt', { type: 'text/plain' });
const items = [{ uid: 'upload-list-item', url: '' }];
const wrapper = mount(
<UploadList listType="picture-card" items={items} locale={{ previewFile: '' }} />,
).instance();
return wrapper.props.previewFile(file);
});
it('extname should work correctly when url not exists', () => {
const items = [{ uid: 'upload-list-item', url: '' }];
const wrapper = mount(
<UploadList listType="picture-card" items={items} locale={{ previewFile: '' }} />,
);
expect(wrapper.find('.ant-upload-list-item-thumbnail').length).toBe(2);
});
it('when picture-card is loading, icon should render correctly', () => {
const items = [{ status: 'uploading', uid: 'upload-list-item' }];
const wrapper = mount(
<UploadList listType="picture-card" items={items} locale={{ uploading: 'uploading' }} />,
);
expect(wrapper.find('.ant-upload-list-item-uploading-text').length).toBe(1);
expect(wrapper.find('.ant-upload-list-item-uploading-text').text()).toBe('uploading');
});
it('onPreview should be called, when url exists', () => {
const onPreview = jest.fn();
const items = [{ thumbUrl: 'thumbUrl', url: 'url', uid: 'upload-list-item' }];
const wrapper = mount(
<UploadList
listType="picture-card"
items={items}
locale={{ uploading: 'uploading' }}
onPreview={onPreview}
/>,
);
wrapper.find('.ant-upload-list-item-thumbnail').simulate('click');
expect(onPreview).toHaveBeenCalled();
wrapper.find('.ant-upload-list-item-name').simulate('click');
expect(onPreview).toHaveBeenCalled();
wrapper.setProps({ items: [{ thumbUrl: 'thumbUrl', uid: 'upload-list-item' }] });
wrapper.find('.ant-upload-list-item-name').simulate('click');
expect(onPreview).toHaveBeenCalled();
});
it('upload image file should be converted to the base64', async () => {
const mockFile = new File([''], 'foo.png', {
type: 'image/png',
});
const wrapper = mount(
<UploadList listType="picture-card" items={fileList} locale={{ uploading: 'uploading' }} />,
);
const instance = wrapper.instance();
return instance.props.previewFile(mockFile).then(dataUrl => {
expect(dataUrl).toEqual('data:image/png;base64,');
});
});
it("upload non image file shouldn't be converted to the base64", async () => {
const mockFile = new File([''], 'foo.7z', {
type: 'application/x-7z-compressed',
});
const wrapper = mount(
<UploadList listType="picture-card" items={fileList} locale={{ uploading: 'uploading' }} />,
);
const instance = wrapper.instance();
return instance.props.previewFile(mockFile).then(dataUrl => {
expect(dataUrl).toBe('');
});
});
it('customize previewFile support', async () => {
const mockThumbnail = 'mock-image';
const previewFile = jest.fn(() => {
return Promise.resolve(mockThumbnail);
});
const file = {
...fileList[0],
originFileObj: new File([], 'xxx.png'),
};
delete file.thumbUrl;
const wrapper = mount(
<Upload listType="picture" defaultFileList={[file]} previewFile={previewFile}>
<button type="button" />
</Upload>,
);
wrapper.setState({});
await delay(0);
expect(previewFile).toHaveBeenCalledWith(file.originFileObj);
wrapper.update();
expect(wrapper.find('.ant-upload-list-item-thumbnail img').prop('src')).toBe(mockThumbnail);
});
});