bufferapp/ui

View on GitHub
src/components/Select/Select.test.tsx

Summary

Maintainability
F
3 days
Test Coverage
/* eslint-disable react/jsx-filename-extension */
import React from 'react'
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'enzy... Remove this comment to see the full error message
import Enzyme, { shallow, mount } from 'enzyme'
import Adapter from '@wojtekmaj/enzyme-adapter-react-17'
import Select from './Select'

Enzyme.configure({ adapter: new Adapter() })

describe('Select component', () => {
  it('openPopup: should open the closed popup', () => {
    const wrapper = mount(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={() => true} items={[]} label="Select" />,
    )
    const instance = wrapper.instance()
    expect(wrapper.state().isOpen).toBe(false)
    instance.onButtonClick()
    expect(wrapper.state().isOpen).toBe(true)
  })

  it('closePopup: should close the open popup', () => {
    const wrapper = mount(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={() => true} items={[]} label="Select" />,
    )
    const instance = wrapper.instance()
    instance.onButtonClick()
    expect(wrapper.state().isOpen).toBe(true)
    instance.closePopover({ target: null })
    expect(wrapper.state().isOpen).toBe(false)
  })

  it('onClose: should close the open popup', () => {
    const wrapper = mount(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={() => true} items={[]} label="Select" />,
    )
    const instance = wrapper.instance()
    instance.onButtonClick()
    expect(wrapper.state().isOpen).toBe(true)
    instance.onClose()
    expect(wrapper.state().isOpen).toBe(false)
  })

  it('componentDidUpdate: run onButtonClick when isOpen prop changes', () => {
    const wrapper = shallow(
      <Select
        // @ts-expect-error TS(2769) FIXME: No overload matches this call.
        onSelectClick={() => true}
        isOpen={false}
        items={[]}
        label="Select"
      />,
    )
    const instance = wrapper.instance()

    const onButtonClickMock = jest.spyOn(instance, 'onButtonClick')
    expect(onButtonClickMock).toHaveBeenCalledTimes(0)

    wrapper.setProps({ isOpen: true })
    expect(onButtonClickMock).toHaveBeenCalledTimes(1)
  })

  it('componentDidUpdate: should run filterOnMenuOpen on open if clearSearchOnBlur is false and search value exists in state', () => {
    const wrapper = mount(
      <Select
        // @ts-expect-error TS(2769) FIXME: No overload matches this call.
        onSelectClick={() => true}
        items={[]}
        label="Select"
        clearSearchOnBlur={false}
      />,
    )
    const instance = wrapper.instance()
    wrapper.setState({ isOpen: false, searchValue: 'Test search value' })

    const onSearchChangeMock = jest.spyOn(instance, 'onSearchChange')
    const filterOnMenuOpenMock = jest.spyOn(instance, 'filterOnMenuOpen')
    const updateSearchMock = jest.fn()
    instance.searchInput = { updateSearch: updateSearchMock }

    wrapper.setState({ isOpen: true })

    expect(filterOnMenuOpenMock).toHaveBeenCalled()
    expect(updateSearchMock).toHaveBeenCalledWith('Test search value')
    expect(onSearchChangeMock).toHaveBeenCalledWith('Test search value')
  })

  it('componentDidUpdate: should clear search value on close if clearSearchOnBlur is true', () => {
    const wrapper = mount(
      <Select
        // @ts-expect-error TS(2769) FIXME: No overload matches this call.
        onSelectClick={() => true}
        items={[]}
        label="Select"
        clearSearchOnBlur
      />,
    )
    const instance = wrapper.instance()
    wrapper.setState({ isOpen: true, searchValue: 'Test search value' })

    const onSearchChangeMock = jest.spyOn(instance, 'onSearchChange')
    const clearSearchOnMenuCloseMock = jest.spyOn(
      instance,
      'clearSearchOnMenuClose',
    )
    const updateSearchMock = jest.fn()
    instance.searchInput = { updateSearch: updateSearchMock }

    wrapper.setState({ isOpen: false })

    expect(clearSearchOnMenuCloseMock).toHaveBeenCalled()
    expect(updateSearchMock).toHaveBeenCalledWith('')
    expect(onSearchChangeMock).toHaveBeenCalledWith('')

    expect(wrapper.state().searchValue).toBe('')
  })

  it('handleSelectOption: should call onSelectClick', () => {
    const onSelectClickSply = jest.fn()
    const wrapper = shallow(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={onSelectClickSply} items={[]} label="Select" />,
    )
    const instance = wrapper.instance()
    instance.handleSelectOption()
    expect(onSelectClickSply).toHaveBeenCalled()
  })

  it('handleSelectOption: should call onSelectClick for custom items', () => {
    const onCustomItemClick = jest.fn()
    const items = [
      {
        id: '1',
        title: 'Item 1',
        selectedItemClick: () => {
          onCustomItemClick()
        },
      },
      {
        id: '2',
        title: 'Item 2',
        selectedItemClick: () => {
          onCustomItemClick()
        },
      },
    ]
    const SelectComponent = (
      <Select
        // @ts-expect-error TS(2769) FIXME: No overload matches this call.
        label="Select"
        // @ts-expect-error TS(7006) FIXME: Parameter 'selectedItem' implicitly has an 'any' t... Remove this comment to see the full error message
        onSelectClick={(selectedItem) => selectedItem.selectedItemClick()}
        items={items}
      />
    )
    const wrapper = shallow(SelectComponent)
    const instance = wrapper.instance()
    instance.handleSelectOption(items[0])
    expect(onCustomItemClick).toHaveBeenCalled()
  })

  it('onClick: should call stopImmediatePropagation', () => {
    const event = {
      stopPropagation: jest.fn(),
      nativeEvent: {
        stopImmediatePropagation: jest.fn(),
      },
    }
    const wrapper = shallow(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={() => true} items={[]} label="Select" />,
    )
    const instance = wrapper.instance()
    instance.onClick(event)
    expect(event.stopPropagation).toHaveBeenCalled()
    expect(event.nativeEvent.stopImmediatePropagation).toHaveBeenCalled()
  })

  it('onSearchChange: should update search in state', () => {
    const wrapper = shallow(
      <Select
        // @ts-expect-error TS(2769) FIXME: No overload matches this call.
        onSelectClick={() => true}
        items={[
          {
            id: '1',
            title: 'Testing',
          },
          {
            id: '2',
            title: '123',
          },
        ]}
        label="Select"
      />,
    )
    const instance = wrapper.instance()
    instance.onSearchChange('Test')
    expect(wrapper.state().items).toEqual([
      {
        id: '1',
        title: 'Testing',
      },
    ])
  })

  it('onSearchChange: should only set isFiltering state to true if search value is provided', () => {
    const wrapper = shallow(
      <Select
        // @ts-expect-error TS(2769) FIXME: No overload matches this call.
        onSelectClick={() => true}
        items={[
          {
            id: '1',
            title: 'Testing',
          },
          {
            id: '2',
            title: '123',
          },
        ]}
        label="Select"
      />,
    )
    const instance = wrapper.instance()

    // Set isFiltering to true because searchValue has a value
    instance.onSearchChange('Test')
    expect(wrapper.state().isFiltering).toEqual(true)

    // Set isFiltering to false because searchValue has no value
    instance.onSearchChange('')
    expect(wrapper.state().isFiltering).toEqual(false)
  })

  it('onSearchChange: should re-order search in state based on matches with startsWith, then includes', () => {
    const wrapper = shallow(
      <Select
        // @ts-expect-error TS(2769) FIXME: No overload matches this call.
        onSelectClick={() => true}
        items={[
          {
            id: '1',
            title: 'Open',
          },
          {
            id: '2',
            title: 'Pending',
          },
          {
            id: '3',
            title: 'Closed',
          },
        ]}
        label="Select"
      />,
    )
    const instance = wrapper.instance()
    instance.onSearchChange('Pen')
    expect(wrapper.state().items).toEqual([
      {
        id: '2',
        title: 'Pending',
      },
      {
        id: '1',
        title: 'Open',
      },
    ])
  })

  it('onSearchChange: should exclude items from search if "hideOnSearch" property is true', () => {
    const wrapper = shallow(
      <Select
        // @ts-expect-error TS(2769) FIXME: No overload matches this call.
        onSelectClick={() => true}
        items={[
          {
            id: '1',
            title: 'Open',
            hideOnSearch: true,
          },
          {
            id: '2',
            title: 'Pending',
          },
          {
            id: '3',
            title: 'Closed',
          },
        ]}
        label="Select"
      />,
    )
    const instance = wrapper.instance()
    instance.onSearchChange('Pen')
    expect(wrapper.state().items).toEqual([
      {
        id: '2',
        title: 'Pending',
      },
    ])
  })

  it('onSearchChange: "hideOnSearch" property should only hide item if search value is provided', () => {
    const wrapper = shallow(
      <Select
        // @ts-expect-error TS(2769) FIXME: No overload matches this call.
        onSelectClick={() => true}
        items={[
          {
            id: '1',
            title: 'Open',
            hideOnSearch: true,
          },
          {
            id: '2',
            title: 'Pending',
          },
          {
            id: '3',
            title: 'Closed',
          },
        ]}
        label="Select"
      />,
    )
    const instance = wrapper.instance()
    instance.onSearchChange('')
    expect(wrapper.state().items).toEqual([
      {
        id: '1',
        title: 'Open',
        hideOnSearch: true,
      },
      {
        id: '2',
        title: 'Pending',
      },
      {
        id: '3',
        title: 'Closed',
      },
    ])
  })

  it('updateHoveredItemPosition: should update hoveredItem in state', () => {
    const wrapper = shallow(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={() => true} items={[]} label="Select" />,
    )
    const instance = wrapper.instance()
    const items = [
      {
        id: '1',
        title: 'Testing',
      },
      {
        id: '2',
        title: '123',
      },
    ]
    const hoveredItem = 0
    const itemsLength = 2
    instance.updateHoveredItemPosition(hoveredItem, itemsLength, items)
    expect(wrapper.state().hoveredItem).toBe(1)
  })

  it('onAddItem: should not call handleSelectOption if hoveredItem is not set in state', () => {
    // @ts-expect-error TS(2769) FIXME: No overload matches this call.
    const wrapper = shallow(<Select items={[]} label="Select" />)
    const instance = wrapper.instance()

    const handleSelectOptionMock = jest.spyOn(instance, 'handleSelectOption')
    instance.onAddItem()
    expect(handleSelectOptionMock).toHaveBeenCalledTimes(0)
  })

  it('onAddItem: should call handleSelectOption if hoveredItem is set in state', () => {
    const items = [
      {
        id: '1',
        title: 'Testing',
      },
      {
        id: '2',
        title: '123',
      },
    ]

    // @ts-expect-error TS(2769) FIXME: No overload matches this call.
    const wrapper = shallow(<Select items={items} label="Select" />)
    const instance = wrapper.instance()
    instance.handleSelectOption = jest.fn()

    const hoveredItem = 0
    const itemsLength = 2
    instance.updateHoveredItemPosition(hoveredItem, itemsLength, items)

    instance.onAddItem()
    expect(instance.handleSelectOption).toHaveBeenCalled()
  })

  it('onMoveDown: should set the hoveredItem in state if there is none', () => {
    const wrapper = shallow(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={() => true} items={[]} label="Select" />,
    )
    const instance = wrapper.instance()
    const spyFunc = jest.spyOn(instance, 'updateHoveredItemPosition')
    instance.onMoveDown()
    expect(spyFunc).toHaveBeenCalled()
    expect(wrapper.state().hoveredItem).toBe(0)
  })

  it('onMoveDown: should increase hoveredItem in state by +1', () => {
    const wrapper = shallow(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={() => true} items={[]} label="Select" />,
    )
    const instance = wrapper.instance()
    wrapper.setState({
      hoveredItem: 0,
      items: [
        {
          id: '1',
          title: 'Testing',
        },
        {
          id: '2',
          title: '123',
        },
      ],
      isFiltering: true,
    })
    instance.onMoveDown()
    expect(wrapper.state().hoveredItem).toBe(1)
  })

  it('onMoveUp: should decrease hoveredItem in state by -1', () => {
    const wrapper = shallow(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={() => true} items={[]} label="Select" />,
    )
    const instance = wrapper.instance()
    wrapper.setState({
      hoveredItem: 1,
      items: [
        {
          id: '1',
          title: 'Testing',
        },
        {
          id: '2',
          title: '123',
        },
      ],
      isFiltering: true,
    })
    instance.onMoveUp()
    expect(wrapper.state().hoveredItem).toBe(0)
  })

  it('Should remove listener on unmount', () => {
    document.removeEventListener = jest.fn()

    const wrapper = shallow(
      // @ts-expect-error TS(2769) FIXME: No overload matches this call.
      <Select onSelectClick={() => true} items={[]} label="Select" />,
    )

    wrapper.unmount()

    expect(document.removeEventListener).toHaveBeenCalled()
  })
})