import { afterEach, describe, expect, it, vi } from 'vitest' import { flushPromises, mount, type VueWrapper } from '@vue/test-utils' import uiPlugin from '@nuxt/ui/vue-plugin' import inviteEmailCases from '../../../../test/testdata/invite_emails.json' import InviteUserModal from '../InviteUserModal.vue' const INVALID_EMAIL = 'Enter a full email address, like name@example.com.' const inviteUser = vi.fn() vi.mock('@/composables/useUsers', () => ({ useUsers: () => ({ inviteUser }), })) let wrapper: VueWrapper | null = null function emailInput() { return document.body.querySelector('input[placeholder="name@example.com"]')! } function dialogText() { return document.body.querySelector('[role="dialog"]')?.textContent ?? '' } function dispatch(element: HTMLElement, type: string) { element.dispatchEvent(new Event(type, { bubbles: true })) } async function mountOpenModal() { wrapper = mount(InviteUserModal, { attachTo: document.body, props: { open: true }, global: { plugins: [uiPlugin] }, }) await flushPromises() } function clickSend() { const send = [...document.body.querySelectorAll('button')].find( (button) => button.textContent?.trim() === 'Send invite', )! send.click() return flushPromises() } afterEach(() => { wrapper?.unmount() wrapper = null document.body.innerHTML = '' inviteUser.mockReset() }) describe('Invite modal email rule', () => { it.each(inviteEmailCases)('gives "$email" the shared verdict (valid: $valid)', async (row) => { inviteUser.mockResolvedValue({ id: 1 }) await mountOpenModal() const input = emailInput() input.value = row.email dispatch(input, 'input') dispatch(input, 'blur') await flushPromises() await clickSend() if (row.valid) { expect(dialogText()).not.toContain(INVALID_EMAIL) expect(inviteUser).toHaveBeenCalledTimes(1) } else { expect(dialogText()).toContain(INVALID_EMAIL) expect(inviteUser).not.toHaveBeenCalled() } }) })