import { renderHook, act } from '@testing-library/react' import React, { ReactNode } from 'react' import { AlertsProvider, useAlerts, useAlert } from '../index' describe('useAlert and useAlerts used together', () => { it('Can add an alert with static arguments', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const { show } = useAlert('test', { permanent: true }) return { alerts, show } }, { wrapper } ) act(() => { result.current.show() }) expect(result.current.alerts).toHaveLength(1) const alert = result.current.alerts[0] expect(alert).toEqual({ id: 1, remove: expect.any(Function), message: 'test', options: { permanent: true, }, }) }) it('Can add an alert with dynamic arguments', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const { show } = useAlert( ({ username }) => `Successfully deleted ${username}`, ({ isCurrentUser }) => isCurrentUser ? { critical: true } : { success: true } ) return { alerts, show } }, { wrapper } ) act(() => { result.current.show({ username: 'hendrik', isCurrentUser: true }) }) expect(result.current.alerts).toHaveLength(1) const alert = result.current.alerts[0] expect(alert).toEqual({ id: 1, remove: expect.any(Function), message: 'Successfully deleted hendrik', options: { critical: true, }, }) }) it('Can remove an alert with the hide function from useAlerts', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const { show, hide } = useAlert('test', { permanent: true }) return { alerts, show, hide } }, { wrapper } ) act(() => { result.current.show() }) expect(result.current.alerts).toHaveLength(1) expect(result.current.alerts[0]).toEqual({ id: 1, remove: expect.any(Function), message: 'test', options: { permanent: true, }, }) act(() => { result.current.hide() }) expect(result.current.alerts).toHaveLength(0) }) it('Can remove an alert with the remove function on the alert itself', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const { show, hide } = useAlert('test', { permanent: true }) return { alerts, show, hide } }, { wrapper } ) act(() => { result.current.show() }) expect(result.current.alerts).toHaveLength(1) expect(result.current.alerts[0]).toEqual({ id: 1, remove: expect.any(Function), message: 'test', options: { permanent: true, }, }) act(() => { result.current.alerts[0].remove!() }) expect(result.current.alerts).toHaveLength(0) }) it('Will not create duplicate alerts because of rerenders', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const hook = renderHook( () => { const alerts = useAlerts() const { show } = useAlert('test', { permanent: true }) return { alerts, show } }, { wrapper } ) act(() => { hook.result.current.show() hook.rerender() hook.rerender() }) expect(hook.result.current.alerts).toHaveLength(1) }) it('Will not create duplicate alerts when show is called multiple times in a render cycle', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const { show } = useAlert('test', { permanent: true }) return { alerts, show } }, { wrapper } ) act(() => { result.current.show() result.current.show() }) expect(result.current.alerts).toHaveLength(1) }) it('Will update the alert if show is called with different arguments', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const { show } = useAlert( ({ message }) => message, ({ options }) => options ) return { alerts, show } }, { wrapper } ) // Show alert for first time const payload1 = { message: 'Message 1', options: { permanent: true, critical: true }, } act(() => { result.current.show(payload1) }) expect(result.current.alerts).toHaveLength(1) expect(result.current.alerts[0]).toEqual({ ...payload1, id: 1, remove: expect.any(Function), options: { ...payload1.options, }, }) // Show alert for second time const payload2 = { message: 'Message 2', options: { success: true }, } act(() => { result.current.show(payload2) }) expect(result.current.alerts).toHaveLength(1) expect(result.current.alerts[0]).toEqual({ ...payload2, id: 1, remove: expect.any(Function), options: { ...payload2.options, }, }) }) it('Will increment IDs when multiple alerts are added', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const { show: show1 } = useAlert('show 1') const { show: show2 } = useAlert('show 2') const { show: show3 } = useAlert('show 3') return { alerts, show1, show2, show3 } }, { wrapper } ) act(() => { result.current.show1() result.current.show2() result.current.show3() }) expect(result.current.alerts).toHaveLength(3) expect(result.current.alerts[0].id).toBe(1) expect(result.current.alerts[1].id).toBe(2) expect(result.current.alerts[2].id).toBe(3) }) it('Removes the correct item from the alerts array', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const alert1 = useAlert('show 1') const alert2 = useAlert('show 2') const alert3 = useAlert('show 3') return { alerts, alert1, alert2, alert3 } }, { wrapper } ) act(() => { result.current.alert1.show() result.current.alert2.show() result.current.alert3.show() }) expect(result.current.alerts).toHaveLength(3) act(() => { result.current.alert2.hide() }) expect(result.current.alerts).toHaveLength(2) expect(result.current.alerts[0].id).toBe(1) expect(result.current.alerts[1].id).toBe(3) }) it('Gets added into a new slot when show is called after hide', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const alert1 = useAlert('show 1') const alert2 = useAlert('show 2') return { alerts, alert1, alert2 } }, { wrapper } ) act(() => { result.current.alert1.show() result.current.alert2.show() }) expect(result.current.alerts).toHaveLength(2) expect(result.current.alerts[0].id).toBe(1) expect(result.current.alerts[1].id).toBe(2) act(() => { result.current.alert1.hide() }) expect(result.current.alerts).toHaveLength(1) expect(result.current.alerts[0].id).toBe(2) act(() => { result.current.alert1.show() }) expect(result.current.alerts).toHaveLength(2) expect(result.current.alerts[0].id).toBe(2) // now is last item with id 3 because it was "re-added" expect(result.current.alerts[1].id).toBe(3) }) it('Gets added into a new slot when show is called after remove is called on an alert', () => { const wrapper = ({ children }: { children?: ReactNode }) => ( {children} ) const { result } = renderHook( () => { const alerts = useAlerts() const alert1 = useAlert('show 1') const alert2 = useAlert('show 2') return { alerts, alert1, alert2 } }, { wrapper } ) act(() => { result.current.alert1.show() result.current.alert2.show() }) expect(result.current.alerts).toHaveLength(2) expect(result.current.alerts[0].id).toBe(1) expect(result.current.alerts[1].id).toBe(2) act(() => { result.current.alerts[0].remove!() }) expect(result.current.alerts).toHaveLength(1) expect(result.current.alerts[0].id).toBe(2) act(() => { result.current.alert1.show() }) expect(result.current.alerts).toHaveLength(2) expect(result.current.alerts[0].id).toBe(2) // now is last item with id 3 because it was "re-added" expect(result.current.alerts[1].id).toBe(3) }) })