Skip to content

Commit

Permalink
feat(spec/bnFileInput): add bnFileInput tests and rename file
Browse files Browse the repository at this point in the history
  • Loading branch information
fprebolledo committed Jul 14, 2023
1 parent fcebb0f commit c90debe
Show file tree
Hide file tree
Showing 3 changed files with 260 additions and 9 deletions.
9 changes: 0 additions & 9 deletions src/components/BnFileInput/BnFIleInput.spec.ts

This file was deleted.

233 changes: 233 additions & 0 deletions src/components/BnFileInput/BnFileInput.spec.ts
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]);
});
});
});
27 changes: 27 additions & 0 deletions src/mocks/data-transfer.ts
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;
}

0 comments on commit c90debe

Please sign in to comment.