/* eslint-env jest */ import { mount } from 'enzyme' import React, { ChangeEvent, useEffect, useState } from 'react' import { act } from 'react-dom/test-utils' import { useMiniSearch, withMiniSearch, WithMiniSearch, UseMiniSearch } from './react-minisearch' import MiniSearch, { Options } from 'minisearch' type DocumentType = { uid: number, title: string } const documents: DocumentType[] = [ { uid: 1, title: 'De Rerum Natura' }, { uid: 2, title: 'The Selfish Gene' } ] const options: Options = { fields: ['title'], idField: 'uid' } type Props = { documents: DocumentType[], options: Options, documentToAdd: DocumentType | null, documentsToAdd: DocumentType[] | null, documentToRemove: DocumentType | null } const props: Props = { documents, options, documentToAdd: null, documentsToAdd: null, documentToRemove: null } const documentToAdd = { uid: 3, title: 'Pista Nera' } const documentsToAdd = [{ uid: 3, title: 'Six Easy Pieces' }, { uid: 4, title: 'Six Not So Easy Pieces' }] const documentToRemove = documents[0] const documentToReplace = { uid: 1, title: 'The Big Picture' } let promise = Promise.resolve() const ChildComponent: React.FC> = ({ search, searchResults, rawResults, autoSuggest, suggestions, add, addAll, addAllAsync, getById, remove, removeById, removeAll, discard, replace, clearSearch, clearSuggestions }) => { const [docTitle, setDocTitle] = useState(null) const handleChange = (event: ChangeEvent) => { const query = event.target.value autoSuggest(query) search(query) } return (
    { suggestions && suggestions.map(({ suggestion }) =>
  • { suggestion }
  • )}
    { searchResults && searchResults.map((result) =>
  • { result.title }
  • ) }
{ docTitle }
) } const testComponent = (Component: React.FC) => { it('gets a miniSearch prop with the MiniSearch instance', () => { const wrap = mount() expect(wrap.find('ChildComponent')).toHaveProp('miniSearch', expect.any(MiniSearch)) }) it('performs search', () => { const wrap = mount() expect(wrap.find('.results li')).not.toExist() wrap.find('input.search').simulate('change', { target: { value: 'natura' } }) const items = wrap.update().find('.results li') expect(items).toHaveLength(1) expect(items.first()).toHaveText(documents[0].title) expect(wrap.find('ChildComponent').prop('rawResults')).toHaveLength(1) expect(wrap.find('ChildComponent').prop('rawResults')[0]).toMatchObject({ id: documents[0].uid, terms: ['natura'] }) wrap.find('input.search').simulate('change', { target: { value: 'xyz' } }) expect(wrap.find('.results li')).not.toExist() expect(wrap.find('ChildComponent').prop('rawResults')).toHaveLength(0) }) it('performs a wildcard search', () => { const wrap = mount() expect(wrap.find('.results li')).not.toExist() wrap.find('button.wildcard-search').simulate('click') const items = wrap.update().find('.results li') expect(items).toHaveLength(documents.length) expect(items.first()).toHaveText(documents[0].title) expect(items.at(1)).toHaveText(documents[1].title) expect(wrap.find('ChildComponent').prop('rawResults')).toHaveLength(documents.length) expect(wrap.find('ChildComponent').prop('rawResults')[0]).toMatchObject({ id: documents[0].uid, terms: [] }) }) it('produces auto suggestions', () => { const wrap = mount() expect(wrap.find('.suggestions li')).not.toExist() wrap.find('input.search').simulate('change', { target: { value: 'sel' } }) const items = wrap.update().find('.suggestions li') expect(items).toHaveLength(1) expect(items.first()).toHaveText('selfish') wrap.find('input.search').simulate('change', { target: { value: 'xyz' } }) expect(wrap.find('.suggestions li')).not.toExist() }) it('adds a document', () => { const wrap = mount() wrap.find('button.add').simulate('click') wrap.find('input.search').simulate('change', { target: { value: 'pista' } }) const items = wrap.update().find('.results li') expect(items).toHaveLength(1) expect(items.first()).toHaveText(documentToAdd.title) }) it('adds multiple documents', () => { const wrap = mount() wrap.find('button.add-all').simulate('click') wrap.find('input.search').simulate('change', { target: { value: 'pieces' } }) const items = wrap.update().find('.results li') expect(items).toHaveLength(2) expect(items.first()).toHaveText(documentsToAdd[0].title) expect(items.last()).toHaveText(documentsToAdd[1].title) }) it('adds multiple documents asyncronously', async () => { const wrap = mount() expect(wrap.find('ChildComponent')).toHaveProp('isIndexing', false) const acting = act(async () => { wrap.find('button.add-all-async').simulate('click') }) expect(wrap.update().find('ChildComponent')).toHaveProp('isIndexing', true) await acting await promise expect(wrap.update().find('ChildComponent')).toHaveProp('isIndexing', false) wrap.find('input.search').simulate('change', { target: { value: 'pieces' } }) const items = wrap.update().find('.results li') expect(items).toHaveLength(2) expect(items.first()).toHaveText(documentsToAdd[0].title) expect(items.last()).toHaveText(documentsToAdd[1].title) }) it('gets a document by id', () => { const wrap = mount() wrap.find('button.add').simulate('click') wrap.find('button.get-by-id').simulate('click') const items = wrap.update().find('.doc-title') expect(items).toHaveLength(1) expect(items.first()).toHaveText(documentToAdd.title) }) it('removes a document', () => { const wrap = mount() wrap.find('button.remove').simulate('click') wrap.find('input.search').simulate('change', { target: { value: 'natura' } }) const items = wrap.update().find('.results li') expect(items).not.toExist() expect(() => { wrap.unmount() }).not.toThrow() }) it('removes a document by id', () => { const wrap = mount() wrap.find('button.remove-by-id').simulate('click') wrap.find('input.search').simulate('change', { target: { value: 'natura' } }) const items = wrap.update().find('.results li') expect(items).not.toExist() }) it('removes all documents', () => { const wrap = mount() wrap.find('button.remove-all').simulate('click') ;['natura', 'selfish'].forEach((query) => { wrap.find('input.search').simulate('change', { target: { value: query } }) const items = wrap.update().find('.results li') expect(items).not.toExist() }) }) it('removes all documents and re-adds some documents', () => { const wrap = mount() wrap.find('button.remove-all').simulate('click') wrap.find('button.add-all').simulate('click') wrap.find('input.search').simulate('change', { target: { value: 'pieces' } }) const items = wrap.update().find('.results li') expect(items).toHaveLength(2) ;['natura', 'selfish'].forEach((query) => { wrap.find('input.search').simulate('change', { target: { value: query } }) const items = wrap.update().find('.results li') expect(items).not.toExist() }) }) it('discards a document', () => { const wrap = mount() wrap.find('button.discard').simulate('click') wrap.find('input.search').simulate('change', { target: { value: 'natura' } }) const items = wrap.update().find('.results li') expect(items).not.toExist() }) it('replaces a document', () => { const wrap = mount() wrap.find('button.replace').simulate('click') wrap.find('input.search').simulate('change', { target: { value: 'picture' } }) let items = wrap.update().find('.results li') expect(items).toHaveLength(1) expect(items.first()).toHaveText(documentToReplace.title) wrap.find('input.search').simulate('change', { target: { value: 'natura' } }) items = wrap.update().find('.results li') expect(items).not.toExist() }) it('clears search and auto suggestions', () => { const wrap = mount() wrap.find('input.search').simulate('change', { target: { value: 'natura' } }) wrap.update() expect(wrap.find('ChildComponent').prop('searchResults')).not.toBeNull() expect(wrap.find('ChildComponent').prop('rawResults')).not.toBeNull() expect(wrap.find('ChildComponent').prop('suggestions')).not.toBeNull() wrap.find('button.clear').simulate('click') wrap.update() expect(wrap.find('ChildComponent').prop('searchResults')).toBeNull() expect(wrap.find('ChildComponent').prop('rawResults')).toBeNull() expect(wrap.find('ChildComponent').prop('suggestions')).toBeNull() }) } describe('useMiniSearch', () => { const MyComponent = ({ documents, options }) => { const props = useMiniSearch(documents, options) return } testComponent(MyComponent) describe('does not trigger unintended effects', () => { const mockDocumentsFetch = () => ({ then: (cb) => cb(documents) }) const MyComponent = ({ options }) => { const props = useMiniSearch([], options) const { addAll } = props useEffect(() => { mockDocumentsFetch().then(addAll) }, [addAll]) return } testComponent(MyComponent) }) }) describe('withMiniSearch', () => { const MyComponent = withMiniSearch(documents, options, ChildComponent) const MyComponentWithAdditionalProp = withMiniSearch(documents, options, ChildComponent) testComponent(MyComponent) it('accepts other props', () => { const wrap = mount() expect(wrap.find('ChildComponent')).toHaveProp('otherProp', 'foo') }) it('sets the display name of the wrapped component', () => { const wrap = mount() expect(wrap.find('WithMiniSearch(ChildComponent)')).toExist() }) }) describe('WithMiniSearch', () => { const MyComponent = ({ documents, options }: Props) => ( { (props) => } ) testComponent(MyComponent) })