-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(spec/bnFileInput): add bnFileInput tests and rename file
- Loading branch information
fprebolledo
committed
Jul 14, 2023
1 parent
fcebb0f
commit c90debe
Showing
3 changed files
with
260 additions
and
9 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
import waitForExpect from 'wait-for-expect'; | ||
import { mount } from '@vue/test-utils'; | ||
import { defineComponent, type PropType } from 'vue'; | ||
import { Form } from 'vee-validate'; | ||
import BnFileInput, { type FileType } from './BnFileInput.vue'; | ||
import MockDataTransfer from '../../mocks/data-transfer'; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
(global as any).DataTransfer = MockDataTransfer; | ||
|
||
function isRequired(val: FileType) { | ||
if (val === undefined || val.length === 0) { | ||
return 'This field is required'; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
function generateFile(name: string) { | ||
return new File([name], `${name}.txt`, { | ||
type: 'text/plain', | ||
}); | ||
} | ||
|
||
const baseFileInput = `<BnFileInput | ||
v-model="image" | ||
name="image" | ||
/>`; | ||
|
||
const arrayFileInput = `<BnFileInput | ||
v-model="imagesArray" | ||
name="image" | ||
multiple | ||
/>`; | ||
|
||
const customArrayFileInput = `<BnFileInput | ||
v-model="imagesArray" | ||
name="image" | ||
multiple | ||
> | ||
<template #default="{ imagePreviewPath, openFileDialog, value, addFile, removeFile }"> | ||
<div class="w-full"> | ||
<button | ||
id="add-file" | ||
v-if="value && value.length > 0" | ||
class="mb-2 rounded border border-gray-300 py-1 px-2 text-sm shadow" | ||
@click="addFile()" | ||
> | ||
Add File | ||
</button> | ||
<button | ||
v-else | ||
class="mb-2 rounded border border-gray-300 py-1 px-2 text-sm shadow" | ||
@click="openFileDialog()" | ||
> | ||
Browse | ||
</button> | ||
<ul | ||
v-if="value && isFileList(value)" | ||
class="w-full" | ||
> | ||
<li | ||
v-for="file in value" | ||
:key="file.name" | ||
class="flex w-full items-center border border-t-0 p-1 text-sm first:border-t" | ||
> | ||
<img | ||
:src="imagePreviewPath(file)" | ||
class="mr-2 h-6 w-6 rounded-full" | ||
> | ||
<span class="truncate">{{ file.name }}</span> ({{ file.size / 1000 }} KB) | ||
<button | ||
id="remove-file" | ||
class="ml-auto" | ||
@click="removeFile(file)" | ||
> | ||
🗑 | ||
</button> | ||
</li> | ||
</ul> | ||
</div> | ||
</template> | ||
</BnFileInput>`; | ||
|
||
function isFileList(object: File[] | File): object is File[] { | ||
return !(object instanceof File); | ||
} | ||
|
||
const fileInputs = { | ||
single: baseFileInput, | ||
multiple: arrayFileInput, | ||
customMultiple: customArrayFileInput, | ||
}; | ||
|
||
function generateExampleForm(fileInputType: 'single' | 'multiple' | 'customMultiple' = 'single') { | ||
return defineComponent({ | ||
components: { | ||
Form, BnFileInput, | ||
}, | ||
props: { | ||
validationSchema: { | ||
type: Object, | ||
default: () => ({}), | ||
}, | ||
initialImages: { | ||
type: Array as PropType<File[]>, | ||
default: () => ([]), | ||
}, | ||
}, | ||
data(props): { image?: File, imagesArray: File[], submittedValues?: Record<string, unknown> } { | ||
return { | ||
image: undefined, | ||
imagesArray: props.initialImages || [], | ||
submittedValues: undefined, | ||
}; | ||
}, | ||
methods: { | ||
isFileList, | ||
submit(values: Record<string, unknown>) { | ||
this.submittedValues = values; | ||
}, | ||
}, | ||
template: ` | ||
<Form @submit="submit" :validation-schema="validationSchema"> | ||
${fileInputs[fileInputType]} | ||
</Form> | ||
`, | ||
}); | ||
} | ||
|
||
const file = generateFile('test'); | ||
|
||
describe('BnFileInput', () => { | ||
it('should mount', () => { | ||
const wrapper = mount(BnFileInput, { props: { name: 'image' } }); | ||
expect(wrapper.findAll('input')).toHaveLength(1); | ||
}); | ||
|
||
it('v-model should work', async () => { | ||
const wrapper = mount(generateExampleForm()); | ||
const fileInput = wrapper.getComponent(BnFileInput).find('input'); | ||
|
||
const event = document.createEvent('HTMLEvents'); | ||
event.initEvent('change', false, true); | ||
Object.defineProperty(event, 'target', { writable: false, value: { files: [file] } }); | ||
|
||
fileInput.element.dispatchEvent(event); | ||
await waitForExpect(() => { | ||
expect(wrapper.vm.image).toBe(file); | ||
}); | ||
}); | ||
|
||
it('vee-validate submit should work', async () => { | ||
const wrapper = mount(generateExampleForm()); | ||
const fileInput = wrapper.getComponent(BnFileInput).find('input'); | ||
|
||
const event = document.createEvent('HTMLEvents'); | ||
event.initEvent('change', false, true); | ||
Object.defineProperty(event, 'target', { writable: false, value: { files: [file] } }); | ||
|
||
fileInput.element.dispatchEvent(event); | ||
wrapper.find('form').trigger('submit'); | ||
await waitForExpect(() => { | ||
expect(wrapper.vm.submittedValues).toStrictEqual({ image: file }); | ||
}); | ||
}); | ||
|
||
it('v-model should work with array', async () => { | ||
const wrapper = mount(generateExampleForm('multiple')); | ||
const fileInput = wrapper.getComponent(BnFileInput).find('input'); | ||
const fileList = [file, generateFile('test2')]; | ||
|
||
const event = document.createEvent('HTMLEvents'); | ||
event.initEvent('change', false, true); | ||
Object.defineProperty(event, 'target', { writable: false, value: { files: fileList } }); | ||
|
||
fileInput.element.dispatchEvent(event); | ||
await waitForExpect(() => { | ||
expect(wrapper.vm.imagesArray).toStrictEqual(fileList); | ||
}); | ||
}); | ||
|
||
it('should validate using prop rules', async () => { | ||
const wrapper = mount(BnFileInput, { props: { name: 'image', rules: isRequired } }); | ||
expect(wrapper.classes().includes('bn-file-input--error')).toBe(false); | ||
const input = wrapper.find('button'); | ||
input.trigger('click'); | ||
await waitForExpect(() => { | ||
expect(wrapper.classes().includes('bn-file-input--error')).toBe(true); | ||
}); | ||
}); | ||
|
||
it('should validate using form rules', async () => { | ||
const wrapper = mount(generateExampleForm(), { props: { validationSchema: { image: isRequired } } }); | ||
const input = wrapper.find('button'); | ||
const inputWrapper = wrapper.find('div'); | ||
expect(inputWrapper.classes().includes('bn-file-input--error')).toBe(false); | ||
input.trigger('click'); | ||
await waitForExpect(() => { | ||
expect(inputWrapper.classes().includes('bn-file-input--error')).toBe(true); | ||
}); | ||
}); | ||
|
||
it('should add file', async () => { | ||
const fileList = [file, generateFile('test2')]; | ||
const wrapper = mount(generateExampleForm('customMultiple'), { props: { initialImages: fileList } }); | ||
const newFile = generateFile('test3'); | ||
|
||
wrapper.find('#add-file').trigger('click'); | ||
|
||
const fileInput = wrapper.getComponent(BnFileInput).find('input'); | ||
|
||
const event = document.createEvent('HTMLEvents'); | ||
event.initEvent('change', false, true); | ||
Object.defineProperty(event, 'target', { writable: false, value: { files: [newFile] } }); | ||
|
||
fileInput.element.dispatchEvent(event); | ||
await waitForExpect(() => { | ||
expect(wrapper.vm.imagesArray).toStrictEqual([...fileList, newFile]); | ||
}); | ||
}); | ||
|
||
it('should remove file', async () => { | ||
const fileList = [generateFile('test2'), file]; | ||
const wrapper = mount(generateExampleForm('customMultiple'), { props: { initialImages: fileList } }); | ||
|
||
wrapper.find('#remove-file').trigger('click'); | ||
|
||
await waitForExpect(() => { | ||
expect(wrapper.vm.imagesArray).toStrictEqual([file]); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-empty-function | ||
function mockFunction(): void {} | ||
|
||
const emptyFileList = { | ||
length: 0, | ||
item: () => null, | ||
}; | ||
|
||
Object.defineProperty(HTMLInputElement.prototype, 'files', { | ||
writable: true, | ||
value: emptyFileList, | ||
}); | ||
|
||
const mockDataTransfer = { | ||
files: emptyFileList, | ||
items: { | ||
add: mockFunction, | ||
}, | ||
|
||
setData: mockFunction, | ||
getData: mockFunction, | ||
}; | ||
|
||
export default function MockDataTransfer() { | ||
return mockDataTransfer; | ||
} |