diff --git a/components/shared/__tests__/__snapshots__/backdrop.test.tsx.snap b/components/shared/__tests__/__snapshots__/backdrop.test.tsx.snap new file mode 100644 index 0000000..8b4b402 --- /dev/null +++ b/components/shared/__tests__/__snapshots__/backdrop.test.tsx.snap @@ -0,0 +1,148 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Backdrop content should be offset 1`] = ` +"
test-value
" +`; + +exports[`Backdrop content should be offset 2`] = ` +"
test-value
" +`; + +exports[`Backdrop should render correctly 1`] = ` +"
test-value
" +`; diff --git a/components/shared/__tests__/__snapshots__/dropdown.test.tsx.snap b/components/shared/__tests__/__snapshots__/dropdown.test.tsx.snap new file mode 100644 index 0000000..7cc6df2 --- /dev/null +++ b/components/shared/__tests__/__snapshots__/dropdown.test.tsx.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Dropdown should render correctly 1`] = `"
"`; diff --git a/components/shared/__tests__/__snapshots__/transition.test.tsx.snap b/components/shared/__tests__/__snapshots__/transition.test.tsx.snap new file mode 100644 index 0000000..555abd2 --- /dev/null +++ b/components/shared/__tests__/__snapshots__/transition.test.tsx.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CSSTransition should render correctly 1`] = `"test"`; diff --git a/components/shared/__tests__/backdrop.test.tsx b/components/shared/__tests__/backdrop.test.tsx new file mode 100644 index 0000000..db95fcd --- /dev/null +++ b/components/shared/__tests__/backdrop.test.tsx @@ -0,0 +1,65 @@ +import React from 'react' +import { mount } from 'enzyme' +import Backdrop from '../backdrop' +import { nativeEvent, updateWrapper } from 'tests/utils' + +describe('Backdrop', () => { + it('should render correctly', () => { + const wrapper = mount( + test-value + ) + expect(wrapper.html()).toMatchSnapshot() + expect(() => wrapper.unmount()).not.toThrow() + }) + + it('should be shown or hidden by prop', async () => { + const wrapper = mount( + test-value + ) + expect(wrapper.find('.backdrop').length).toBe(0) + wrapper.setProps({ visible: true }) + await updateWrapper(wrapper, 350) + expect(wrapper.find('.backdrop').length).not.toBe(0) + }) + + it('background click events should be captured', () => { + const handler = jest.fn() + const wrapper = mount( + test-value + ) + wrapper.find('.backdrop').simulate('click', nativeEvent) + expect(handler).toHaveBeenCalled() + handler.mockRestore() + }) + + it('should be no error when handler missing', () => { + const wrapper = mount( + test-value + ) + wrapper.find('.backdrop').simulate('click', nativeEvent) + expect(() => wrapper.unmount()).not.toThrow() + }) + + it('should be prevent event from the container', () => { + const handler = jest.fn() + const wrapper = mount( + test-value + ) + wrapper.find('.content').simulate('click', nativeEvent) + wrapper.find('.offset').simulate('click', nativeEvent) + expect(handler).not.toHaveBeenCalled() + handler.mockRestore() + }) + + it('content should be offset', () => { + const wrapper = mount( + test-value + ) + const notOffset = wrapper.html() + expect(wrapper.html()).toMatchSnapshot() + + wrapper.setProps({ offsetY: '100' }) + expect(wrapper.html()).toMatchSnapshot() + expect(notOffset).not.toEqual(wrapper.html()) + }) +}) diff --git a/components/shared/__tests__/dropdown.test.tsx b/components/shared/__tests__/dropdown.test.tsx new file mode 100644 index 0000000..1e3159a --- /dev/null +++ b/components/shared/__tests__/dropdown.test.tsx @@ -0,0 +1,165 @@ +import React, { useRef } from 'react' +import { mount } from 'enzyme' +import Dropdown from '../dropdown' +import { nativeEvent, updateWrapper } from 'tests/utils' +import { act } from 'react-dom/test-utils' + +const simulateGlobalClick = () => { + document.body.dispatchEvent( + new MouseEvent('click', { + view: window, + bubbles: true, + cancelable: true, + }), + ) +} + +describe('Dropdown', () => { + beforeAll(() => { + window.Element.prototype.getBoundingClientRect = () => ({ + width: 100, + left: 0, + right: 100, + top: 0, + bottom: 100, + height: 100, + x: 0, + } as DOMRect) + }) + + it('should render correctly', async () => { + const Mock: React.FC<{ visible?: boolean }> = ({ visible = false }) => { + const ref = useRef(null) + return ( +
+ + test-value + +
+ ) + } + const wrapper = mount() + wrapper.setProps({ visible: true }) + await updateWrapper(wrapper, 300) + + expect(wrapper.find('.dropdown').html()).toContain('test-value') + expect(wrapper.html()).toMatchSnapshot() + expect(() => wrapper.unmount()).not.toThrow() + }) + + it('should be work without parent', () => { + const wrapper = mount( + + test-value + + ) + + expect(() => wrapper.unmount()).not.toThrow() + }) + + it('events should be prevented', () => { + const handler = jest.fn() + const Mock: React.FC<{}> = () => { + const ref = useRef(null) + return ( +
+ + test-value + +
+ ) + } + const wrapper = mount() + wrapper.find('.dropdown').simulate('click', nativeEvent) + + expect(handler).not.toHaveBeenCalled() + expect(() => wrapper.unmount()).not.toThrow() + handler.mockRestore() + }) + + it('should trigger rect update', async () => { + let dynamicTopMock = 100, calledTimes = 0 + window.Element.prototype.getBoundingClientRect = () => { + calledTimes ++ + return { + width: 100, + left: 0, + right: 100, + top: 0, + bottom: dynamicTopMock, + height: 100, + x: 0, + } as DOMRect + } + const Mock: React.FC<{}> = () => { + const ref = useRef(null) + return ( +
+ + test-value + +
+ ) + } + const wrapper = mount() + expect(calledTimes).toBe(1) + + // Do not render if position is not updated + act(() => simulateGlobalClick()) + expect(calledTimes).toBe(2) + await updateWrapper(wrapper, 50) + + // Trigger position diff first, then trigger the update + // Get Rect twice total + act(() => { + dynamicTopMock++ + simulateGlobalClick() + }) + expect(calledTimes).toBeGreaterThanOrEqual(4) + + act(() => { + dynamicTopMock++ + window.dispatchEvent(new Event('resize')) + }) + expect(calledTimes).toBeGreaterThanOrEqual(5) + + expect(() => wrapper.unmount()).not.toThrow() + }) + + it('should tigger rect update when mouseenter', () => { + let calledTimes = 0 + window.Element.prototype.getBoundingClientRect = () => { + calledTimes ++ + return { + width: 100, + left: 0, + right: 100, + top: 0, + bottom: 100, + height: 100, + x: 0, + } as DOMRect + } + const Mock: React.FC<{}> = () => { + const ref = useRef(null) + return ( +
+ + test-value + +
+ ) + } + const wrapper = mount() + expect(calledTimes).toBe(1) + + // MouseEnter event is monitored by native API, the simulate can not trigger it. + const parent = wrapper.find('#parent').getDOMNode() as HTMLDivElement + act(() => { + parent.dispatchEvent(new Event('mouseenter')) + }) + expect(calledTimes).toBe(2) + + expect(() => wrapper.unmount()).not.toThrow() + }) +}) diff --git a/components/shared/__tests__/transition.test.tsx b/components/shared/__tests__/transition.test.tsx new file mode 100644 index 0000000..d872ac7 --- /dev/null +++ b/components/shared/__tests__/transition.test.tsx @@ -0,0 +1,67 @@ +import React from 'react' +import { mount } from 'enzyme' +import CSSTransition from '../css-transition' +import { updateWrapper } from 'tests/utils' + +describe('CSSTransition', () => { + it('should render correctly', () => { + const wrapper = mount(test) + expect(wrapper.text()).toContain('test') + expect(wrapper.html()).toMatchSnapshot() + expect(() => wrapper.unmount()).not.toThrow() + }) + + it('should work correctly with time props', async () => { + const wrapper = mount( + + test + + ) + expect(wrapper.find('.transition-enter-active').length).toBe(0) + + wrapper.setProps({ visible: true }) + await updateWrapper(wrapper, 310) + expect(wrapper.find('.transition-enter-active').length).not.toBe(0) + + wrapper.setProps({ visible: false }) + await updateWrapper(wrapper, 310) + expect(wrapper.find('.transition-leave-active').length).not.toBe(0) + }) + + it('should clear css-transition classes after hidden', async () => { + const wrapper = mount(test) + // don't remove classes after shown + await updateWrapper(wrapper, 60) + expect(wrapper.find('.transition-enter-active').length).not.toBe(0) + + await updateWrapper(wrapper, 150) + expect(wrapper.find('.transition-enter-active').length).not.toBe(0) + + // remove classes after hidden + wrapper.setProps({ visible: false }) + await updateWrapper(wrapper, 60) + expect(wrapper.find('.transition-leave-active').length).not.toBe(0) + + await updateWrapper(wrapper, 150) + expect(wrapper.find('.transition-leave-active').length).toBe(0) + expect(wrapper.find('.transition-enter-active').length).toBe(0) + }) + + it('custom class names should be rendered', async () => { + const wrapper = mount( + + test + + ) + + expect(wrapper.find('.test-enter-active').length).toBe(0) + + wrapper.setProps({ visible: true }) + await updateWrapper(wrapper, 60) + expect(wrapper.find('.test-enter-active').length).not.toBe(0) + + wrapper.setProps({ visible: false }) + await updateWrapper(wrapper, 60) + expect(wrapper.find('.test-leave-active').length).not.toBe(0) + }) +}) diff --git a/components/shared/dropdown.tsx b/components/shared/dropdown.tsx index 2b4b080..5af00b7 100644 --- a/components/shared/dropdown.tsx +++ b/components/shared/dropdown.tsx @@ -57,6 +57,7 @@ const Dropdown: React.FC> = React.memo(({ useEffect(() => { if (!parent || !parent.current) return parent.current.addEventListener('mouseenter', updateRect) + /* istanbul ignore next */ return () => { if (!parent || !parent.current) return parent.current.removeEventListener('mouseenter', updateRect)