upload.test.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. import sleep from '@douyinfe/semi-ui/_test_/utils/function/sleep';
  2. import { IconUser } from '@douyinfe/semi-icons';
  3. import { Upload, Button } from '../../index';
  4. import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants';
  5. let action = 'https://semi.bytendance.com';
  6. const PROGRESS_COEFFICIENT = 0.95;
  7. function getUpload(props, formProps = {}) {
  8. if (!props.children) {
  9. props.children = (
  10. <Button icon={<IconUser />} theme="light">
  11. 点击上传
  12. </Button>
  13. );
  14. }
  15. if (!props.action) {
  16. props.action = action;
  17. }
  18. return mount(<Upload {...props}></Upload>);
  19. }
  20. function trigger(upload, event) {
  21. const input = upload.find(`.${BASE_CLASS_PREFIX}-upload-hidden-input`);
  22. input.simulate('change', event);
  23. }
  24. const file = new File(['(⌐□_□)'], 'chucknorris.png', { type: 'image/png' });
  25. const createFile = (size = 44320, name = 'semi-logo.png', type = 'image/png') => {
  26. return new File([new ArrayBuffer(size)], name, {
  27. type: type,
  28. });
  29. };
  30. const createEvent = file => {
  31. let event = { target: { files: [file] } };
  32. return event;
  33. };
  34. const defaultFileList = [
  35. {
  36. uid: '1',
  37. name: 'vigo.png',
  38. status: 'success',
  39. size: '130KB',
  40. preview: true,
  41. url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dbf7351bb779433d17c4f50478cf42f7.jpg',
  42. },
  43. {
  44. uid: '2',
  45. name: 'test.jpeg',
  46. status: 'uploadFail',
  47. size: '222KB',
  48. preview: false,
  49. url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dbf7351bb779433d17c4f50478cf42f7.jpg',
  50. },
  51. ];
  52. // TODO
  53. /**
  54. * 1、unitTest无法测试点击Upload Children自动弹出文件浏览器的case,需要在e2e里完成
  55. * 2、multiple 属性无法测试,理由同上
  56. * 3、disabled时点击不弹出文件浏览器的行为无法测试,理由同上
  57. */
  58. describe('Upload', () => {
  59. let requests;
  60. let xhr;
  61. window.URL.createObjectURL = jest.fn();
  62. beforeEach(() => {
  63. xhr = sinon.useFakeXMLHttpRequest();
  64. requests = [];
  65. xhr.onCreate = req => requests.push(req);
  66. window.URL.createObjectURL.mockReset();
  67. });
  68. afterEach(() => {
  69. xhr.restore();
  70. });
  71. it('className & style', () => {
  72. let props = {
  73. className: 'test',
  74. style: { color: 'red' },
  75. action: '',
  76. };
  77. const upload = getUpload(props);
  78. expect(upload.find('div.test').length).toEqual(1);
  79. });
  80. it('action / withCredentials', () => {
  81. let props = {
  82. action,
  83. withCredentials: true,
  84. data: { semiKey: 123456 },
  85. };
  86. const upload = getUpload(props);
  87. let event = { target: { files: [file] } };
  88. trigger(upload, event);
  89. expect(requests[0].url).toEqual(action);
  90. expect(requests[0].withCredentials).toEqual(true);
  91. });
  92. it('data / headers / name', () => {
  93. let headers = { 'x-tt-header': 'semi' };
  94. let name = 'bytedance.jpeg';
  95. let props = {
  96. data: { semiKey: 123456 },
  97. name,
  98. headers,
  99. };
  100. const upload = getUpload(props);
  101. let event = { target: { files: [file] } };
  102. trigger(upload, event);
  103. let requestBody = Array.from(requests[0].requestBody);
  104. expect(requestBody[0][0]).toEqual('semiKey');
  105. expect(requestBody[0][1]).toEqual('123456');
  106. let requestHeaders = requests[0].requestHeaders;
  107. expect(requestHeaders).toEqual(headers);
  108. expect(requestBody[1][0]).toEqual(name);
  109. });
  110. it('data / headrs : function', () => {
  111. let headers = { 'x-tt-header': 'semi' };
  112. let data = { semiKey: 123456 };
  113. let getHeaders = file => {
  114. return headers;
  115. };
  116. let getData = file => {
  117. return data;
  118. };
  119. let spyGetData = sinon.spy(getData);
  120. let spyGetHeaders = sinon.spy(getHeaders);
  121. let props = {
  122. data: spyGetData,
  123. headers: spyGetHeaders,
  124. };
  125. const upload = getUpload(props);
  126. let event = { target: { files: [file] } };
  127. trigger(upload, event);
  128. // test data
  129. let requestBody = Array.from(requests[0].requestBody);
  130. expect(requestBody[0][0]).toEqual('semiKey');
  131. expect(requestBody[0][1]).toEqual('123456');
  132. expect(spyGetData.calledOnce).toEqual(true);
  133. expect(spyGetData.calledWithMatch(file)).toEqual(true);
  134. // test headers
  135. let requestHeaders = requests[0].requestHeaders;
  136. expect(requestHeaders).toEqual(headers);
  137. expect(spyGetHeaders.calledOnce).toEqual(true);
  138. expect(spyGetHeaders.calledWithMatch(file)).toEqual(true);
  139. });
  140. it('accept', () => {
  141. let accept = 'application/pdf,image/png,image/jpeg';
  142. let props = {
  143. accept,
  144. };
  145. const upload = getUpload(props);
  146. expect(upload.find(`input.${BASE_CLASS_PREFIX}-upload-hidden-input`).instance().accept).toEqual(accept);
  147. });
  148. it('minSize / maxSize / onSizeError', () => {
  149. let kb1 = 1024 * 1024;
  150. let onSizeError = (file, fileList) => {};
  151. let spyOnSizeError = sinon.spy(onSizeError);
  152. let props = {
  153. maxSize: kb1 * 3,
  154. minSize: kb1 * 2,
  155. onSizeError: spyOnSizeError,
  156. };
  157. const upload = getUpload(props);
  158. const bigFile = createFile(kb1 * 4, 'bigSemi.jpeg');
  159. const smallFile = createFile(kb1, 'smallSemi.jpeg');
  160. let bigEvent = { target: { files: [bigFile] } };
  161. let smallEvent = { target: { files: [smallFile] } };
  162. // choose file over maxSize
  163. trigger(upload, bigEvent);
  164. // choose file below minSize
  165. trigger(upload, smallEvent);
  166. expect(spyOnSizeError.callCount).toEqual(2);
  167. let firstCall = spyOnSizeError.getCall(0);
  168. let secondCall = spyOnSizeError.getCall(1);
  169. let firstArgs = firstCall.args;
  170. let secondArgs = secondCall.args;
  171. // 如果是calledWithMatch({}, []) 这种写法,即使放空object,和空数组也能过,可能只判断了类型?但实际上第一个参数是File
  172. // 这里借助文件name来判断一下
  173. expect(firstArgs[0] instanceof File).toEqual(true);
  174. expect(firstArgs[0].name).toEqual('bigSemi.jpeg');
  175. expect(Array.isArray(firstArgs[1]) && firstArgs[1].length === 0).toEqual(true);
  176. // expect(firstCall.calledWithMatch({}, [])).toEqual(true);
  177. expect(secondArgs[0] instanceof File).toEqual(true);
  178. expect(secondArgs[0].name).toEqual('smallSemi.jpeg');
  179. expect(
  180. Array.isArray(secondArgs[1]) && secondArgs[1].length === 1 && secondArgs[1][0].name === 'bigSemi.jpeg'
  181. ).toEqual(true);
  182. });
  183. it('prompt / promptPosition', () => {
  184. let prompt = 'Some info for extra text';
  185. let props = {
  186. prompt,
  187. promptPosition: 'right',
  188. };
  189. const upload = getUpload(props);
  190. expect(upload.find(`.${BASE_CLASS_PREFIX}-upload-prompt`).text()).toEqual(prompt);
  191. expect(upload.find(`.${BASE_CLASS_PREFIX}-upload`).instance().getAttribute('x-prompt-pos')).toEqual('right');
  192. upload.setProps({ promptPosition: 'bottom' });
  193. upload.update();
  194. expect(upload.find(`.${BASE_CLASS_PREFIX}-upload`).instance().getAttribute('x-prompt-pos')).toEqual('bottom');
  195. upload.setProps({ promptPosition: 'left' });
  196. upload.update();
  197. expect(upload.find(`.${BASE_CLASS_PREFIX}-upload`).instance().getAttribute('x-prompt-pos')).toEqual('left');
  198. });
  199. it('limit / onExceed', () => {
  200. let onExceed = (file, fileList) => {
  201. // debugger;
  202. };
  203. let spyOnExceed = sinon.spy(onExceed);
  204. let props = {
  205. limit: 2,
  206. onExceed: spyOnExceed,
  207. };
  208. const upload = getUpload(props);
  209. let fileA = createFile(1024, 'fileA');
  210. let fileB = createFile(1024, 'fileB');
  211. let fileC = createFile(1024, 'fileC');
  212. let files = [fileA, fileB, fileC];
  213. let event = {
  214. target: { files },
  215. };
  216. trigger(upload, event);
  217. expect(spyOnExceed.calledOnce).toEqual(true);
  218. // expect(spyOnExceed.calledWithMatch(fileC, files)).toEqual(true);
  219. });
  220. it('beforeUpload - return boolean', () => {
  221. let beforeUpload = ({ file, fileList }) => {
  222. if (file.name === 'pass.jpg') {
  223. return true;
  224. }
  225. return false;
  226. };
  227. let spyBefore = sinon.spy(beforeUpload);
  228. const props = {
  229. beforeUpload: spyBefore,
  230. };
  231. const upload = getUpload(props);
  232. let event = createEvent(createFile(10, 'pass.jpg'));
  233. trigger(upload, event);
  234. expect(upload.state().fileList[0].name).toEqual('pass.jpg');
  235. event = createEvent(createFile(20, 'fail.jpg'));
  236. trigger(upload, event);
  237. const fileList = upload.state().fileList;
  238. expect(fileList.length).toEqual(2);
  239. expect(fileList[0].status === 'uploading').toEqual(true);
  240. expect(fileList[1].status === 'validateFail').toEqual(true);
  241. expect(spyBefore.callCount).toEqual(2);
  242. });
  243. it('beforeUpload - return object sync', () => {
  244. // beforeUploadResult:
  245. // {
  246. // fileInstance?: File,
  247. // status?: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait',
  248. // validateMessage?: React.ReactNode | string, // 文件的校验信息
  249. // shouldUpload: boolean, // 是否需要上传。默认为true,如果为false,该fileItem只会被展示在列表中,不会触发上传操作
  250. // autoRemove?: boolean, // 是否从fileList中移除该文件,默认为false
  251. // }
  252. let beforeUpload = ({ file, fileList }) => {
  253. let result = {
  254. shouldUpload: false,
  255. autoRemove: false,
  256. };
  257. if (file.name === 'pass.jpg') {
  258. result.shouldUpload = true;
  259. }
  260. if (file.name === 'invalid.jpg') {
  261. result.validateMessage = 'not valid file';
  262. result.status = 'validateFail';
  263. }
  264. if (file.name === 'autoRemove.jpg') {
  265. result.autoRemove = true;
  266. }
  267. return result;
  268. };
  269. let spyBefore = sinon.spy(beforeUpload);
  270. const props = {
  271. beforeUpload: spyBefore,
  272. };
  273. const upload = getUpload(props);
  274. // pass a file will pass validate
  275. let eventA = createEvent(createFile(10, 'pass.jpg'));
  276. trigger(upload, eventA);
  277. expect(upload.state().fileList[0].name).toEqual('pass.jpg');
  278. // pass a file invalid & change it's validateMessage & status
  279. let eventB = createEvent(createFile(20, 'invalid.jpg'));
  280. trigger(upload, eventB);
  281. // pass a file invalid & auto remove this file from fileList
  282. let eventC = createEvent(createFile(30, 'autoRemove.jpg'));
  283. trigger(upload, eventC);
  284. const fileList = upload.state().fileList;
  285. expect(fileList.length).toEqual(2);
  286. expect(fileList[0].status === 'uploading').toEqual(true);
  287. expect(fileList[1].status === 'validateFail').toEqual(true);
  288. expect(fileList[1].validateMessage === 'not valid file').toEqual(true);
  289. expect(fileList.every(item => item !== 'autoRemove.jpg')).toEqual(true);
  290. expect(spyBefore.callCount).toEqual(3);
  291. });
  292. // 1、promise reject:not upload file
  293. // 2、promise resolve: upload file
  294. // 3、promise reslove / reject object
  295. it('beforeUpload - return promise', async () => {
  296. function selectFile(filename, upload) {
  297. let file = createFile(50, filename);
  298. let event = createEvent(file);
  299. trigger(upload, event);
  300. }
  301. let beforeUpload = ({ file, fileList }) => {
  302. let result;
  303. switch (file.name) {
  304. case 'reject.jpg':
  305. result = new Promise((resolve, reject) => setTimeout(reject, 10));
  306. break;
  307. case 'resolve.jpg':
  308. result = new Promise((resolve, reject) => setTimeout(resolve, 10));
  309. break;
  310. case 'resloveObject.jpg':
  311. let pro = new Promise((resolve, reject) => {
  312. let newFile = createFile(200, 'afterProcess.jpg');
  313. let result = {
  314. fileInstance: newFile,
  315. // shouldUpload: true,
  316. };
  317. setTimeout(() => resolve(result), 10);
  318. });
  319. result = pro;
  320. break;
  321. case 'rejectObject.jpg':
  322. let pro2 = new Promise((resolve, reject) => {
  323. let result = {
  324. status: 'validateFail',
  325. validateMessage: 'not valid',
  326. // shouldUpload: false,
  327. };
  328. setTimeout(() => reject(result), 10);
  329. });
  330. result = pro2;
  331. break;
  332. default:
  333. break;
  334. }
  335. return result;
  336. };
  337. let spyBefore = sinon.spy(beforeUpload);
  338. const props = {
  339. beforeUpload: spyBefore,
  340. };
  341. const upload = getUpload(props);
  342. selectFile('reject.jpg', upload);
  343. await sleep(60);
  344. selectFile('resolve.jpg', upload);
  345. await sleep(60);
  346. selectFile('resloveObject.jpg', upload);
  347. await sleep(60);
  348. selectFile('rejectObject.jpg', upload);
  349. await sleep(100);
  350. expect(spyBefore.callCount).toEqual(4);
  351. const fileList = upload.state().fileList;
  352. expect(fileList.length).toEqual(4);
  353. expect(fileList[0].status === 'validateFail').toEqual(true);
  354. expect(fileList[1].status === 'uploading').toEqual(true);
  355. expect(fileList[2].status === 'uploading' && fileList[2].fileInstance.name === 'afterProcess.jpg').toEqual(
  356. true
  357. );
  358. expect(fileList[3].status === 'validateFail' && fileList[3].validateMessage === 'not valid').toEqual(true);
  359. });
  360. it('onFileChange', () => {
  361. let onFileChange = files => {};
  362. let spyOnFileChange = sinon.spy(onFileChange);
  363. const props = {
  364. onFileChange: spyOnFileChange,
  365. };
  366. const upload = getUpload(props);
  367. let event = { target: { files: [file] } };
  368. trigger(upload, event);
  369. expect(spyOnFileChange.calledOnce).toEqual(true);
  370. expect(spyOnFileChange.calledWithMatch([file])).toEqual(true);
  371. });
  372. it('onProgress', () => {
  373. let onProgress = (percent, file, fileList) => {};
  374. let spyProgress = sinon.spy(onProgress);
  375. let props = {
  376. onProgress: spyProgress,
  377. };
  378. const server = sinon.fakeServer.create();
  379. const upload = getUpload(props);
  380. let event = { target: { files: [file] } };
  381. trigger(upload, event);
  382. server.requests[0].uploadProgress({ loaded: 40, total: 100 });
  383. server.requests[0].uploadProgress({ loaded: 80, total: 100 });
  384. expect(spyProgress.callCount).toEqual(2);
  385. expect(spyProgress.getCall(0).calledWithMatch(40 * PROGRESS_COEFFICIENT)).toEqual(true);
  386. expect(spyProgress.getCall(1).calledWithMatch(80 * PROGRESS_COEFFICIENT)).toEqual(true);
  387. });
  388. it('onError', () => {
  389. let onError = (responseBody, file, fileList, xhr) => {};
  390. let spyOnError = sinon.spy(onError);
  391. const props = {
  392. onError: spyOnError,
  393. };
  394. const upload = getUpload(props);
  395. let event = { target: { files: [file] } };
  396. trigger(upload, event);
  397. requests[0].respond(404, { 'Content-Type': 'application/json' }, '[{ "id": 12, "comment": "Hey there" }]');
  398. expect(spyOnError.calledOnce).toEqual(true);
  399. const [error, fileInstance, fileList, xhr] = spyOnError.args[0];
  400. expect(error.method).toEqual('post');
  401. expect(fileInstance instanceof File).toEqual(true);
  402. expect(Array.isArray(fileList)).toEqual(true);
  403. expect(xhr instanceof XMLHttpRequest).toEqual(true);
  404. });
  405. it('onRetry', () => {
  406. const onRetry = f => {};
  407. const spyOnRetry = sinon.spy(onRetry);
  408. const fileInstance = createFile(200, 'semi.jpg');
  409. const file = {
  410. uid: '2',
  411. name: 'test.jpeg',
  412. status: 'uploadFail',
  413. size: '222KB',
  414. preview: true,
  415. fileInstance,
  416. url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dbf7351bb779433d17c4f50478cf42f7.jpg',
  417. };
  418. const props = {
  419. fileList: [file],
  420. onRetry: spyOnRetry,
  421. };
  422. const upload = getUpload(props);
  423. upload.find(`.${BASE_CLASS_PREFIX}-upload-file-card-info-retry`).simulate('click');
  424. expect(spyOnRetry.calledOnce).toEqual(true);
  425. const [f] = spyOnRetry.args[0];
  426. expect(f.fileInstance instanceof File).toEqual(true);
  427. });
  428. it('onOpenFileDialog', () => {
  429. const onOpenFileDialog = () => {};
  430. const spyOnOpenFileDialog = sinon.spy(onOpenFileDialog);
  431. const props = {
  432. onOpenFileDialog: spyOnOpenFileDialog,
  433. };
  434. const upload = getUpload(props);
  435. upload.find(`div.${BASE_CLASS_PREFIX}-upload-add`).simulate('click');
  436. expect(spyOnOpenFileDialog.calledOnce).toEqual(true);
  437. });
  438. it('onSuccess', () => {
  439. let body = [{ id: 12, comment: 'Hey there' }];
  440. let onSuccess = (responseBody, file, fileList) => {
  441. console.log(body);
  442. // debugger;
  443. };
  444. let spyOnSuccess = sinon.spy(onSuccess);
  445. const props = {
  446. onSuccess: spyOnSuccess,
  447. };
  448. const upload = getUpload(props);
  449. let event = { target: { files: [file] } };
  450. trigger(upload, event);
  451. requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(body));
  452. expect(spyOnSuccess.calledOnce).toEqual(true);
  453. // TODO check argument,凡是涉及到fileList的都会failed
  454. expect(spyOnSuccess.calledWith([{ id: 12, comment: 'Hey there' }], file)).toEqual(true);
  455. });
  456. it('onRemove', () => {
  457. let onRemove = (file, fileList) => {};
  458. const spyOnRemove = sinon.spy(onRemove);
  459. let fileInstance = createFile(200, 'semi.jpg');
  460. let file = {
  461. uid: '2',
  462. name: 'test.jpeg',
  463. status: 'error',
  464. size: '222KB',
  465. preview: true,
  466. fileInstance,
  467. url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dbf7351bb779433d17c4f50478cf42f7.jpg',
  468. };
  469. let props = {
  470. defaultFileList: [file],
  471. onRemove: spyOnRemove,
  472. };
  473. const upload = getUpload(props);
  474. upload.find(`button.${BASE_CLASS_PREFIX}-upload-file-card-close`).simulate('click', {});
  475. setTimeout(() => {
  476. expect(spyOnRemove.calledOnce).toEqual(true);
  477. expect(spyOnRemove.calledOnceWith(fileInstance, [])).toEqual(true);
  478. });
  479. });
  480. it('defaultFileList', () => {
  481. let props = {
  482. defaultFileList,
  483. };
  484. const upload = getUpload(props);
  485. expect(upload.find(`.${BASE_CLASS_PREFIX}-upload-file-list`).children().length).toEqual(2);
  486. // TODO check item name, size, preview
  487. });
  488. it('showUploadList', () => {
  489. let props = {
  490. showUploadList: false,
  491. defaultFileList: [
  492. {
  493. uid: '2',
  494. name: 'test.jpeg',
  495. status: 'error',
  496. size: '222KB',
  497. preview: true,
  498. url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dbf7351bb779433d17c4f50478cf42f7.jpg',
  499. },
  500. ],
  501. };
  502. const upload = getUpload(props);
  503. expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload-file-list`)).toEqual(false);
  504. });
  505. it('listType', () => {
  506. let props = {
  507. listType: 'picture',
  508. };
  509. const upload = getUpload(props);
  510. expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload.${BASE_CLASS_PREFIX}-upload-picture`)).toEqual(true);
  511. });
  512. it('previewFile', () => {
  513. let specificContent = <div>2</div>;
  514. let previewFile = () => specificContent;
  515. let spyPreview = sinon.spy(previewFile);
  516. let props = {
  517. previewFile: spyPreview,
  518. };
  519. const upload = getUpload(props);
  520. let event = { target: { files: [file] } };
  521. trigger(upload, event);
  522. requests[0].respond(200, { 'Content-Type': 'application/json' }, 'success');
  523. const previewContent = upload.find(`.${BASE_CLASS_PREFIX}-upload-file-card-preview`);
  524. expect(previewContent.contains(specificContent)).toEqual(true);
  525. upload.unmount();
  526. });
  527. it('afterUpload', () => {
  528. // afterUploadResult:
  529. // {
  530. // status?: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait',
  531. // validateMessage?: React.ReactNode | string, // 文件的校验信息
  532. // autoRemove: boolean, // 是否从fileList中移除该文件,默认为false
  533. // name: string,
  534. // }
  535. let codeStatusMaps = {
  536. 0: 'success',
  537. 1: 'uploadFail',
  538. 2: 'validateFail',
  539. };
  540. let afterUpload = ({ response, file, fileList }) => {
  541. let result = {};
  542. result.status = codeStatusMaps[response.code];
  543. if (response.message) {
  544. result.validateMessage = response.message;
  545. }
  546. if (response.autoRemove) {
  547. result.autoRemove = true;
  548. }
  549. if (response.newName) {
  550. result.name = response.newName;
  551. }
  552. return result;
  553. };
  554. let spyAfterUpload = sinon.spy(afterUpload);
  555. let props = {
  556. afterUpload: spyAfterUpload,
  557. };
  558. const upload = getUpload(props);
  559. // test status-success
  560. let eventA = { target: { files: [createFile(234, 'uploadSuccess.jpg')] } };
  561. let resA = { code: 0 };
  562. trigger(upload, eventA);
  563. requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(resA));
  564. // test status-uploadFail & validateMessage
  565. let eventB = { target: { files: [createFile(123, 'uploadFail.jpg')] } };
  566. trigger(upload, eventB);
  567. let uploadFailMessage = 'upload request fail';
  568. let resB = { code: 1, message: uploadFailMessage };
  569. requests[1].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(resB));
  570. // test status-validateFail & validateMessage
  571. let eventC = { target: { files: [createFile(123, 'validateFail.jpg')] } };
  572. trigger(upload, eventC);
  573. let validateFailMessage = 'what u upload is invalid';
  574. let resC = { code: 2, message: validateFailMessage };
  575. requests[2].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(resC));
  576. // test autoRemove, should auto remove this file
  577. let eventD = { target: { files: [createFile(123, 'remove.jpg')] } };
  578. trigger(upload, eventD);
  579. let resD = { code: 2, autoRemove: true };
  580. requests[3].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(resD));
  581. // test rename
  582. let eventE = { target: { files: [createFile(123, 'semi.jpg')] } };
  583. trigger(upload, eventE);
  584. let rename = 'renameByAfterUpload.jpg';
  585. let resE = { code: 1, newName: rename };
  586. requests[4].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(resE));
  587. const stateFileList = upload.state().fileList;
  588. expect(stateFileList.length).toEqual(4);
  589. expect(stateFileList[0].status === 'success').toEqual(true);
  590. expect(
  591. stateFileList[1].status === 'uploadFail' && stateFileList[1].validateMessage === uploadFailMessage
  592. ).toEqual(true);
  593. expect(
  594. stateFileList[2].status === 'validateFail' && stateFileList[2].validateMessage === validateFailMessage
  595. ).toEqual(true);
  596. expect(stateFileList.every(item => item.name !== 'remove.jpg')).toEqual(true);
  597. expect(stateFileList[3].status === 'uploadFail' && stateFileList[3].name === rename).toEqual(true);
  598. upload.unmount();
  599. });
  600. it('uploadTrigger', () => {
  601. let props = {
  602. uploadTrigger: 'custom',
  603. };
  604. const upload = getUpload(props);
  605. let eventA = { target: { files: [createFile(234, 'semi.jpg')] } };
  606. trigger(upload, eventA);
  607. expect(upload.state().fileList.length).toEqual(1);
  608. console.log(requests);
  609. expect(requests.length).toEqual(0);
  610. upload.instance().upload();
  611. // only new XHR after trigger upload instance (by ref) method: upload()
  612. expect(requests.length).toEqual(1);
  613. });
  614. it('auto hide trigger when limit & listType="picture"', () => {
  615. let props = {
  616. limit: 2,
  617. listType: 'picture',
  618. defaultFileList,
  619. };
  620. const upload = getUpload(props);
  621. expect(upload.find(`.${BASE_CLASS_PREFIX}-upload-file-list-main`).children().length).toEqual(2);
  622. });
  623. it('showClear', () => {
  624. let props = {
  625. defaultFileList,
  626. showClear: false,
  627. };
  628. const upload = getUpload(props);
  629. expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload-file-list-title-clear`)).toEqual(false);
  630. let props2 = {
  631. defaultFileList,
  632. };
  633. const upload2 = getUpload(props2);
  634. expect(upload2.exists(`.${BASE_CLASS_PREFIX}-upload-file-list-title-clear`)).toEqual(true);
  635. });
  636. it('showRetry', () => {
  637. let props = {
  638. defaultFileList,
  639. showRetry: false,
  640. };
  641. const upload = getUpload(props);
  642. expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload-file-card-info-retry`)).toEqual(false);
  643. });
  644. it('validateMessage', () => {
  645. let props = {
  646. defaultFileList,
  647. validateMessage: 'test',
  648. };
  649. const upload = getUpload(props);
  650. expect(upload.find(`.${BASE_CLASS_PREFIX}-upload-validate-message`).text()).toEqual('test');
  651. });
  652. it('renderFileItem', () => {
  653. let props = {
  654. defaultFileList,
  655. renderFileItem: fileItem => <div className="customRender">{fileItem.name}</div>,
  656. };
  657. const upload = getUpload(props);
  658. expect(upload.find('.customRender').length).toEqual(2);
  659. expect(upload.find('.customRender').at(0).text()).toEqual(defaultFileList[0].name);
  660. expect(upload.find('.customRender').at(1).text()).toEqual(defaultFileList[1].name);
  661. });
  662. it('limit=1,file replace', () => {
  663. let props = {
  664. limit: 1,
  665. defaultFileList,
  666. };
  667. const upload = getUpload(props);
  668. const file = createFile(100, 'a.png');
  669. const event = createEvent(file);
  670. trigger(upload, event);
  671. expect(upload.state().fileList.length).toEqual(1);
  672. expect(upload.state().fileList[0].name).toEqual('a.png');
  673. });
  674. it('onAcceptInvalid when file change', () => {
  675. const fakeOnAcceptInvalid = sinon.spy();
  676. let props = {
  677. accept: 'image/png',
  678. onAcceptInvalid: fakeOnAcceptInvalid,
  679. };
  680. const upload = getUpload(props);
  681. const input = upload.find(`.${BASE_CLASS_PREFIX}-upload-hidden-input`).at(0);
  682. const file = createFile(100, 'a.jpg', 'image/jpg');
  683. const event = { target: { files: [file] } };
  684. input.simulate('change', event);
  685. const arg = fakeOnAcceptInvalid.firstCall.args[0];
  686. expect(fakeOnAcceptInvalid.calledOnce).toEqual(true);
  687. expect(Array.isArray(arg)).toEqual(true);
  688. expect(arg.length === 1).toEqual(true);
  689. expect(arg[0].name === 'a.jpg').toEqual(true);
  690. });
  691. it('onAcceptInvalid when file replace', () => {
  692. const fakeOnAcceptInvalid = sinon.spy();
  693. let props = {
  694. defaultFileList: [
  695. {
  696. uid: '1',
  697. name: 'vigo.png',
  698. status: 'success',
  699. size: '130KB',
  700. preview: true,
  701. url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dbf7351bb779433d17c4f50478cf42f7.jpg',
  702. },
  703. ],
  704. showReplace: true,
  705. accept: 'image/png',
  706. onAcceptInvalid: fakeOnAcceptInvalid,
  707. };
  708. const upload = getUpload(props);
  709. const input = upload.find(`.${BASE_CLASS_PREFIX}-upload-hidden-input-replace`).at(0);
  710. const file = createFile(100, 'a.jpg', 'image/jpg');
  711. const event = { target: { files: [file] } };
  712. upload.setState({ replaceIdx: 0 }, () => {
  713. input.simulate('change', event);
  714. const arg = fakeOnAcceptInvalid.firstCall.args[0];
  715. expect(fakeOnAcceptInvalid.calledOnce).toEqual(true);
  716. expect(Array.isArray(arg)).toEqual(true);
  717. expect(arg.length === 1).toEqual(true);
  718. expect(arg[0].name === 'a.jpg').toEqual(true);
  719. });
  720. });
  721. it('beforeRemove effects', () => {
  722. const props = {
  723. fileList: [
  724. {
  725. uid: '1',
  726. name: 'vigo.png',
  727. status: 'success',
  728. size: '130KB',
  729. preview: true,
  730. url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dbf7351bb779433d17c4f50478cf42f7.jpg',
  731. },
  732. ],
  733. };
  734. const spyOnChange = sinon.spy();
  735. const spyOnRemove = sinon.spy();
  736. const spyOnChangePass = sinon.spy();
  737. const spyOnRemovePass = sinon.spy();
  738. const upload = getUpload({
  739. ...props,
  740. beforeRemove: () => false,
  741. onChange: spyOnChange,
  742. onRemove: spyOnRemove,
  743. });
  744. const uploadPass = getUpload({
  745. ...props,
  746. beforeRemove: () => true,
  747. onChange: spyOnChangePass,
  748. onRemove: spyOnRemovePass,
  749. });
  750. const removeBtn = upload.find(`.${BASE_CLASS_PREFIX}-upload-file-card-close`).at(0);
  751. const removeBtnPass = uploadPass.find(`.${BASE_CLASS_PREFIX}-upload-file-card-close`).at(0);
  752. const event = { target: {} };
  753. removeBtn.simulate('click', event);
  754. removeBtnPass.simulate('click', event);
  755. setTimeout(() => {
  756. expect(spyOnChange.callCount).toEqual(0);
  757. expect(spyOnRemove.callCount).toEqual(0);
  758. expect(spyOnChangePass.callCount).toEqual(1);
  759. expect(Array.isArray(spyOnChangePass.firstCall.args[0].fileList)).toEqual(true);
  760. expect(spyOnChangePass.firstCall.args[0].fileList.length).toEqual(0);
  761. expect(spyOnRemovePass.callCount).toEqual(1);
  762. expect(spyOnRemovePass.firstCall.args[0].uid).toEqual('1');
  763. });
  764. });
  765. it('beforeClear effects', () => {
  766. const props = {
  767. fileList: [
  768. {
  769. uid: '1',
  770. name: 'vigo.png',
  771. status: 'success',
  772. size: '130KB',
  773. preview: true,
  774. url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dbf7351bb779433d17c4f50478cf42f7.jpg',
  775. },
  776. ],
  777. };
  778. const spyOnChange = sinon.spy();
  779. const spyOnClear = sinon.spy();
  780. const spyOnChangePass = sinon.spy();
  781. const spyOnClearPass = sinon.spy();
  782. const spyOnChangeReject = sinon.spy();
  783. const spyOnClearReject = sinon.spy();
  784. const upload = getUpload({
  785. ...props,
  786. beforeClear: () => Promise.resolve(false),
  787. onChange: spyOnChange,
  788. onClear: spyOnClear,
  789. });
  790. const uploadPass = getUpload({
  791. ...props,
  792. beforeClear: () => Promise.resolve(true),
  793. onChange: spyOnChangePass,
  794. onClear: spyOnClearPass,
  795. });
  796. const uploadReject = getUpload({
  797. ...props,
  798. beforeClear: () => Promise.reject(),
  799. onChange: spyOnChangeReject,
  800. onClear: spyOnClearReject,
  801. });
  802. const clearBtn = upload.find(`.${BASE_CLASS_PREFIX}-upload-file-list-title-clear`).at(0);
  803. const clearBtnPass = uploadPass.find(`.${BASE_CLASS_PREFIX}-upload-file-list-title-clear`).at(0);
  804. const clearBtnReject = uploadReject.find(`.${BASE_CLASS_PREFIX}-upload-file-list-title-clear`).at(0);
  805. const event = { target: {} };
  806. clearBtn.simulate('click', event);
  807. clearBtnPass.simulate('click', event);
  808. clearBtnReject.simulate('click', event);
  809. setTimeout(() => {
  810. expect(spyOnChange.callCount).toEqual(0);
  811. expect(spyOnClear.callCount).toEqual(0);
  812. expect(spyOnChangePass.callCount).toEqual(1);
  813. expect(Array.isArray(spyOnChangePass.firstCall.args[0].fileList)).toEqual(true);
  814. expect(spyOnChangePass.firstCall.args[0].fileList.length).toEqual(0);
  815. expect(spyOnChangeReject.callCount).toEqual(0);
  816. expect(spyOnClearReject.callCount).toEqual(0);
  817. });
  818. });
  819. it('insert method', () => {
  820. const props = {
  821. defaultFileList: [],
  822. };
  823. const upload = getUpload(props);
  824. const uploadInstance = upload.instance();
  825. const file_0 = new File([new ArrayBuffer(1024)], 'chucknorris_0.png', { type: 'image/png' });
  826. const file_1 = new File([new ArrayBuffer(1024)], 'chucknorris_1.png', { type: 'image/png' });
  827. const file_2 = new File([new ArrayBuffer(1024)], 'chucknorris_2.png', { type: 'image/png' });
  828. expect(uploadInstance instanceof Upload).toEqual(true);
  829. expect(Object.prototype.hasOwnProperty.call(uploadInstance, 'insert')).toEqual(true);
  830. /**
  831. * test fileList state should be [] => [file_0] => [file_1, file_0] => [file_1, file_2, file_0]
  832. */
  833. upload.instance().insert([file_0]);
  834. upload.instance().insert([file_1], 0);
  835. upload.instance().insert([file_2], 1);
  836. expect(Array.isArray(upload.state('fileList'))).toEqual(true);
  837. expect(upload.state('fileList').length).toEqual(3);
  838. expect(upload.state('fileList')[0].name).toEqual('chucknorris_1.png');
  839. expect(upload.state('fileList')[1].name).toEqual('chucknorris_2.png');
  840. expect(upload.state('fileList')[2].name).toEqual('chucknorris_0.png');
  841. });
  842. it('showPicInfo works', () => {
  843. const props = {
  844. listType: 'picture',
  845. defaultFileList: [
  846. {
  847. uid: '1',
  848. name: 'jiafang1.jpeg',
  849. status: 'success',
  850. size: '130kb',
  851. url: 'https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg',
  852. },
  853. ],
  854. showPicInfo: true,
  855. };
  856. const upload = getUpload(props);
  857. expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload`)).toEqual(true);
  858. expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload-file-list-main`)).toEqual(true);
  859. expect(upload.exists(`.${BASE_CLASS_PREFIX}-upload-picture-file-card-pic-info`)).toEqual(true);
  860. });
  861. });