import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { vi, test, expect, beforeEach } from 'vitest'; vi.mock('../syncService.js', () => ({ getCollaborators: vi.fn(), createEditInvite: vi.fn(), removeCollaborator: vi.fn() })); import CoTeachers from './CoTeachers.jsx'; import { createEditInvite, getCollaborators, removeCollaborator } from '../syncService.js'; beforeEach(() => { vi.clearAllMocks(); getCollaborators.mockResolvedValue({ ok: true, data: { collaborators: [{ userId: 'u2', email: 'sam@x.co' }], pendingInvites: 0 } }); createEditInvite.mockResolvedValue({ ok: true, data: { token: 'abc123', expiresAt: Date.now() + 7 * 86_400_000 } }); removeCollaborator.mockResolvedValue({ ok: true }); vi.spyOn(window, 'confirm').mockReturnValue(true); }); test('the owner sees the co-teachers and can make a single-use invite link', async () => { const user = userEvent.setup(); render(); expect(await screen.findByText('sam@x.co')).toBeInTheDocument(); await user.click(screen.getByRole('button', { name: /invite a co-teacher/i })); const link = await screen.findByLabelText('Invite link'); expect(link.value).toContain('?join=abc123'); expect(createEditInvite).toHaveBeenCalledWith('p1'); }); test('the owner can remove a co-teacher after confirming', async () => { const user = userEvent.setup(); render(); await user.click(await screen.findByRole('button', { name: 'Remove' })); expect(removeCollaborator).toHaveBeenCalledWith('p1', 'u2'); }); test('someone invited to a study sees who shared it and can leave', async () => { const onLeave = vi.fn(); const user = userEvent.setup(); render(); expect(screen.getByText(/owner@x.co invited you to edit/)).toBeInTheDocument(); expect(getCollaborators).not.toHaveBeenCalled(); // only owners can list co-teachers await user.click(screen.getByRole('button', { name: /leave this study/i })); expect(onLeave).toHaveBeenCalled(); }); test('when the server can\'t be reached, it says so instead of offering a link', async () => { getCollaborators.mockResolvedValue({ ok: false, status: 404, error: 'Project not found.' }); render(); await waitFor(() => expect(screen.getByText(/Invites need your account/)).toBeInTheDocument()); expect(screen.queryByRole('button', { name: /invite a co-teacher/i })).toBeNull(); });