Skip to content

Commit

Permalink
fix(upload): apply accept attribute (testing-library#558)
Browse files Browse the repository at this point in the history
Co-authored-by: Philipp Fritsche <ph.fritsche@gmail.com>
  • Loading branch information
LauraBeatris and ph-fritsche authored Feb 22, 2021
1 parent ad427a6 commit d513d6e
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 4 deletions.
52 changes: 52 additions & 0 deletions src/__tests__/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,55 @@ test('should call onChange/input bubbling up the event when a file is selected',
expect(onInputInput).toHaveBeenCalledTimes(1)
expect(onInputForm).toHaveBeenCalledTimes(1)
})

test('should upload file with accepted format', () => {
const file = new File(['hello'], 'hello.png', {type: 'image/png'})
const {element} = setup('<input type="file" accept="image/png" />')

userEvent.upload(element, file)

expect(element.files).toHaveLength(1)
})

test('should upload multiple files with accepted format', () => {
const files = [
new File(['hello'], 'hello.png', {type: 'image/png'}),
new File(['there'], 'there.jpg', {type: 'audio/mp3'}),
new File(['there'], 'there.csv', {type: 'text/csv'}),
new File(['there'], 'there.jpg', {type: 'video/mp4'}),
]
const {element} = setup(`
<input
type="file"
accept="video/*,audio/*,.png" multiple
/>
`)

userEvent.upload(element, files)

expect(element.files).toHaveLength(3)
})

test('should not upload file with unaccepted format', () => {
const file = new File(['hello'], 'hello.png', {type: 'image/png'})
const {element} = setup('<input type="file" accept="image/jpg" />')

userEvent.upload(element, file)

expect(element.files).toHaveLength(0)
})

test('should not upload multiple files with unaccepted formats', () => {
const files = [
new File(['hello'], 'hello.txt', {type: 'text/plain'}),
new File(['there'], 'there.pdf', {type: 'application/pdf'}),
new File(['there'], 'there.png', {type: 'image/png'}),
]
const {element} = setup(`
<input id="files" type="file" accept="video/*" multiple />
`)

userEvent.upload(element, files)

expect(element.files).toHaveLength(0)
})
30 changes: 26 additions & 4 deletions src/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ function upload(element, fileOrFiles, init) {

const input = element.tagName === 'LABEL' ? element.control : element

const files = (Array.isArray(fileOrFiles)
? fileOrFiles
: [fileOrFiles]
).slice(0, input.multiple ? undefined : 1)
const files = (Array.isArray(fileOrFiles) ? fileOrFiles : [fileOrFiles])
.filter(file => isAcceptableFile(file, element.accept))
.slice(0, input.multiple ? undefined : 1)

// blur fires when the file selector pops up
blur(element, init)
// focus fires when they make their selection
focus(element, init)

// treat empty array as if the user just closed the file upload dialog
if (files.length === 0) {
return
}

// the event fired in the browser isn't actually an "input" or "change" event
// but a new Event with a type set to "input" and "change"
// Kinda odd...
Expand All @@ -46,4 +50,22 @@ function upload(element, fileOrFiles, init) {
})
}

function isAcceptableFile(file, accept) {
if (!accept) {
return true
}

const wildcards = ['audio/*', 'image/*', 'video/*']

return accept.split(',').some(acceptToken => {
if (acceptToken[0] === '.') {
// tokens starting with a dot represent a file extension
return file.name.endsWith(acceptToken)
} else if (wildcards.includes(acceptToken)) {
return file.type.startsWith(acceptToken.substr(0, acceptToken.length - 1))
}
return file.type === acceptToken
})
}

export {upload}

0 comments on commit d513d6e

Please sign in to comment.