import { expect, test, vi, it, } from 'vitest'; import {render, fireEvent, act} from '@testing-library/react'; import $ from 'jquery'; import {t} from '@/scripts/i18n'; import Modal from '@/components/Modal'; test('hidden by default', () => { const {queryByRole} = render(); expect(queryByRole('dialog')).toBeNull(); }); test('receive id', () => { const {getByRole} = render(); expect(getByRole('dialog')).toHaveAttribute('id', 'kumiko'); }); test('centered dialog', () => { const {getByRole} = render(); expect(getByRole('document')).toHaveClass('modal-dialog-centered'); }); test('background color', () => { const {container} = render(); expect(container.querySelector('.modal-content')).toHaveClass('bg-primary'); }); test('jQuery events', () => { const {getByText} = render(); act(() => { vi.runAllTimers(); $('.modal').trigger('hidden.bs.modal'); }); fireEvent.click(getByText(t('general.cancel'))); act(() => { vi.runAllTimers(); $('.modal').trigger('hidden.bs.modal'); }); }); describe('modal header', () => { it('modal title', () => { const {queryByText} = render(); expect(queryByText('Tips')).toBeInTheDocument(); }); it('hide modal header', () => { const {queryByText} = render( , ); expect(queryByText('Tips')).not.toBeInTheDocument(); }); }); describe('modal body', () => { it('custom children', () => { const {queryByText} = render(body); expect(queryByText('body')).toBeInTheDocument(); }); it('text with linebreaks', () => { const {queryByText} = render(); expect(queryByText('L1')).toBeInTheDocument(); expect(queryByText('L2')).toBeInTheDocument(); }); it('dangerous HTML', () => { const {getByText} = render( , ); expect(getByText('ab')).toHaveClass('h1'); }); describe('input control', () => { it('set default value', () => { const {queryByDisplayValue} = render( , ); expect(queryByDisplayValue('val')).toBeInTheDocument(); }); it('placeholder', () => { const {queryByPlaceholderText} = render( , ); expect(queryByPlaceholderText('hint')).toBeInTheDocument(); }); it('input control type', () => { const {getByPlaceholderText} = render( , ); expect(getByPlaceholderText('password')).toHaveAttribute( 'type', 'password', ); }); }); }); describe('modal footer', () => { it('custom footer content', () => { const {queryByText} = render(footer}/>); expect(queryByText('footer')).toBeInTheDocument(); expect(queryByText(t('general.confirm'))).not.toBeInTheDocument(); }); it('flex footer', () => { const {getByText} = render(); expect(getByText('footer')).toHaveClass('d-flex', 'justify-content-between'); }); it('custom ok button', () => { const {getByText} = render( , ); expect(getByText('kumiko')).toHaveClass('btn-primary'); }); it('custom cancel button', () => { const {getByText} = render( , ); expect(getByText('reina')).toHaveClass('btn-success'); }); }); describe('"alert" mode', () => { it('buttons', () => { const resolve = vi.fn(); const {getByText, queryByText} = render( , ); fireEvent.click(getByText(t('general.confirm'))); expect(resolve).toBeCalledWith({value: ''}); expect(queryByText(t('general.cancel'))).toBeNull(); }); it('confirm callback is optional', () => { const {getByText} = render(); fireEvent.click(getByText(t('general.confirm'))); }); }); describe('"confirm" mode', () => { it('default mode is "confirm"', () => { const {queryByText} = render(); expect(queryByText(t('general.confirm'))).toBeInTheDocument(); expect(queryByText(t('general.cancel'))).toBeInTheDocument(); }); it('"confirm" button', () => { const resolve = vi.fn(); const reject = vi.fn(); const {getByText} = render( , ); fireEvent.click(getByText(t('general.confirm'))); expect(resolve).toBeCalledWith({value: ''}); expect(reject).not.toBeCalled(); }); it('"cancel" button', () => { const resolve = vi.fn(); const reject = vi.fn(); const {getByText} = render( , ); fireEvent.click(getByText(t('general.cancel'))); expect(resolve).not.toBeCalled(); expect(reject).toBeCalled(); }); }); describe('"prompt" mode', () => { it('retrieve input value', () => { const resolve = vi.fn(); const reject = vi.fn(); const {getByPlaceholderText, getByText} = render( , ); fireEvent.change(getByPlaceholderText('hint'), { target: {value: 'my'}, }); fireEvent.click(getByText(t('general.confirm'))); expect(resolve).toBeCalledWith({value: 'my'}); expect(reject).not.toBeCalled(); }); it('cancel dialog', () => { const resolve = vi.fn(); const reject = vi.fn(); const {getByText} = render( , ); fireEvent.click(getByText(t('general.cancel'))); expect(resolve).not.toBeCalled(); expect(reject).toBeCalled(); }); it('radio inputs', () => { const choices = [ {text: 'A', value: 'a'}, {text: 'B', value: 'b'}, ]; const resolve = vi.fn(); const {getByText, getByLabelText} = render( , ); fireEvent.click(getByLabelText('B')); fireEvent.click(getByText(t('general.confirm'))); expect(resolve).toBeCalledWith({value: 'b'}); }); it('validate input', () => { const resolve = vi.fn(); const reject = vi.fn(); const validator = vi.fn().mockReturnValue(true); const {getByText} = render( , ); fireEvent.click(getByText(t('general.confirm'))); expect(resolve).toBeCalledWith({value: 'val'}); expect(reject).not.toBeCalled(); }); it('report validator message', () => { const message = 'Invalid input.'; const resolve = vi.fn(); const reject = vi.fn(); const validator = vi.fn().mockReturnValue(message); const {getByText, queryByText} = render( , ); expect(queryByText(message)).not.toBeInTheDocument(); fireEvent.click(getByText(t('general.confirm'))); expect(queryByText(message)).toBeInTheDocument(); expect(resolve).not.toBeCalled(); expect(reject).not.toBeCalled(); }); }); describe('"onClose" event', () => { it('button confirm', () => { const mock = vi.fn(); const {getByText} = render(); fireEvent.click(getByText(t('general.confirm'))); expect(mock).toBeCalled(); }); it('button cancel', () => { const mock = vi.fn(); const {getByText} = render(); fireEvent.click(getByText(t('general.cancel'))); expect(mock).toBeCalled(); }); });