From 595860655806cc7684d21b64508129471a0e4279 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 28 Jan 2020 15:39:59 -0600 Subject: [PATCH 1/8] chore: check-in work --- .../FileUploader/FileUploader-test.js | 356 ------------------ .../__tests__/FileUploader-test.js | 89 +++++ .../__tests__/FileUploaderButton-test.js | 112 ++++++ .../FileUploaderDropContainer-test.js | 104 +++++ .../__tests__/FileUploaderItem-test.js | 26 ++ .../__tests__/FileUploaderSkeleton-test.js | 34 ++ .../FileUploader/__tests__/Filename-test.js | 83 ++++ .../__snapshots__/Filename-test.js.snap} | 0 .../src/components/FileUploader/index.js | 2 +- 9 files changed, 449 insertions(+), 357 deletions(-) delete mode 100644 packages/react/src/components/FileUploader/FileUploader-test.js create mode 100644 packages/react/src/components/FileUploader/__tests__/FileUploader-test.js create mode 100644 packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js create mode 100644 packages/react/src/components/FileUploader/__tests__/FileUploaderDropContainer-test.js create mode 100644 packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js create mode 100644 packages/react/src/components/FileUploader/__tests__/FileUploaderSkeleton-test.js create mode 100644 packages/react/src/components/FileUploader/__tests__/Filename-test.js rename packages/react/src/components/FileUploader/{__snapshots__/FileUploader-test.js.snap => __tests__/__snapshots__/Filename-test.js.snap} (100%) diff --git a/packages/react/src/components/FileUploader/FileUploader-test.js b/packages/react/src/components/FileUploader/FileUploader-test.js deleted file mode 100644 index 367dc88e217a..000000000000 --- a/packages/react/src/components/FileUploader/FileUploader-test.js +++ /dev/null @@ -1,356 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2018 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import { settings } from 'carbon-components'; -import { Close16, CheckmarkFilled16 } from '@carbon/icons-react'; -import { mount, shallow } from 'enzyme'; -import FileUploader, { FileUploaderButton, Filename } from './FileUploader'; -import FileUploaderDropContainer from './FileUploaderDropContainer'; -import FileUploaderItem from './FileUploaderItem'; -import FileUploaderSkeleton from '../FileUploader/FileUploader.Skeleton'; -import Loading from '../Loading'; - -const { prefix } = settings; - -describe('Filename', () => { - describe('renders as expected', () => { - const icons = [Loading, Close16, CheckmarkFilled16]; - const statuses = ['uploading', 'edit', 'complete']; - statuses.forEach((status, i) => { - const wrapper = mount( - - ); - - it('renders upload status icon as expected', () => { - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find(icons[i]).length).toBe(1); - }); - }); - }); - - describe('Check that functions passed in as props are called', () => { - let wrapper; - let mockProps; - - beforeEach(() => { - mockProps = { - onClick: jest.fn(), - onKeyDown: jest.fn(), - status: 'complete', - }; - wrapper = mount(); - }); - - it('should call onClick', () => { - wrapper.simulate('click'); - expect(mockProps.onClick).toBeCalled(); - }); - - it('should call onKeyDown', () => { - wrapper.simulate('keydown'); - expect(mockProps.onKeyDown).toBeCalled(); - }); - }); - - describe('click on edit icon (close--solid)', () => { - it('should have a click event', () => { - const mountWrapper = mount( - - ); - const onClick = jest.fn(); - mountWrapper.setProps({ onClick, status: 'edit' }); - mountWrapper.find(Close16).simulate('click'); - expect(onClick).toBeCalled(); - }); - }); -}); - -describe('FileUploaderItem', () => { - const mountWrapper = mount( - - ); - - describe('click on edit icon (close--solid)', () => { - it('should have a click event', () => { - const onDelete = jest.fn(); - mountWrapper.setProps({ onDelete, status: 'edit' }); - mountWrapper.find(Close16).simulate('click'); - expect(onDelete).toBeCalled(); - }); - }); -}); - -describe('FileUploaderButton', () => { - const button = ; - const mountWrapper = mount(button); - - describe('Renders as expected with default props', () => { - it('renders with expected className', () => { - expect(mountWrapper.find('label').hasClass(`${prefix}--btn`)).toBe(true); - }); - - it('renders with given className', () => { - expect(mountWrapper.hasClass('extra-class')).toBe(true); - }); - - it('renders with default labelText prop', () => { - expect(mountWrapper.props().labelText).toEqual('Add file'); - }); - - it('renders with default buttonKind prop', () => { - expect(mountWrapper.props().buttonKind).toEqual('primary'); - }); - - it('renders with expected button className', () => { - expect(mountWrapper.find(`.${prefix}--btn--primary`).exists()).toBe(true); - }); - - it('renders with default multiple prop', () => { - expect(mountWrapper.props().multiple).toEqual(false); - }); - - it('renders with default disableLabelChanges prop', () => { - expect(mountWrapper.props().disableLabelChanges).toEqual(false); - }); - - it('renders with default accept prop', () => { - expect(mountWrapper.props().accept).toEqual([]); - }); - - it('renders with default disabled prop', () => { - expect(mountWrapper.props().disabled).toBe(false); - }); - - it('disables file upload input', () => { - const wrapper = shallow(button); - wrapper.setProps({ disabled: true }); - expect(wrapper.find('input').prop('disabled')).toEqual(true); - }); - - it('does have default role', () => { - expect(mountWrapper.props().role).toBeTruthy(); - }); - - it('resets the input value onClick', () => { - const input = mountWrapper.find(`.${prefix}--visually-hidden`); - input.instance().value = ''; - const evt = { target: { value: input.instance().value } }; - input.simulate('click', evt); - - expect(evt.target.value).toEqual(null); - }); - }); - - describe('Unique id props', () => { - it('each FileUploaderButton should have a unique ID', () => { - const mountedButtons = mount( -
- - -
- ); - const firstButton = mountedButtons.find(FileUploaderButton).at(0); - const lastButton = mountedButtons.find(FileUploaderButton).at(1); - const isEqual = firstButton === lastButton; - expect(isEqual).toBe(false); - }); - }); - - describe('Update labelText', () => { - it('should have equal state and props', () => { - expect( - shallow().state().labelText - ).toEqual('foo'); - }); - - it('should change the label text upon change in props', () => { - mountWrapper.setProps({ labelText: 'foo' }); - mountWrapper.setState({ labelText: 'foo' }); - mountWrapper.setProps({ labelText: 'bar' }); - expect(mountWrapper.state().labelText).toEqual('bar'); - }); - - it('should avoid change the label text upon setting props, unless there the value actually changes', () => { - mountWrapper.setProps({ labelText: 'foo' }); - mountWrapper.setState({ labelText: 'bar' }); - mountWrapper.setProps({ labelText: 'foo' }); - expect(mountWrapper.state().labelText).toEqual('bar'); - }); - }); -}); - -describe('FileUploader', () => { - const fileUploader = ; - const mountWrapper = mount(fileUploader); - - describe('Renders as expected with defaults', () => { - it('should render with default className', () => { - expect(mountWrapper.children().hasClass(`${prefix}--form-item`)).toEqual( - true - ); - }); - - it('should render with given className', () => { - expect(mountWrapper.hasClass('extra-class')).toEqual(true); - }); - - it('renders with FileUploaderButton with disableLabelChanges set to true', () => { - expect( - mountWrapper.find('FileUploaderButton').props().disableLabelChanges - ).toEqual(true); - }); - it('renders input with hidden prop', () => { - expect(mountWrapper.find('input').props().className).toEqual( - `${prefix}--visually-hidden` - ); - }); - it(`renders with empty div.${prefix}--file-container by default`, () => { - expect(mountWrapper.find(`div.${prefix}--file-container`).text()).toEqual( - '' - ); - }); - it('clears all uploaded files when the clearFiles method is called', () => { - const mountUploadedWrapper = mount(fileUploader); - mountUploadedWrapper.setState({ - filenames: ['examplefile.jpg'], - filenameStatus: 'complete', - }); - - // Test to make sure that the Filename is rendered - expect(mountUploadedWrapper.find(Filename)).toHaveLength(1); - - // Test to make sure it was properly removed - mountUploadedWrapper.instance().clearFiles(); - expect(mountUploadedWrapper.update().find(Filename)).toHaveLength(0); - }); - }); - - describe('Update filenameStatus', () => { - it('should have equal state and props', () => { - expect( - shallow().state() - .filenameStatus - ).toEqual('uploading'); - }); - - it('should change the label text upon change in props', () => { - mountWrapper.setProps({ filenameStatus: 'uploading' }); - mountWrapper.setState({ filenameStatus: 'uploading' }); - mountWrapper.setProps({ filenameStatus: 'edit' }); - expect(mountWrapper.state().filenameStatus).toEqual('edit'); - }); - - it('should avoid change the label text upon setting props, unless there the value actually changes', () => { - mountWrapper.setProps({ filenameStatus: 'uploading' }); - mountWrapper.setState({ filenameStatus: 'edit' }); - mountWrapper.setProps({ filenameStatus: 'uploading' }); - expect(mountWrapper.state().filenameStatus).toEqual('edit'); - }); - }); -}); - -describe('FileUploaderDropContainer', () => { - let onAddFiles; - let dropContainer; - let mountWrapper; - - beforeEach(() => { - onAddFiles = jest.fn(); - dropContainer = ( - - ); - mountWrapper = mount(dropContainer); - }); - - describe('Renders as expected with default props', () => { - it('renders with given className', () => { - expect(mountWrapper.hasClass('extra-class')).toBe(true); - }); - - it('renders with default labelText prop', () => { - expect(mountWrapper.props().labelText).toEqual('Add file'); - }); - - it('renders with default multiple prop', () => { - expect(mountWrapper.props().multiple).toEqual(false); - }); - - it('renders with default accept prop', () => { - expect(mountWrapper.props().accept).toEqual([]); - }); - - it('disables file upload input', () => { - const wrapper = shallow(dropContainer); - wrapper.setProps({ disabled: true }); - expect(wrapper.find('input').prop('disabled')).toEqual(true); - }); - - it('does not have default role', () => { - expect(mountWrapper.props().role).not.toBeTruthy(); - }); - - it('resets the input value onClick', () => { - const input = mountWrapper.find(`.${prefix}--file-input`); - input.instance().value = ''; - const evt = { target: { value: input.instance().value } }; - input.simulate('click', evt); - - expect(evt.target.value).toEqual(null); - }); - - it('should call `onAddFiles` when a file is selected', () => { - const fileFoo = new File(['foo'], 'foo.txt', { type: 'text/plain' }); - const fileBar = new File(['bar'], 'bar.txt', { type: 'text/plain' }); - const mockFiles = [fileFoo, fileBar]; - const input = mountWrapper.find(`.${prefix}--file-input`); - const evt = { target: { files: mockFiles } }; - input.simulate('change', evt); - expect(onAddFiles).toHaveBeenCalledTimes(1); - expect(onAddFiles).toHaveBeenCalledWith( - expect.objectContaining({ - target: { - files: [fileFoo, fileBar], - }, - }), - { addedFiles: [fileFoo, fileBar] } - ); - }); - }); - - describe('Unique id props', () => { - it('each FileUploaderDropContainer should have a unique ID', () => { - const mountedDropContainers = mount( -
- - -
- ); - const firstDropContainer = mountedDropContainers - .find(FileUploaderDropContainer) - .at(0); - const lastDropContainer = mountedDropContainers - .find(FileUploaderDropContainer) - .at(1); - const isEqual = firstDropContainer === lastDropContainer; - expect(isEqual).toBe(false); - }); - }); -}); - -describe('FileUploaderSkeleton', () => { - describe('Renders as expected', () => { - const wrapper = shallow(); - - it('Has the expected classes', () => { - expect(wrapper.hasClass(`${prefix}--form-item`)).toEqual(true); - }); - }); -}); diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js new file mode 100644 index 000000000000..cff58a31ef5f --- /dev/null +++ b/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js @@ -0,0 +1,89 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { settings } from 'carbon-components'; +import { mount, shallow } from 'enzyme'; +import React from 'react'; +import FileUploader, { + FileUploaderButton, + Filename, + FileUploaderDropContainer, + FileUploaderItem, + FileUploaderSkeleton, +} from '../'; + +const { prefix } = settings; + +describe('FileUploader', () => { + const fileUploader = ; + const mountWrapper = mount(fileUploader); + + describe('Renders as expected with defaults', () => { + it('should render with default className', () => { + expect(mountWrapper.children().hasClass(`${prefix}--form-item`)).toEqual( + true + ); + }); + + it('should render with given className', () => { + expect(mountWrapper.hasClass('extra-class')).toEqual(true); + }); + + it('renders with FileUploaderButton with disableLabelChanges set to true', () => { + expect( + mountWrapper.find('FileUploaderButton').props().disableLabelChanges + ).toEqual(true); + }); + it('renders input with hidden prop', () => { + expect(mountWrapper.find('input').props().className).toEqual( + `${prefix}--visually-hidden` + ); + }); + it(`renders with empty div.${prefix}--file-container by default`, () => { + expect(mountWrapper.find(`div.${prefix}--file-container`).text()).toEqual( + '' + ); + }); + it('clears all uploaded files when the clearFiles method is called', () => { + const mountUploadedWrapper = mount(fileUploader); + mountUploadedWrapper.setState({ + filenames: ['examplefile.jpg'], + filenameStatus: 'complete', + }); + + // Test to make sure that the Filename is rendered + expect(mountUploadedWrapper.find(Filename)).toHaveLength(1); + + // Test to make sure it was properly removed + mountUploadedWrapper.instance().clearFiles(); + expect(mountUploadedWrapper.update().find(Filename)).toHaveLength(0); + }); + }); + + describe('Update filenameStatus', () => { + it('should have equal state and props', () => { + expect( + shallow().state() + .filenameStatus + ).toEqual('uploading'); + }); + + it('should change the label text upon change in props', () => { + mountWrapper.setProps({ filenameStatus: 'uploading' }); + mountWrapper.setState({ filenameStatus: 'uploading' }); + mountWrapper.setProps({ filenameStatus: 'edit' }); + expect(mountWrapper.state().filenameStatus).toEqual('edit'); + }); + + it('should avoid change the label text upon setting props, unless there the value actually changes', () => { + mountWrapper.setProps({ filenameStatus: 'uploading' }); + mountWrapper.setState({ filenameStatus: 'edit' }); + mountWrapper.setProps({ filenameStatus: 'uploading' }); + expect(mountWrapper.state().filenameStatus).toEqual('edit'); + }); + }); +}); diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js new file mode 100644 index 000000000000..50ebcfc846e9 --- /dev/null +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js @@ -0,0 +1,112 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { settings } from 'carbon-components'; +import { mount, shallow } from 'enzyme'; +import React from 'react'; +import { FileUploaderButton } from '../'; + +const { prefix } = settings; + +describe('FileUploaderButton', () => { + const button = ; + const mountWrapper = mount(button); + + describe('Renders as expected with default props', () => { + it('renders with expected className', () => { + expect(mountWrapper.find('label').hasClass(`${prefix}--btn`)).toBe(true); + }); + + it('renders with given className', () => { + expect(mountWrapper.hasClass('extra-class')).toBe(true); + }); + + it('renders with default labelText prop', () => { + expect(mountWrapper.props().labelText).toEqual('Add file'); + }); + + it('renders with default buttonKind prop', () => { + expect(mountWrapper.props().buttonKind).toEqual('primary'); + }); + + it('renders with expected button className', () => { + expect(mountWrapper.find(`.${prefix}--btn--primary`).exists()).toBe(true); + }); + + it('renders with default multiple prop', () => { + expect(mountWrapper.props().multiple).toEqual(false); + }); + + it('renders with default disableLabelChanges prop', () => { + expect(mountWrapper.props().disableLabelChanges).toEqual(false); + }); + + it('renders with default accept prop', () => { + expect(mountWrapper.props().accept).toEqual([]); + }); + + it('renders with default disabled prop', () => { + expect(mountWrapper.props().disabled).toBe(false); + }); + + it('disables file upload input', () => { + const wrapper = shallow(button); + wrapper.setProps({ disabled: true }); + expect(wrapper.find('input').prop('disabled')).toEqual(true); + }); + + it('does have default role', () => { + expect(mountWrapper.props().role).toBeTruthy(); + }); + + it('resets the input value onClick', () => { + const input = mountWrapper.find(`.${prefix}--visually-hidden`); + input.instance().value = ''; + const evt = { target: { value: input.instance().value } }; + input.simulate('click', evt); + + expect(evt.target.value).toEqual(null); + }); + }); + + describe('Unique id props', () => { + it('each FileUploaderButton should have a unique ID', () => { + const mountedButtons = mount( +
+ + +
+ ); + const firstButton = mountedButtons.find(FileUploaderButton).at(0); + const lastButton = mountedButtons.find(FileUploaderButton).at(1); + const isEqual = firstButton === lastButton; + expect(isEqual).toBe(false); + }); + }); + + describe('Update labelText', () => { + it('should have equal state and props', () => { + expect( + shallow().state().labelText + ).toEqual('foo'); + }); + + it('should change the label text upon change in props', () => { + mountWrapper.setProps({ labelText: 'foo' }); + mountWrapper.setState({ labelText: 'foo' }); + mountWrapper.setProps({ labelText: 'bar' }); + expect(mountWrapper.state().labelText).toEqual('bar'); + }); + + it('should avoid change the label text upon setting props, unless there the value actually changes', () => { + mountWrapper.setProps({ labelText: 'foo' }); + mountWrapper.setState({ labelText: 'bar' }); + mountWrapper.setProps({ labelText: 'foo' }); + expect(mountWrapper.state().labelText).toEqual('bar'); + }); + }); +}); diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderDropContainer-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderDropContainer-test.js new file mode 100644 index 000000000000..addb475246cd --- /dev/null +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderDropContainer-test.js @@ -0,0 +1,104 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { settings } from 'carbon-components'; +import { mount, shallow } from 'enzyme'; +import React from 'react'; +import { FileUploaderDropContainer } from '../'; + +const { prefix } = settings; + +describe('FileUploaderDropContainer', () => { + let onAddFiles; + let dropContainer; + let mountWrapper; + + beforeEach(() => { + onAddFiles = jest.fn(); + dropContainer = ( + + ); + mountWrapper = mount(dropContainer); + }); + + describe('Renders as expected with default props', () => { + it('renders with given className', () => { + expect(mountWrapper.hasClass('extra-class')).toBe(true); + }); + + it('renders with default labelText prop', () => { + expect(mountWrapper.props().labelText).toEqual('Add file'); + }); + + it('renders with default multiple prop', () => { + expect(mountWrapper.props().multiple).toEqual(false); + }); + + it('renders with default accept prop', () => { + expect(mountWrapper.props().accept).toEqual([]); + }); + + it('disables file upload input', () => { + const wrapper = shallow(dropContainer); + wrapper.setProps({ disabled: true }); + expect(wrapper.find('input').prop('disabled')).toEqual(true); + }); + + it('does not have default role', () => { + expect(mountWrapper.props().role).not.toBeTruthy(); + }); + + it('resets the input value onClick', () => { + const input = mountWrapper.find(`.${prefix}--file-input`); + input.instance().value = ''; + const evt = { target: { value: input.instance().value } }; + input.simulate('click', evt); + + expect(evt.target.value).toEqual(null); + }); + + it('should call `onAddFiles` when a file is selected', () => { + const fileFoo = new File(['foo'], 'foo.txt', { type: 'text/plain' }); + const fileBar = new File(['bar'], 'bar.txt', { type: 'text/plain' }); + const mockFiles = [fileFoo, fileBar]; + const input = mountWrapper.find(`.${prefix}--file-input`); + const evt = { target: { files: mockFiles } }; + input.simulate('change', evt); + expect(onAddFiles).toHaveBeenCalledTimes(1); + expect(onAddFiles).toHaveBeenCalledWith( + expect.objectContaining({ + target: { + files: [fileFoo, fileBar], + }, + }), + { addedFiles: [fileFoo, fileBar] } + ); + }); + }); + + describe('Unique id props', () => { + it('each FileUploaderDropContainer should have a unique ID', () => { + const mountedDropContainers = mount( +
+ + +
+ ); + const firstDropContainer = mountedDropContainers + .find(FileUploaderDropContainer) + .at(0); + const lastDropContainer = mountedDropContainers + .find(FileUploaderDropContainer) + .at(1); + const isEqual = firstDropContainer === lastDropContainer; + expect(isEqual).toBe(false); + }); + }); +}); diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js new file mode 100644 index 000000000000..ee93cfecfc78 --- /dev/null +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js @@ -0,0 +1,26 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Close16 } from '@carbon/icons-react'; +import { mount, shallow } from 'enzyme'; +import React from 'react'; +import { FileUploaderItem } from '../'; + +describe('FileUploaderItem', () => { + const mountWrapper = mount( + + ); + + describe('click on edit icon (close--solid)', () => { + it('should have a click event', () => { + const onDelete = jest.fn(); + mountWrapper.setProps({ onDelete, status: 'edit' }); + mountWrapper.find(Close16).simulate('click'); + expect(onDelete).toBeCalled(); + }); + }); +}); diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderSkeleton-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderSkeleton-test.js new file mode 100644 index 000000000000..ca69eee2090e --- /dev/null +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderSkeleton-test.js @@ -0,0 +1,34 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { render, cleanup } from '@carbon/test-utils/react'; +import React from 'react'; +import { FileUploaderSkeleton } from '../'; + +describe('FileUploaderSkeleton', () => { + afterEach(cleanup); + + describe('automated accessibility testing', () => { + it('should have no axe violations', async () => { + const { container } = render(); + await expect(container).toHaveNoAxeViolations(); + }); + + it('should have no DAP violations', async () => { + const { container } = render(); + await expect(container).toHaveNoDAPViolations('FileUploaderSkeleton'); + }); + }); + + it('should accept a custom className prop on the root node', () => { + const className = 'test'; + const { container } = render( + + ); + expect(container.firstChild.classList.contains(className)).toBe(true); + }); +}); diff --git a/packages/react/src/components/FileUploader/__tests__/Filename-test.js b/packages/react/src/components/FileUploader/__tests__/Filename-test.js new file mode 100644 index 000000000000..e4d7e81a26e1 --- /dev/null +++ b/packages/react/src/components/FileUploader/__tests__/Filename-test.js @@ -0,0 +1,83 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Close16, CheckmarkFilled16 } from '@carbon/icons-react'; +import { getByText } from '@carbon/test-utils/dom'; +import { render, cleanup } from '@carbon/test-utils/react'; +import { mount, shallow } from 'enzyme'; +import React from 'react'; +import { Simulate } from 'react-dom/test-utils'; +import { Filename } from '../'; +import Loading from '../../Loading'; + +const statuses = ['uploading', 'edit', 'complete']; + +describe('Filename', () => { + afterEach(cleanup); + + describe('automated accessibility tests', () => { + it.each(statuses)( + 'should have no axe violations with status %s', + async status => { + const { container } = render( + + ); + await expect(container).toHaveNoAxeViolations(); + } + ); + + it.each(statuses)( + 'should have no DAP violations with status %s', + async status => { + const { container } = render( + + ); + await expect(container).toHaveNoDAPViolations(`Filename-${status}`); + } + ); + }); + + it('should support events on interactive icons when `edit` or `complete` is the status', () => { + const onClick = jest.fn(); + const { container: edit } = render( + + ); + + Simulate.click(edit.querySelector(`[aria-label="test description"]`)); + expect(onClick).toHaveBeenCalledTimes(1); + + onClick.mockReset(); + + const { container: complete } = render( + + ); + + Simulate.click(complete.querySelector(`[aria-label="test description"]`)); + expect(onClick).toHaveBeenCalledTimes(1); + + const { container: uploading } = render( + + ); + + onClick.mockReset(); + + Simulate.click(getByText(uploading, 'test description')); + expect(onClick).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/react/src/components/FileUploader/__snapshots__/FileUploader-test.js.snap b/packages/react/src/components/FileUploader/__tests__/__snapshots__/Filename-test.js.snap similarity index 100% rename from packages/react/src/components/FileUploader/__snapshots__/FileUploader-test.js.snap rename to packages/react/src/components/FileUploader/__tests__/__snapshots__/Filename-test.js.snap diff --git a/packages/react/src/components/FileUploader/index.js b/packages/react/src/components/FileUploader/index.js index 74611ad9bd09..321ca3fc8dd7 100644 --- a/packages/react/src/components/FileUploader/index.js +++ b/packages/react/src/components/FileUploader/index.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -export * from './FileUploader.Skeleton'; +export { default as FileUploaderSkeleton } from './FileUploader.Skeleton'; export FileUploaderItem from './FileUploaderItem'; export FileUploaderDropContainer from './FileUploaderDropContainer'; export * from './FileUploader'; From 5fb5b7566dc525ee9c4aef8a1a103d83448d4268 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 12 Mar 2020 10:29:12 -0500 Subject: [PATCH 2/8] chore: check-in work --- .../FileUploader/FileUploaderItem.js | 22 +-- .../__tests__/FileUploaderButton-test.js | 35 +++- .../__tests__/FileUploaderItem-test.js | 76 +++++++-- .../__snapshots__/Filename-test.js.snap | 155 ------------------ .../src/components/FileUploader/index.js | 4 +- 5 files changed, 108 insertions(+), 184 deletions(-) delete mode 100644 packages/react/src/components/FileUploader/__tests__/__snapshots__/Filename-test.js.snap diff --git a/packages/react/src/components/FileUploader/FileUploaderItem.js b/packages/react/src/components/FileUploader/FileUploaderItem.js index 099ca41a5717..1be60b1cb5c3 100644 --- a/packages/react/src/components/FileUploader/FileUploaderItem.js +++ b/packages/react/src/components/FileUploader/FileUploaderItem.js @@ -5,17 +5,17 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; -import PropTypes from 'prop-types'; import { settings } from 'carbon-components'; -import classNames from 'classnames'; -import { Filename } from './FileUploader'; +import cx from 'classnames'; +import PropTypes from 'prop-types'; +import React, { useRef } from 'react'; +import { Filename } from './'; import { keys, matches } from '../../internal/keyboard'; import uid from '../../tools/uniqueId'; const { prefix } = settings; -export default function FileUploaderItem({ +function FileUploaderItem({ uuid, name, status, @@ -26,7 +26,8 @@ export default function FileUploaderItem({ errorBody, ...other }) { - const classes = classNames(`${prefix}--file__selected-file`, { + const { current: id } = useRef(uuid || uid()); + const classes = cx(`${prefix}--file__selected-file`, { [`${prefix}--file__selected-file--invalid`]: invalid, }); return ( @@ -40,13 +41,13 @@ export default function FileUploaderItem({ onKeyDown={evt => { if (matches(evt, [keys.Enter, keys.Space])) { if (status === 'edit') { - onDelete(evt, { uuid }); + onDelete(evt, { uuid: id }); } } }} onClick={evt => { if (status === 'edit') { - onDelete(evt, { uuid }); + onDelete(evt, { uuid: id }); } }} /> @@ -71,7 +72,7 @@ FileUploaderItem.propTypes = { /** * Unique identifier for the file object */ - uuid: PropTypes.string.isRequired, + uuid: PropTypes.string, /** * Name of the uploaded file @@ -111,7 +112,8 @@ FileUploaderItem.propTypes = { }; FileUploaderItem.defaultProps = { - uuid: uid(), status: 'uploading', onDelete: () => {}, }; + +export default FileUploaderItem; diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js index 50ebcfc846e9..4d70a081941d 100644 --- a/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js @@ -5,18 +5,27 @@ * LICENSE file in the root directory of this source tree. */ +import { render, cleanup } from '@carbon/test-utils/react'; +import { getByText } from '@carbon/test-utils/dom'; import { settings } from 'carbon-components'; -import { mount, shallow } from 'enzyme'; import React from 'react'; import { FileUploaderButton } from '../'; const { prefix } = settings; describe('FileUploaderButton', () => { - const button = ; - const mountWrapper = mount(button); + afterEach(cleanup); + // const button = ; + // const mountWrapper = mount(button); - describe('Renders as expected with default props', () => { + describe('automated accessibility tests', () => { + it('should have no axe violations', async () => { + const { container } = render(); + await expect(container).toHaveNoAxeViolations(); + }); + }); + + xdescribe('Renders as expected with default props', () => { it('renders with expected className', () => { expect(mountWrapper.find('label').hasClass(`${prefix}--btn`)).toBe(true); }); @@ -73,7 +82,7 @@ describe('FileUploaderButton', () => { }); }); - describe('Unique id props', () => { + xdescribe('Unique id props', () => { it('each FileUploaderButton should have a unique ID', () => { const mountedButtons = mount(
@@ -88,7 +97,7 @@ describe('FileUploaderButton', () => { }); }); - describe('Update labelText', () => { + xdescribe('Update labelText', () => { it('should have equal state and props', () => { expect( shallow().state().labelText @@ -109,4 +118,18 @@ describe('FileUploaderButton', () => { expect(mountWrapper.state().labelText).toEqual('bar'); }); }); + + // labelText is how to find the thing + + // Class name + // Prevent label changes + // id + // name + // role + // tabIndex + // disabled + // listFiles should list files + // support multiple files with multiple + // should call onChange when value changes + // should call onClick when clicked }); diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js index ee93cfecfc78..2bbaba66c3e6 100644 --- a/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js @@ -6,21 +6,73 @@ */ import { Close16 } from '@carbon/icons-react'; -import { mount, shallow } from 'enzyme'; +import { render, cleanup } from '@carbon/test-utils/react'; +import { getByText } from '@carbon/test-utils/dom'; import React from 'react'; +import { Simulate } from 'react-dom/test-utils'; import { FileUploaderItem } from '../'; +import { keys } from '../../../internal/keyboard'; + +const statuses = ['uploading', 'edit', 'complete']; describe('FileUploaderItem', () => { - const mountWrapper = mount( - - ); - - describe('click on edit icon (close--solid)', () => { - it('should have a click event', () => { - const onDelete = jest.fn(); - mountWrapper.setProps({ onDelete, status: 'edit' }); - mountWrapper.find(Close16).simulate('click'); - expect(onDelete).toBeCalled(); - }); + afterEach(cleanup); + + describe('automated accessibility tests', () => { + it.each(statuses)( + 'should have no axe violations with status %s', + async status => { + const { container } = render( + + ); + await expect(container).toHaveNoAxeViolations(); + } + ); + }); + + it('should support calling `onDelete` if the user interacts with the filename during editing', () => { + const onDelete = jest.fn(); + const description = 'test-description'; + const edit = render( + + ); + + let removeFile = getByText(edit.container, description); + Simulate.click(removeFile); + expect(onDelete).toHaveBeenCalledTimes(1); + + Simulate.keyDown(removeFile, keys.Enter); + expect(onDelete).toHaveBeenCalledTimes(2); + + Simulate.keyDown(removeFile, keys.Space); + expect(onDelete).toHaveBeenCalledTimes(3); + + onDelete.mockReset(); + + const uploading = render( + + ); + removeFile = getByText(uploading.container, description); + + Simulate.click(removeFile); + expect(onDelete).not.toHaveBeenCalled(); + + Simulate.keyDown(removeFile, keys.Enter); + expect(onDelete).not.toHaveBeenCalled(); + + Simulate.keyDown(removeFile, keys.Space); + expect(onDelete).not.toHaveBeenCalled(); }); }); diff --git a/packages/react/src/components/FileUploader/__tests__/__snapshots__/Filename-test.js.snap b/packages/react/src/components/FileUploader/__tests__/__snapshots__/Filename-test.js.snap deleted file mode 100644 index 8b0fdeeedd0e..000000000000 --- a/packages/react/src/components/FileUploader/__tests__/__snapshots__/Filename-test.js.snap +++ /dev/null @@ -1,155 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Filename renders as expected renders upload status icon as expected 1`] = ` - - -
- - - - Upload complete - - - - -
-
-
-`; - -exports[`Filename renders as expected renders upload status icon as expected 2`] = ` - - - - - - - Upload complete - - - - - -`; - -exports[`Filename renders as expected renders upload status icon as expected 3`] = ` - - - - - - - - Upload complete - - - - - -`; diff --git a/packages/react/src/components/FileUploader/index.js b/packages/react/src/components/FileUploader/index.js index 24a1583640c9..b912e878ccb2 100644 --- a/packages/react/src/components/FileUploader/index.js +++ b/packages/react/src/components/FileUploader/index.js @@ -5,7 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +import FileUploader, { Filename, FileUploaderButton } from './FileUploader'; export { default as FileUploaderSkeleton } from './FileUploader.Skeleton'; export { default as FileUploaderItem } from './FileUploaderItem'; export { default as FileUploaderDropContainer } from './FileUploaderDropContainer'; -export default from './FileUploader'; +export { Filename, FileUploaderButton }; +export default FileUploader; From f41316ec75732395338173666a9d41667dcfb313 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 13 Mar 2020 13:58:05 -0500 Subject: [PATCH 3/8] test(file-uploader): update test for FileUploaderButton --- .../__tests__/FileUploaderButton-test.js | 231 +++++++++++------- 1 file changed, 139 insertions(+), 92 deletions(-) diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js index 4d70a081941d..cd2c38d21fac 100644 --- a/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js @@ -7,129 +7,176 @@ import { render, cleanup } from '@carbon/test-utils/react'; import { getByText } from '@carbon/test-utils/dom'; -import { settings } from 'carbon-components'; import React from 'react'; +import { Simulate } from 'react-dom/test-utils'; import { FileUploaderButton } from '../'; -const { prefix } = settings; - describe('FileUploaderButton', () => { afterEach(cleanup); - // const button = ; - // const mountWrapper = mount(button); describe('automated accessibility tests', () => { it('should have no axe violations', async () => { - const { container } = render(); + const { container } = render(); await expect(container).toHaveNoAxeViolations(); }); }); - xdescribe('Renders as expected with default props', () => { - it('renders with expected className', () => { - expect(mountWrapper.find('label').hasClass(`${prefix}--btn`)).toBe(true); - }); + it('should support a custom class name on the root element', () => { + const { container } = render(); + expect(container.firstChild.classList.contains('test')).toBe(true); + }); - it('renders with given className', () => { - expect(mountWrapper.hasClass('extra-class')).toBe(true); - }); + it('should call `onClick` if the label is clicked', () => { + const onClick = jest.fn(); + const { container } = render( + + ); + const label = getByText(container, 'test'); + Simulate.click(label); + expect(onClick).toHaveBeenCalledTimes(1); + }); - it('renders with default labelText prop', () => { - expect(mountWrapper.props().labelText).toEqual('Add file'); - }); + it('should call `onChange` if the value of the input changes', () => { + const onChange = jest.fn(); + const { container } = render( + + ); + const input = container.querySelector('input'); + const file = new File(['test'], 'test.png', { type: 'image/png' }); + uploadFiles(input, file); + expect(onChange).toHaveBeenCalledTimes(1); + }); - it('renders with default buttonKind prop', () => { - expect(mountWrapper.props().buttonKind).toEqual('primary'); - }); + it('should not support multiple files by default', () => { + const { container } = render(); + const input = container.querySelector('input'); + expect(input.getAttribute('multiple')).toBeFalsy(); + }); - it('renders with expected button className', () => { - expect(mountWrapper.find(`.${prefix}--btn--primary`).exists()).toBe(true); - }); + it('should have a unique id', () => { + const { container } = render( + <> + + + + ); + const inputs = container.querySelectorAll('input'); + expect(inputs[0].getAttribute('id')).not.toBe(inputs[1].getAttribute('id')); + }); - it('renders with default multiple prop', () => { - expect(mountWrapper.props().multiple).toEqual(false); - }); + it('should reset the input value when the label is clicked', () => { + const { container } = render( + + ); + const input = container.querySelector('input'); - it('renders with default disableLabelChanges prop', () => { - expect(mountWrapper.props().disableLabelChanges).toEqual(false); - }); + const filename = 'test.png'; + const file = new File(['test'], filename, { type: 'image/png' }); + uploadFiles(input, [file]); - it('renders with default accept prop', () => { - expect(mountWrapper.props().accept).toEqual([]); - }); + expect(input.files.length).toBe(1); + Simulate.click(input); + expect(input.files.length).toBe(0); + }); - it('renders with default disabled prop', () => { - expect(mountWrapper.props().disabled).toBe(false); - }); + it('should update the label text if it receives a new value from props', () => { + const container = document.createElement('div'); + render(, { container }); + expect(getByText(container, 'test')).toBeInstanceOf(HTMLElement); - it('disables file upload input', () => { - const wrapper = shallow(button); - wrapper.setProps({ disabled: true }); - expect(wrapper.find('input').prop('disabled')).toEqual(true); - }); + render(, { container }); + expect(getByText(container, 'tester')).toBeInstanceOf(HTMLElement); + }); - it('does have default role', () => { - expect(mountWrapper.props().role).toBeTruthy(); - }); + describe('FileUploaderButton label', () => { + it('should update the label when a file is selected', () => { + const { container } = render( + + ); + const input = container.querySelector('input'); + const label = getByText(container, 'test'); + expect(label).toBeInstanceOf(HTMLElement); - it('resets the input value onClick', () => { - const input = mountWrapper.find(`.${prefix}--visually-hidden`); - input.instance().value = ''; - const evt = { target: { value: input.instance().value } }; - input.simulate('click', evt); + const filename = 'test.png'; + const file = new File(['test'], filename, { type: 'image/png' }); + uploadFiles(input, [file]); - expect(evt.target.value).toEqual(null); + expect(getByText(container, filename)).toBeInstanceOf(HTMLElement); }); - }); - xdescribe('Unique id props', () => { - it('each FileUploaderButton should have a unique ID', () => { - const mountedButtons = mount( -
- - -
+ it('should update the label when multiple files are selected', () => { + const { container } = render( + + ); + const input = container.querySelector('input'); + const label = getByText(container, 'test'); + expect(label).toBeInstanceOf(HTMLElement); + + const files = [ + new File(['test-1'], 'test-1.png', { type: 'image/png' }), + new File(['test-2'], 'test-1.png', { type: 'image/png' }), + new File(['test-3'], 'test-1.png', { type: 'image/png' }), + ]; + + uploadFiles(input, files); + expect(getByText(container, `${files.length} files`)).toBeInstanceOf( + HTMLElement ); - const firstButton = mountedButtons.find(FileUploaderButton).at(0); - const lastButton = mountedButtons.find(FileUploaderButton).at(1); - const isEqual = firstButton === lastButton; - expect(isEqual).toBe(false); }); - }); - xdescribe('Update labelText', () => { - it('should have equal state and props', () => { - expect( - shallow().state().labelText - ).toEqual('foo'); - }); + it('should not update the label when files are selected but `disableLabelChanges` is false', () => { + const { container } = render( + + ); + const input = container.querySelector('input'); + const label = getByText(container, 'test'); + expect(label).toBeInstanceOf(HTMLElement); - it('should change the label text upon change in props', () => { - mountWrapper.setProps({ labelText: 'foo' }); - mountWrapper.setState({ labelText: 'foo' }); - mountWrapper.setProps({ labelText: 'bar' }); - expect(mountWrapper.state().labelText).toEqual('bar'); - }); + const filename = 'test.png'; + const file = new File(['test'], filename, { type: 'image/png' }); + uploadFiles(input, [file]); - it('should avoid change the label text upon setting props, unless there the value actually changes', () => { - mountWrapper.setProps({ labelText: 'foo' }); - mountWrapper.setState({ labelText: 'bar' }); - mountWrapper.setProps({ labelText: 'foo' }); - expect(mountWrapper.state().labelText).toEqual('bar'); + expect(getByText(container, 'test')).toBeInstanceOf(HTMLElement); }); }); - - // labelText is how to find the thing - - // Class name - // Prevent label changes - // id - // name - // role - // tabIndex - // disabled - // listFiles should list files - // support multiple files with multiple - // should call onChange when value changes - // should call onClick when clicked }); + +/** + * A helper with standardizing behavior around selecting and clearing files with + * an input with type="file". + * + * Based on comments on this discussion over in react-testing-library: + * https://github.com/testing-library/react-testing-library/issues/93#issuecomment-392126991 + * + * @param {HTMLInputElement} input + * @param {Array} [files] + */ +function uploadFiles(input, files = []) { + // Define the 'files' property on the input with the given files + Object.defineProperty(input, 'files', { + writable: true, + value: files, + }); + + // When we update the value of the empty, if it is falsy we clear the input + // files to mirror browser behavior + Object.defineProperty(input, 'value', { + set(newValue) { + if (!newValue) { + input.files.length = 0; + } + return newValue; + }, + }); + + // Simulate the change event with the given options + Simulate.change(input, { + target: { + files, + }, + }); +} From b25574960dcff2b769059ff7e45cf6fefd897584 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 13 Mar 2020 14:27:44 -0500 Subject: [PATCH 4/8] test(file-uploader): update tests for FileUploaderDropContainer --- .../__tests__/FileUploaderButton-test.js | 37 +---- .../FileUploaderDropContainer-test.js | 143 ++++++++---------- .../components/FileUploader/test-helpers.js | 44 ++++++ 3 files changed, 107 insertions(+), 117 deletions(-) create mode 100644 packages/react/src/components/FileUploader/test-helpers.js diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js index cd2c38d21fac..5f0519340cc9 100644 --- a/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderButton-test.js @@ -10,6 +10,7 @@ import { getByText } from '@carbon/test-utils/dom'; import React from 'react'; import { Simulate } from 'react-dom/test-utils'; import { FileUploaderButton } from '../'; +import { uploadFiles } from '../test-helpers'; describe('FileUploaderButton', () => { afterEach(cleanup); @@ -144,39 +145,3 @@ describe('FileUploaderButton', () => { }); }); }); - -/** - * A helper with standardizing behavior around selecting and clearing files with - * an input with type="file". - * - * Based on comments on this discussion over in react-testing-library: - * https://github.com/testing-library/react-testing-library/issues/93#issuecomment-392126991 - * - * @param {HTMLInputElement} input - * @param {Array} [files] - */ -function uploadFiles(input, files = []) { - // Define the 'files' property on the input with the given files - Object.defineProperty(input, 'files', { - writable: true, - value: files, - }); - - // When we update the value of the empty, if it is falsy we clear the input - // files to mirror browser behavior - Object.defineProperty(input, 'value', { - set(newValue) { - if (!newValue) { - input.files.length = 0; - } - return newValue; - }, - }); - - // Simulate the change event with the given options - Simulate.change(input, { - target: { - files, - }, - }); -} diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderDropContainer-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderDropContainer-test.js index addb475246cd..caccd59ed9b7 100644 --- a/packages/react/src/components/FileUploader/__tests__/FileUploaderDropContainer-test.js +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderDropContainer-test.js @@ -5,100 +5,81 @@ * LICENSE file in the root directory of this source tree. */ -import { settings } from 'carbon-components'; -import { mount, shallow } from 'enzyme'; +import { getByText } from '@carbon/test-utils/dom'; +import { render, cleanup } from '@carbon/test-utils/react'; import React from 'react'; +import { Simulate } from 'react-dom/test-utils'; import { FileUploaderDropContainer } from '../'; - -const { prefix } = settings; +import { uploadFiles } from '../test-helpers'; describe('FileUploaderDropContainer', () => { - let onAddFiles; - let dropContainer; - let mountWrapper; + afterEach(cleanup); - beforeEach(() => { - onAddFiles = jest.fn(); - dropContainer = ( - + it('should support a custom class name on the drop area', () => { + const { container } = render( + ); - mountWrapper = mount(dropContainer); + const dropArea = container.querySelector('[role="button"]'); + expect(dropArea.classList.contains('test')).toBe(true); }); - describe('Renders as expected with default props', () => { - it('renders with given className', () => { - expect(mountWrapper.hasClass('extra-class')).toBe(true); - }); - - it('renders with default labelText prop', () => { - expect(mountWrapper.props().labelText).toEqual('Add file'); - }); - - it('renders with default multiple prop', () => { - expect(mountWrapper.props().multiple).toEqual(false); - }); - - it('renders with default accept prop', () => { - expect(mountWrapper.props().accept).toEqual([]); - }); - - it('disables file upload input', () => { - const wrapper = shallow(dropContainer); - wrapper.setProps({ disabled: true }); - expect(wrapper.find('input').prop('disabled')).toEqual(true); - }); + it('should have a unique id each time it is used', () => { + const { container } = render( + <> + + + + ); + const inputs = container.querySelectorAll('input'); + expect(inputs[0].getAttribute('id')).not.toBe(inputs[1].getAttribute('id')); + }); - it('does not have default role', () => { - expect(mountWrapper.props().role).not.toBeTruthy(); - }); + it('should render with the default labelText prop', () => { + const { container } = render(); + const label = getByText(container, 'Add file'); + expect(label).toBeInstanceOf(HTMLElement); + }); - it('resets the input value onClick', () => { - const input = mountWrapper.find(`.${prefix}--file-input`); - input.instance().value = ''; - const evt = { target: { value: input.instance().value } }; - input.simulate('click', evt); + it('should render with multiple set to false by default', () => { + const { container } = render(); + const input = container.querySelector('input'); + expect(input.getAttribute('multiple')).toBeFalsy(); + }); - expect(evt.target.value).toEqual(null); - }); + it('should reset the value of the input when the drop area is clicked', () => { + const { container } = render( + + ); + const input = container.querySelector('input'); - it('should call `onAddFiles` when a file is selected', () => { - const fileFoo = new File(['foo'], 'foo.txt', { type: 'text/plain' }); - const fileBar = new File(['bar'], 'bar.txt', { type: 'text/plain' }); - const mockFiles = [fileFoo, fileBar]; - const input = mountWrapper.find(`.${prefix}--file-input`); - const evt = { target: { files: mockFiles } }; - input.simulate('change', evt); - expect(onAddFiles).toHaveBeenCalledTimes(1); - expect(onAddFiles).toHaveBeenCalledWith( - expect.objectContaining({ - target: { - files: [fileFoo, fileBar], - }, - }), - { addedFiles: [fileFoo, fileBar] } - ); - }); + uploadFiles(input, [ + new File(['content'], 'test.png', { type: 'image/png' }), + ]); + expect(input.files.length).toBe(1); + Simulate.click(input); + expect(input.files.length).toBe(0); }); - describe('Unique id props', () => { - it('each FileUploaderDropContainer should have a unique ID', () => { - const mountedDropContainers = mount( -
- - -
- ); - const firstDropContainer = mountedDropContainers - .find(FileUploaderDropContainer) - .at(0); - const lastDropContainer = mountedDropContainers - .find(FileUploaderDropContainer) - .at(1); - const isEqual = firstDropContainer === lastDropContainer; - expect(isEqual).toBe(false); - }); + it('should call `onAddFiles` when a file is selected', () => { + const onAddFiles = jest.fn(); + const { container } = render( + + ); + const input = container.querySelector('input'); + const files = [ + new File(['foo'], 'foo.txt', { type: 'text/plain' }), + new File(['bar'], 'bar.txt', { type: 'text/plain' }), + ]; + + uploadFiles(input, files); + expect(onAddFiles).toHaveBeenCalledTimes(1); + expect(onAddFiles).toHaveBeenCalledWith( + expect.objectContaining({ + target: { + files, + }, + }), + { addedFiles: files } + ); }); }); diff --git a/packages/react/src/components/FileUploader/test-helpers.js b/packages/react/src/components/FileUploader/test-helpers.js new file mode 100644 index 000000000000..430ea55db7fd --- /dev/null +++ b/packages/react/src/components/FileUploader/test-helpers.js @@ -0,0 +1,44 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { Simulate } from 'react-dom/test-utils'; + +/** + * A helper with standardizing behavior around selecting and clearing files with + * an input with type="file". + * + * Based on comments on this discussion over in react-testing-library: + * https://github.com/testing-library/react-testing-library/issues/93#issuecomment-392126991 + * + * @param {HTMLInputElement} input + * @param {Array} [files] + */ +export function uploadFiles(input, files = []) { + // Define the 'files' property on the input with the given files + Object.defineProperty(input, 'files', { + writable: true, + value: files, + }); + + // When we update the value of the empty, if it is falsy we clear the input + // files to mirror browser behavior + Object.defineProperty(input, 'value', { + set(newValue) { + if (!newValue) { + input.files.length = 0; + } + return newValue; + }, + }); + + // Simulate the change event with the given options + Simulate.change(input, { + target: { + files, + }, + }); +} From 0c549dac0c84871f595ba8c47f840a3abd4171c9 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 13 Mar 2020 15:45:12 -0500 Subject: [PATCH 5/8] test(file-uploader): update tests for FileUploader --- .../__snapshots__/PublicAPI-test.js.snap | 2 - .../__tests__/FileUploader-test.js | 117 ++++++++---------- 2 files changed, 49 insertions(+), 70 deletions(-) diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index 21b7a4fc60b6..6689a48e827d 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -2475,7 +2475,6 @@ Map { "defaultProps": Object { "onDelete": [Function], "status": "uploading", - "uuid": "id1", }, "propTypes": Object { "errorBody": Object { @@ -2507,7 +2506,6 @@ Map { "type": "oneOf", }, "uuid": Object { - "isRequired": true, "type": "string", }, }, diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js index cff58a31ef5f..2e9f9b51802f 100644 --- a/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js +++ b/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js @@ -5,85 +5,66 @@ * LICENSE file in the root directory of this source tree. */ -import { settings } from 'carbon-components'; -import { mount, shallow } from 'enzyme'; +import { getByText } from '@carbon/test-utils/dom'; +import { render, cleanup } from '@carbon/test-utils/react'; import React from 'react'; -import FileUploader, { - FileUploaderButton, - Filename, - FileUploaderDropContainer, - FileUploaderItem, - FileUploaderSkeleton, -} from '../'; - -const { prefix } = settings; +import FileUploader from '../'; +import { uploadFiles } from '../test-helpers'; describe('FileUploader', () => { - const fileUploader = ; - const mountWrapper = mount(fileUploader); + afterEach(cleanup); + + it('should support a custom class name on the root element', () => { + const { container } = render(); + expect(container.firstChild.classList.contains('test')).toBe(true); + }); - describe('Renders as expected with defaults', () => { - it('should render with default className', () => { - expect(mountWrapper.children().hasClass(`${prefix}--form-item`)).toEqual( - true - ); - }); + it('should not update the label by default when selecting files', () => { + const { container } = render(); + const input = container.querySelector('input'); + const label = getByText(container, 'upload'); - it('should render with given className', () => { - expect(mountWrapper.hasClass('extra-class')).toEqual(true); - }); + expect(label).toBeInstanceOf(HTMLElement); + uploadFiles(input, [new File(['test'], 'test.png', { type: 'image/png' })]); + expect(getByText(container, 'upload')).toBeInstanceOf(HTMLElement); + }); - it('renders with FileUploaderButton with disableLabelChanges set to true', () => { - expect( - mountWrapper.find('FileUploaderButton').props().disableLabelChanges - ).toEqual(true); - }); - it('renders input with hidden prop', () => { - expect(mountWrapper.find('input').props().className).toEqual( - `${prefix}--visually-hidden` - ); - }); - it(`renders with empty div.${prefix}--file-container by default`, () => { - expect(mountWrapper.find(`div.${prefix}--file-container`).text()).toEqual( - '' - ); - }); - it('clears all uploaded files when the clearFiles method is called', () => { - const mountUploadedWrapper = mount(fileUploader); - mountUploadedWrapper.setState({ - filenames: ['examplefile.jpg'], - filenameStatus: 'complete', - }); + it('should clear all uploaded files when `clearFiles` is called on a ref', () => { + const ref = React.createRef(); + const { container } = render(); + const input = container.querySelector('input'); - // Test to make sure that the Filename is rendered - expect(mountUploadedWrapper.find(Filename)).toHaveLength(1); + const filename = 'test.png'; + uploadFiles(input, [new File(['test'], filename, { type: 'image/png' })]); - // Test to make sure it was properly removed - mountUploadedWrapper.instance().clearFiles(); - expect(mountUploadedWrapper.update().find(Filename)).toHaveLength(0); - }); + expect(getByText(container, filename)).toBeInstanceOf(HTMLElement); + ref.current.clearFiles(); + expect(getByText(container, filename)).not.toBeInstanceOf(HTMLElement); }); - describe('Update filenameStatus', () => { - it('should have equal state and props', () => { - expect( - shallow().state() - .filenameStatus - ).toEqual('uploading'); - }); + it('should synchronize the filename status state when its prop changes', () => { + const container = document.createElement('div'); + const description = 'test'; + render( + , + { + container, + } + ); + + const input = container.querySelector('input'); + uploadFiles(input, [new File(['test'], 'test.png', { type: 'image/png' })]); + + const edit = getByText(container, description); - it('should change the label text upon change in props', () => { - mountWrapper.setProps({ filenameStatus: 'uploading' }); - mountWrapper.setState({ filenameStatus: 'uploading' }); - mountWrapper.setProps({ filenameStatus: 'edit' }); - expect(mountWrapper.state().filenameStatus).toEqual('edit'); - }); + render( + , + { + container, + } + ); - it('should avoid change the label text upon setting props, unless there the value actually changes', () => { - mountWrapper.setProps({ filenameStatus: 'uploading' }); - mountWrapper.setState({ filenameStatus: 'edit' }); - mountWrapper.setProps({ filenameStatus: 'uploading' }); - expect(mountWrapper.state().filenameStatus).toEqual('edit'); - }); + const complete = getByText(container, description); + expect(edit.parentNode).not.toEqual(complete.parentNode); }); }); From b89c38ff58a4646ea17002bd5a96368f3bbeb277 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Fri, 13 Mar 2020 15:53:31 -0500 Subject: [PATCH 6/8] test(file-uploader): add tests for axe and DAP --- .../FileUploader/__tests__/FileUploader-test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js index 2e9f9b51802f..131f71513391 100644 --- a/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js +++ b/packages/react/src/components/FileUploader/__tests__/FileUploader-test.js @@ -14,6 +14,18 @@ import { uploadFiles } from '../test-helpers'; describe('FileUploader', () => { afterEach(cleanup); + describe('automated accessibility tests', () => { + it.skip('should have no axe violations', async () => { + const { container } = render(); + await expect(container).toHaveNoAxeViolations(); + }); + + it.skip('should have no DAP violations', async () => { + const { container } = render(); + await expect(container).toHaveNoDAPViolations('FileUploader'); + }); + }); + it('should support a custom class name on the root element', () => { const { container } = render(); expect(container.firstChild.classList.contains('test')).toBe(true); From 5b7faf468b52b3de3c585f7b54c0176b6f3c4eca Mon Sep 17 00:00:00 2001 From: Josh Black Date: Sun, 15 Mar 2020 18:55:50 -0500 Subject: [PATCH 7/8] chore(file-uploader): remove eslint violations --- .../components/FileUploader/__tests__/FileUploaderItem-test.js | 1 - .../src/components/FileUploader/__tests__/Filename-test.js | 3 --- 2 files changed, 4 deletions(-) diff --git a/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js b/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js index 2bbaba66c3e6..ba8f2a116c22 100644 --- a/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js +++ b/packages/react/src/components/FileUploader/__tests__/FileUploaderItem-test.js @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import { Close16 } from '@carbon/icons-react'; import { render, cleanup } from '@carbon/test-utils/react'; import { getByText } from '@carbon/test-utils/dom'; import React from 'react'; diff --git a/packages/react/src/components/FileUploader/__tests__/Filename-test.js b/packages/react/src/components/FileUploader/__tests__/Filename-test.js index e4d7e81a26e1..14db70c2f5bd 100644 --- a/packages/react/src/components/FileUploader/__tests__/Filename-test.js +++ b/packages/react/src/components/FileUploader/__tests__/Filename-test.js @@ -5,14 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import { Close16, CheckmarkFilled16 } from '@carbon/icons-react'; import { getByText } from '@carbon/test-utils/dom'; import { render, cleanup } from '@carbon/test-utils/react'; -import { mount, shallow } from 'enzyme'; import React from 'react'; import { Simulate } from 'react-dom/test-utils'; import { Filename } from '../'; -import Loading from '../../Loading'; const statuses = ['uploading', 'edit', 'complete']; From f9613e2212227a585665a9e66909c98234905ffc Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 16 Mar 2020 09:49:24 -0500 Subject: [PATCH 8/8] chore(file-uploader): update snapshots --- .../__snapshots__/FileUploader-test.js.snap | 155 ------------------ 1 file changed, 155 deletions(-) delete mode 100644 packages/react/src/components/FileUploader/__snapshots__/FileUploader-test.js.snap diff --git a/packages/react/src/components/FileUploader/__snapshots__/FileUploader-test.js.snap b/packages/react/src/components/FileUploader/__snapshots__/FileUploader-test.js.snap deleted file mode 100644 index 1eb5d68d8aaf..000000000000 --- a/packages/react/src/components/FileUploader/__snapshots__/FileUploader-test.js.snap +++ /dev/null @@ -1,155 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Filename renders as expected renders upload status icon as expected 1`] = ` - - -
- - - - Upload complete - - - - -
-
-
-`; - -exports[`Filename renders as expected renders upload status icon as expected 2`] = ` - - - - - - - Upload complete - - - - - -`; - -exports[`Filename renders as expected renders upload status icon as expected 3`] = ` - - - - - - - - Upload complete - - - - - -`;