UNPKG

@uppy/transloadit

Version:

The Transloadit plugin can be used to upload files to Transloadit for all kinds of processing, such as transcoding video, resizing images, zipping/unzipping, and more

321 lines (279 loc) 9.1 kB
import Core from '@uppy/core' import { HttpResponse, http } from 'msw' import { setupServer } from 'msw/node' import { describe, expect, it, vi } from 'vitest' import Transloadit from './index.ts' import 'whatwg-fetch' // Mock EventSource for testing global.EventSource = vi.fn(() => ({ addEventListener: vi.fn(), removeEventListener: vi.fn(), close: vi.fn(), })) describe('Transloadit', () => { it('Does not leave lingering progress if getAssemblyOptions fails', () => { const error = new Error('expected failure') const uppy = new Core() uppy.use(Transloadit, { assemblyOptions() { return Promise.reject(error) }, }) uppy.addFile({ source: 'test', name: 'abc', data: new Uint8Array(100), }) return uppy .upload() .then(() => { throw new Error('Should not have succeeded') }) .catch((err) => { const fileID = Object.keys(uppy.getState().files)[0] expect(err).toBe(error) expect(uppy.getFile(fileID).progress.uploadStarted).toBe(null) }) }) it('Does not leave lingering progress if creating assembly fails', () => { const uppy = new Core() uppy.use(Transloadit, { assemblyOptions: { params: { auth: { key: 'some auth key string' }, template_id: 'some template id string', }, }, }) uppy.getPlugin('Transloadit').client.createAssembly = () => Promise.reject(new Error('VIDEO_ENCODE_VALIDATION')) uppy.addFile({ source: 'test', name: 'abc', data: new Uint8Array(100), }) return uppy.upload().then( () => { throw new Error('Should not have succeeded') }, (err) => { const fileID = Object.keys(uppy.getState().files)[0] expect(err.message).toBe( 'Transloadit: Could not create Assembly: VIDEO_ENCODE_VALIDATION', ) expect(uppy.getFile(fileID).progress.uploadStarted).toBe(null) }, ) }) it('should complete when resuming after pause', async () => { const assemblyStatusBase = { assembly_id: 'test-assembly-id', websocket_url: 'ws://localhost:8080', tus_url: 'http://localhost/resumable/files/', assembly_ssl_url: 'https://api2.transloadit.com/assemblies/test-assembly-id', } const tusUploads = new Map() let uploadIndex = 0 const tusBaseUrl = 'http://localhost/resumable/files/' const server = setupServer( http.options('http://localhost/resumable/files*', () => { return new HttpResponse(null, { status: 204, headers: { 'Tus-Resumable': '1.0.0', 'Tus-Version': '1.0.0', 'Tus-Extension': 'creation,creation-defer-length', }, }) }), http.post('http://localhost/resumable/files*', ({ request }) => { const uploadLengthHeader = request.headers.get('upload-length') const uploadLength = uploadLengthHeader ? Number(uploadLengthHeader) : 0 const uploadId = `test-upload-${uploadIndex++}` tusUploads.set(uploadId, { length: Number.isNaN(uploadLength) ? 0 : uploadLength, offset: 0, }) return new HttpResponse(null, { status: 201, headers: { Location: `${tusBaseUrl}${uploadId}`, 'Upload-Offset': '0', 'Tus-Resumable': '1.0.0', }, }) }), http.head('http://localhost/resumable/files/:uploadId', ({ params }) => { const upload = tusUploads.get(params.uploadId) if (!upload) { return new HttpResponse(null, { status: 404 }) } return new HttpResponse(null, { status: 200, headers: { 'Upload-Offset': String(upload.offset), 'Upload-Length': String(upload.length), 'Tus-Resumable': '1.0.0', }, }) }), http.patch( 'http://localhost/resumable/files/:uploadId', async ({ request, params }) => { const upload = tusUploads.get(params.uploadId) if (!upload) { return new HttpResponse(null, { status: 404 }) } if (upload.offset === 0) { await new Promise((resolve) => setTimeout(resolve, 200)) } const body = await request.arrayBuffer() const offsetHeader = request.headers.get('upload-offset') const baseOffset = offsetHeader ? Number(offsetHeader) : upload.offset const nextOffset = baseOffset + body.byteLength upload.offset = nextOffset return new HttpResponse(null, { status: 204, headers: { 'Upload-Offset': String(nextOffset), 'Tus-Resumable': '1.0.0', }, }) }, ), http.post('https://api2.transloadit.com/assemblies', ({ request }) => { return HttpResponse.json({ ...assemblyStatusBase, ok: 'ASSEMBLY_EXECUTING', }) }), http.get('https://api2.transloadit.com/assemblies/*', () => { return HttpResponse.json({ ...assemblyStatusBase, ok: 'ASSEMBLY_COMPLETED', results: {}, }) }), http.post('https://transloaditstatus.com/client_error', () => { return HttpResponse.json({}) }), ) server.listen({ onUnhandledRequest: 'error' }) const uppy = new Core() const successSpy = vi.fn() uppy.on('complete', successSpy) uppy.use(Transloadit, { assemblyOptions: { params: { auth: { key: 'test-auth-key' }, template_id: 'test-template-id', }, }, }) uppy.addFile({ source: 'test', name: 'cat.jpg', data: new File([new Uint8Array([1, 2, 3, 4, 5])], 'cat.jpg', { type: 'image/jpeg', }), }) uppy.addFile({ source: 'test', name: 'traffic.jpg', data: new File([new Uint8Array([6, 7, 8, 9, 10, 11])], 'traffic.jpg', { type: 'image/jpeg', }), }) // Initially should be true expect(uppy.getState().allowNewUpload).toBe(true) const uploadPromise = uppy.upload() // Should be set to false during upload expect(uppy.getState().allowNewUpload).toBe(false) // Trying to add a new file during upload with Transloadit should not be possible expect(() => uppy.addFile({ source: 'test', name: 'additionalFile.jpg', data: new File([new Uint8Array([0])], 'additionalFile.jpg', { type: 'image/jpeg', }), }), ).toThrowError('Cannot add more files') await new Promise((resolve) => setTimeout(resolve, 100)) uppy.pauseAll() uppy.resumeAll() await uploadPromise expect(successSpy).toHaveBeenCalled() // Should be reset to true after upload completes expect(uppy.getState().allowNewUpload).toBe(true) server.close() }) it('resets allowNewUpload to true on preprocessor error', async () => { const uppy = new Core() uppy.use(Transloadit, { assemblyOptions: { params: { auth: { key: 'test-auth-key' }, template_id: 'test-template-id', }, }, }) // Mock createAssembly to throw an error uppy.getPlugin('Transloadit').client.createAssembly = () => Promise.reject(new Error('Assembly creation failed')) uppy.addFile({ source: 'test', name: 'test.jpg', data: Buffer.from('test file content'), }) // Initially should be true expect(uppy.getState().allowNewUpload).toBe(true) try { await uppy.upload() } catch { // Expected to fail } // Should be reset to true after error expect(uppy.getState().allowNewUpload).toBe(true) }) it('resets allowNewUpload to true on cancel-all', async () => { const uppy = new Core() uppy.use(Transloadit, { assemblyOptions: { params: { auth: { key: 'test-auth-key' }, template_id: 'test-template-id', }, }, }) // Manually set allowNewUpload to false to simulate an upload in progress uppy.setState({ allowNewUpload: false }) expect(uppy.getState().allowNewUpload).toBe(false) // Simulate cancel-all uppy.cancelAll() // Should be reset to true expect(uppy.getState().allowNewUpload).toBe(true) }) it('resets allowNewUpload to true on error event', () => { const uppy = new Core() uppy.use(Transloadit, { assemblyOptions: { params: { auth: { key: 'test-auth-key' }, template_id: 'test-template-id', }, }, }) // Manually set allowNewUpload to false to simulate an upload in progress uppy.setState({ allowNewUpload: false }) expect(uppy.getState().allowNewUpload).toBe(false) // Trigger error event uppy.emit('error', { name: 'TestError', message: 'Test error message', }) // Should be reset to true expect(uppy.getState().allowNewUpload).toBe(true) }) })