react-activestorage-provider
Version:
A React component that allows easy file upload using ActiveStorage
196 lines (174 loc) • 6.04 kB
Flow
/**
* @noflow
*/
import Upload from './Upload'
jest.mock('activestorage', () => {
return {
DirectUpload: jest.fn(file => ({
id: 'id',
file,
create(cb) {
cb(null, { signed_id: 'signedId' })
},
})),
}
})
const file = new File([], 'file')
const options = {
onChangeFile: jest.fn(),
onBeforeBlobRequest: jest.fn(),
onBeforeStorageRequest: jest.fn(),
}
describe('Upload', () => {
beforeEach(() => {
jest.clearAllMocks()
})
describe('constructor', () => {
it('falls back to default options', () => {
const upload = new Upload(file, {
...options,
directUploadsPath: undefined,
})
expect(upload.options.directUploadsPath).toEqual(
Upload.CONVENTIONAL_DIRECT_UPLOADS_PATH
)
})
it('reports that it is waiting to upload', () => {
new Upload(file, options) // eslint-disable-line no-new
expect(options.onChangeFile).toHaveBeenCalledWith({
id: { state: 'waiting', id: 'id', file },
})
})
})
describe('directUploadsUrl', () => {
it('uses the conventional direct upload url by default', () => {
const upload = new Upload(file, options)
expect(upload.directUploadsUrl).toEqual(
'/rails/active_storage/direct_uploads'
)
})
test.each`
protocol | host | port | url
${'http'} | ${'0.0.0.0'} | ${3000} | ${'http://0.0.0.0:3000/rails/active_storage/direct_uploads'}
${'http'} | ${'0.0.0.0'} | ${undefined} | ${'http://0.0.0.0/rails/active_storage/direct_uploads'}
${'http://'} | ${'0.0.0.0'} | ${undefined} | ${'http://0.0.0.0/rails/active_storage/direct_uploads'}
${undefined} | ${'0.0.0.0'} | ${3000} | ${'https://0.0.0.0:3000/rails/active_storage/direct_uploads'}
${undefined} | ${'0.0.0.0'} | ${undefined} | ${'https://0.0.0.0/rails/active_storage/direct_uploads'}
`(
'allows the consumer to specify a different origin { protocol: $protocol, host: $host, port: $port}',
({ protocol, host, port, url }) => {
const origin = { protocol, host, port }
const upload = new Upload(file, { ...options, origin })
expect(upload.directUploadsUrl).toEqual(url)
}
)
})
describe('start()', () => {
it('resolves with the signed id from the direct upload', async () => {
const upload = new Upload(file, options)
expect(await upload.start()).toEqual('signedId')
})
it('reports that the upload is finished if it does so', async () => {
const upload = new Upload(file, options)
await upload.start()
expect(options.onChangeFile).toHaveBeenCalledWith({
id: { state: 'finished', id: 'id', file },
})
})
describe('if the upload fails', () => {
jest.resetModules().doMock('activestorage', () => {
return {
DirectUpload: jest.fn(file => ({
id: 'id',
file,
create(cb) {
cb('Failed') // eslint-disable-line standard/no-callback-literal
},
})),
}
})
const Upload = require('./Upload').default
it('rejects with the error', async () => {
const upload = new Upload(file, options)
let error
await upload.start().catch(e => (error = e))
expect(error).toEqual('Failed')
})
it('reports an error', async () => {
const upload = new Upload(file, options)
await upload.start().catch(() => {})
expect(options.onChangeFile).toHaveBeenCalledWith({
id: { state: 'error', id: 'id', file, error: 'Failed' },
})
})
})
})
describe('as a direct upload delegate', () => {
describe('directUploadWillCreateBlobWithXHR', () => {
it('calls options.onBeforeBlobRequest', () => {
const xhr = new XMLHttpRequest()
const upload = new Upload(file, options)
upload.directUploadWillCreateBlobWithXHR(xhr)
expect(options.onBeforeBlobRequest).toHaveBeenCalledWith({
id: 'id',
file,
xhr,
})
})
describe('if headers are provided', () => {
const mockXHR = {
setRequestHeader: jest.fn(),
readyState: 1,
}
window.XMLHttpRequest = jest.fn(() => mockXHR)
it('adds the headers to the xhr request', () => {
const xhr = new XMLHttpRequest()
const headerKey = 'Test-Header'
const headerValue = 'testValue'
const headers = { [headerKey]: headerValue }
const upload = new Upload(file, { headers, ...options })
upload.directUploadWillCreateBlobWithXHR(xhr)
expect(mockXHR.setRequestHeader).toHaveBeenCalledWith(
headerKey,
headerValue
)
})
})
})
describe('directUploadWillStoreFileWithXHR', () => {
it('calls options.onBeforeStorageRequest', () => {
const xhr = { upload: { addEventListener: jest.fn() } }
const upload = new Upload(file, options)
upload.directUploadWillStoreFileWithXHR(xhr)
expect(options.onBeforeStorageRequest).toHaveBeenCalledWith({
id: 'id',
file,
xhr,
})
})
it('listens for progress', () => {
const xhr = { upload: { addEventListener: jest.fn() } }
const upload = new Upload(file, options)
upload.directUploadWillStoreFileWithXHR(xhr)
expect(xhr.upload.addEventListener).toHaveBeenCalledWith(
'progress',
upload.handleProgress
)
})
})
describe('handleProgress', () => {
it('reports uploading with the progress', () => {
const upload = new Upload(file, options)
upload.handleProgress({ loaded: 20, total: 100 })
expect(options.onChangeFile).toHaveBeenCalledWith({
id: {
id: 'id',
state: 'uploading',
file,
progress: 20,
},
})
})
})
})
})