test('renders a form with title, content, tags, and a submit button', async () => { const fakeUser = {id: 'user-1'} const {getByLabelText, getByText} = render(<Editor user={fakeUser} />) const fakePost = { title: 'Test Title', content: 'Test content', tags: ['tag1', 'tag2'], } const preDate = Date.now() getByLabelText(/title/i).value = fakePost.title getByLabelText(/content/i).value = fakePost.content getByLabelText(/tags/i).value = fakePost.tags.join(', ') const submitButton = getByText(/submit/i) fireEvent.click(submitButton) expect(submitButton).toBeDisabled() expect(mockSavePost).toHaveBeenCalledTimes(1) expect(mockSavePost).toHaveBeenCalledWith({ ...fakePost, date: expect.any(String), authorId: fakeUser.id, }) const postDate = Date.now() const date = new Date(mockSavePost.mock.calls[0][0].date).getTime() expect(date).toBeGreaterThanOrEqual(preDate) expect(date).toBeLessThanOrEqual(postDate) await wait(() => expect(MockRedirect).toHaveBeenCalledTimes(1)) expect(MockRedirect).toHaveBeenCalledWith({to: '/'}, {}) })
test('focuses correctly and submits on enter in last input', async () => { // Given const myFn = jest.fn() // When const { container, getByValue } = render( <InputEnterStepping submitAction={myFn} render={({ getInputPropsForIndex, setRefForIndex }) => { return ( <React.Fragment> <input {...getInputPropsForIndex(0, { initialFocus: true, defaultValue: 'first', ref: ref => setRefForIndex(0, ref) })} /> <input {...getInputPropsForIndex(1, { defaultValue: 'second', ref: ref => setRefForIndex(1, ref) })} /> </React.Fragment> ) }} /> ) // Then expect(container).toMatchSnapshot() // Need to wait for the focus to get there, since it's set by setTimeout await wait(() => expect(document.activeElement).toEqual(getByValue('first'))) // When // Enter in first should focus second fireEvent.keyDown(getByValue('first'), { key: 'Enter', keyCode: 13, which: 13 }) // Then expect(document.activeElement).toEqual(getByValue('second')) expect(myFn).toHaveBeenCalledTimes(0) // When // Enter in last should submit fireEvent.keyDown(getByValue('second'), { key: 'Enter', keyCode: 13, which: 13 }) // Then expect(document.activeElement).toEqual(getByValue('second')) expect(myFn).toHaveBeenCalledTimes(1) })
async function waitForTrayClosed() { return wait(() => { if (context.onExited.callCount > 0) { return } throw new Error('Tray is still open') }) }
test('loads greetings on click', async () => { const {getByLabelText, getByText, getByTestId} = render(<GreetingLoader />) const nameInput = getByLabelText(/name/i) const loadButton = getByText(/load/i) nameInput.value = 'Mary' fireEvent.click(loadButton) expect(mockLoadGreeting).toHaveBeenCalledTimes(1) expect(mockLoadGreeting).toHaveBeenCalledWith('Mary') await wait(() => expect(getByTestId('greeting')).toHaveTextContent(`Hi Mary`)) })
it('deletes the assignment and reloads', async () => { const reloadSpy = jest.spyOn(window.location, 'reload') const assignment = mockAssignment() const {getByTestId} = await openDeleteDialog(assignment, [ saveAssignmentResult(assignment, {state: 'deleted'}, {state: 'deleted'}) ]) const reallyDeleteButton = await waitForElement(() => getByTestId('delete-dialog-confirm-button') ) fireEvent.click(reallyDeleteButton) await wait(() => expect(reloadSpy).toHaveBeenCalled()) })
test('loads greetings on click', async () => { const mockLoadGreeting = jest.fn(subject => Promise.resolve({data: {greeting: `Hi ${subject}`}}), ) const {getByLabelText, getByText, getByTestId} = render( <GreetingLoader loadGreeting={mockLoadGreeting} />, ) const nameInput = getByLabelText(/name/i) const loadButton = getByText(/load/i) nameInput.value = 'Mary' fireEvent.click(loadButton) expect(mockLoadGreeting).toHaveBeenCalledTimes(1) expect(mockLoadGreeting).toHaveBeenCalledWith('Mary') await wait(() => expect(getByTestId('greeting')).toHaveTextContent(`Hi Mary`)) })
test('should change file', async () => { const { container } = render(<Uploader {...props} />) const input = container.querySelector('input[type="file"]') await fireEvent.change(input, { target: { files: [new File(['(⌐□_□)'], 'mypicture.jpg', { type: 'image/jpeg' })] } }) await wait(() => { expect(container.querySelector('img')).toBeInTheDocument() expect(container).toMatchSnapshot() }) })
test('should delete photo', async () => { const { getByText, store } = renderComponent() fireEvent.click(getByText('Yes')) const expectedActions = [ { type: 'DELETE_PHOTO_REQUEST', id: 1 }, { type: 'DELETE_PHOTO_SUCCESS', response: {}, id: 1 }, { type: 'ADD_MESSAGE', status: 'success', key: 'CRUD_PHOTO', message: 'Your photo has been deleted successfully' } ] await wait(() => { expect(store.getActions()).toEqual(expectedActions) }) })
test('calls onMount with data on mounting', async () => { // Given const mFn = jest.fn() const context = { projects: [ { graphs: [ { status: 'ACTIVE', configuration: { protocols: { bolt: { username: '******' } } } } ] } ] } const integrationPoint = { getContext: () => Promise.resolve(context) } // When const { container, rerender } = render( <DesktopIntegration integrationPoint={integrationPoint} onMount={mFn} /> ) await wait(() => expect(mFn).toHaveBeenCalledTimes(1)) // Then expect(container).toMatchSnapshot() // When rerender(<DesktopIntegration integrationPoint={integrationPoint} />) // Then expect(mFn).toHaveBeenCalledTimes(1) expect(container).toMatchSnapshot() })
async function waitForPosting() { await wait(() => resolvePostAssignmentGradesStatusStub.callCount > 0) }
test('an effect which sets state but has no dependencies will warn', async () => { jest.spyOn(console, 'warn').mockImplementation(() => {}) React.useEffect = function tryCatchUseEffect(cb, deps) { return originalUseEffect(() => { // stop the infinite loop if we've warned. The test is over now, but // before we restore the useEffect back to where it should go, we need // to prevent the infinite loop otherwise React will figure out what // we're doing and be mad :-( if (console.warn.mock.calls.length) { return } try { return cb() } catch (e) { if (e.message && e.message.includes('stop-runaway-react-effects')) { // try/catch prevents test from failing. We assert on the warning } else { // something else is up throw e } } }, deps) } hijackEffects({callCount: 2}) function Test() { const forceUpdate = useForceUpdate() React.useEffect(() => { forceUpdate() }) return null } let rendered = false // it's super weird, but somehow the error is not try/catchable here, but // it still fails the test. It's really odd. So we do some weird stuff to make // sure we wait for it to be thrown. await wait( () => { if (!rendered) { rendered = true render(<Test />) } expect(console.warn).toHaveBeenCalledTimes(1) }, {timeout: 500}, ) expect(console.warn.mock.calls[0]).toMatchInlineSnapshot(` Array [ "The following effect callback was invoked 2 times in 1000ms", " ", "() => { forceUpdate(); }", " ", "This effect is not called with a dependencies argument and probably should. Start by adding \`[]\` as a second argument to the useEffect call, then add any other dependencies as elements to that array. You may also be interested in installing ESLint with https://npm.im/eslint-plugin-react-hooks", ] `) console.warn.mockRestore() })
test('an effect which sets state and has changing dependencies will warn', async () => { jest.spyOn(console, 'warn').mockImplementation(() => {}) React.useEffect = function tryCatchUseEffect(cb, deps) { return originalUseEffect(() => { // stop the infinite loop if we've warned. The test is over now, but // before we restore the useEffect back to where it should go, we need // to prevent the infinite loop otherwise React will figure out what // we're doing and be mad :-( if (console.warn.mock.calls.length) { return } try { return cb() } catch (e) { if (e.message && e.message.includes('stop-runaway-react-effects')) { // try/catch prevents test from failing. We assert on the warning } else { // something else is up throw e } } }, deps) } hijackEffects({callCount: 2}) function Test() { const forceUpdate = useForceUpdate() React.useEffect(() => { forceUpdate() }, ['I am unchanged', {}, 'I am also unchanged']) return null } let rendered = false // it's super weird, but somehow the error is not try/catchable here, but // it still fails the test. It's really odd. So we do some weird stuff to make // sure we wait for it to be thrown. await wait( () => { if (!rendered) { rendered = true render(<Test />) } expect(console.warn).toHaveBeenCalledTimes(1) }, {timeout: 500}, ) expect(console.warn.mock.calls[0]).toMatchInlineSnapshot(` Array [ "The following effect callback was invoked 2 times in 1000ms", " ", "() => { forceUpdate(); }", " ", "Here are the dependencies this effect was called with the last 2 times:", Array [ Array [ "I am unchanged", Object {}, "I am also unchanged", ], Array [ "I am unchanged", Object {}, "I am also unchanged", ], ], " ", "Here are the dependency changes between each call:", Array [ Array [ "I am unchanged", Object {}, "I am also unchanged", ], Array [ "UNCHANGED", Object {}, "UNCHANGED", ], ], " ", "Try to find where those changing dependencies are initialized. You probably need to memoize them using React.useMemo or React.useCallback", ] `) console.warn.mockRestore() })