diff options
Diffstat (limited to 'spec/frontend/vue_shared/components/upload_dropzone/upload_dropzone_spec.js')
-rw-r--r-- | spec/frontend/vue_shared/components/upload_dropzone/upload_dropzone_spec.js | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/spec/frontend/vue_shared/components/upload_dropzone/upload_dropzone_spec.js b/spec/frontend/vue_shared/components/upload_dropzone/upload_dropzone_spec.js new file mode 100644 index 00000000000..11982eb513d --- /dev/null +++ b/spec/frontend/vue_shared/components/upload_dropzone/upload_dropzone_spec.js @@ -0,0 +1,174 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlIcon } from '@gitlab/ui'; +import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue'; + +jest.mock('~/flash'); + +describe('Upload dropzone component', () => { + let wrapper; + + const mockDragEvent = ({ types = ['Files'], files = [] }) => { + return { dataTransfer: { types, files } }; + }; + + const findDropzoneCard = () => wrapper.find('.upload-dropzone-card'); + const findDropzoneArea = () => wrapper.find('[data-testid="dropzone-area"]'); + const findIcon = () => wrapper.find(GlIcon); + + function createComponent({ slots = {}, data = {}, props = {} } = {}) { + wrapper = shallowMount(UploadDropzone, { + slots, + propsData: { + displayAsCard: true, + ...props, + }, + data() { + return data; + }, + }); + } + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when slot provided', () => { + it('renders dropzone with slot content', () => { + createComponent({ + slots: { + default: ['<div>dropzone slot</div>'], + }, + }); + + expect(wrapper.element).toMatchSnapshot(); + }); + }); + + describe('when no slot provided', () => { + it('renders default dropzone card', () => { + createComponent(); + + expect(wrapper.element).toMatchSnapshot(); + }); + + it('triggers click event on file input element when clicked', () => { + createComponent(); + const clickSpy = jest.spyOn(wrapper.find('input').element, 'click'); + + findDropzoneCard().trigger('click'); + expect(clickSpy).toHaveBeenCalled(); + }); + }); + + describe('when dragging', () => { + it.each` + description | eventPayload + ${'is empty'} | ${{}} + ${'contains text'} | ${mockDragEvent({ types: ['text'] })} + ${'contains files and text'} | ${mockDragEvent({ types: ['Files', 'text'] })} + ${'contains files'} | ${mockDragEvent({ types: ['Files'] })} + `('renders correct template when drag event $description', ({ eventPayload }) => { + createComponent(); + + wrapper.trigger('dragenter', eventPayload); + return wrapper.vm.$nextTick().then(() => { + expect(wrapper.element).toMatchSnapshot(); + }); + }); + + it('renders correct template when dragging stops', () => { + createComponent(); + + wrapper.trigger('dragenter'); + return wrapper.vm + .$nextTick() + .then(() => { + wrapper.trigger('dragleave'); + return wrapper.vm.$nextTick(); + }) + .then(() => { + expect(wrapper.element).toMatchSnapshot(); + }); + }); + }); + + describe('when dropping', () => { + it('emits upload event', () => { + createComponent(); + const mockFile = { name: 'test', type: 'image/jpg' }; + const mockEvent = mockDragEvent({ files: [mockFile] }); + + wrapper.trigger('dragenter', mockEvent); + return wrapper.vm + .$nextTick() + .then(() => { + wrapper.trigger('drop', mockEvent); + return wrapper.vm.$nextTick(); + }) + .then(() => { + expect(wrapper.emitted().change[0]).toEqual([[mockFile]]); + }); + }); + }); + + describe('ondrop', () => { + const mockData = { dragCounter: 1, isDragDataValid: true }; + + describe('when drag data is valid', () => { + it('emits upload event for valid files', () => { + createComponent({ data: mockData }); + + const mockFile = { type: 'image/jpg' }; + const mockEvent = mockDragEvent({ files: [mockFile] }); + + wrapper.vm.ondrop(mockEvent); + expect(wrapper.emitted().change[0]).toEqual([[mockFile]]); + }); + + it('emits error event when files are invalid', () => { + createComponent({ data: mockData }); + const mockEvent = mockDragEvent({ files: [{ type: 'audio/midi' }] }); + + wrapper.vm.ondrop(mockEvent); + expect(wrapper.emitted()).toHaveProperty('error'); + }); + + it('allows validation function to be overwritten', () => { + createComponent({ data: mockData, props: { isFileValid: () => true } }); + + const mockEvent = mockDragEvent({ files: [{ type: 'audio/midi' }] }); + + wrapper.vm.ondrop(mockEvent); + expect(wrapper.emitted()).not.toHaveProperty('error'); + }); + }); + }); + + it('applies correct classes when displaying as a standalone item', () => { + createComponent({ props: { displayAsCard: false } }); + expect(findDropzoneArea().classes()).not.toContain('gl-flex-direction-column'); + expect(findIcon().classes()).toEqual(['gl-mr-3', 'gl-text-gray-500']); + expect(findIcon().props('size')).toBe(16); + }); + + it('applies correct classes when displaying in card mode', () => { + createComponent({ props: { displayAsCard: true } }); + expect(findDropzoneArea().classes()).toContain('gl-flex-direction-column'); + expect(findIcon().classes()).toEqual(['gl-mb-2']); + expect(findIcon().props('size')).toBe(24); + }); + + it('correctly overrides description and drop messages', () => { + createComponent({ + props: { + dropToStartMessage: 'Test drop-to-start message.', + validFileMimetypes: ['image/jpg', 'image/jpeg'], + }, + slots: { + 'upload-text': '<span>Test %{linkStart}description%{linkEnd} message.</span>', + }, + }); + + expect(wrapper.element).toMatchSnapshot(); + }); +}); |