/** * Tests for the BugReportBubble component. */ import { describe, it, expect } from 'vitest'; import { render, screen, waitFor } from '../utils'; import userEvent from '@testing-library/user-event'; import { http, HttpResponse } from 'msw'; import { server } from '../mocks/server'; import { BugReportBubble } from '../../components/BugReportBubble'; function getDescriptionTextarea() { return document.querySelector('textarea') as HTMLTextAreaElement; } function getSubmitButton() { const buttons = screen.getAllByRole('button'); return buttons.find( (b) => b.className.includes('bg-red-500') && !b.className.includes('rounded-full') && b.textContent !== '' ); } describe('BugReportBubble', () => { it('renders the floating bug button', () => { render(); const button = screen.getByRole('button'); expect(button).toBeInTheDocument(); }); it('opens panel when bubble is clicked', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button')); expect(getDescriptionTextarea()).toBeInTheDocument(); }); it('closes panel when X button is clicked', async () => { const user = userEvent.setup(); render(); // Open await user.click(screen.getByRole('button')); expect(getDescriptionTextarea()).toBeInTheDocument(); // Close via the X button const buttons = screen.getAllByRole('button'); const closeButton = buttons.find((b) => b.querySelector('.lucide-x')); if (closeButton) await user.click(closeButton); await waitFor(() => { expect(document.querySelector('textarea')).not.toBeInTheDocument(); }); }); it('disables submit when description is empty', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button')); expect(getSubmitButton()).toBeDisabled(); }); it('enables submit when description is provided', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button')); await user.type(getDescriptionTextarea(), 'Something is broken'); expect(getSubmitButton()).not.toBeDisabled(); }); it('shows collecting state with countdown after submit', async () => { const user = userEvent.setup(); // Delay the API response so we can see collecting state server.use( http.post('*/bug-report/submit', async () => { await new Promise((resolve) => setTimeout(resolve, 60000)); return HttpResponse.json({ success: true, message: 'ok', issue_url: null, issue_number: null }); }) ); render(); await user.click(screen.getByRole('button')); await user.type(getDescriptionTextarea(), 'Test bug report'); const submitBtn = getSubmitButton(); if (submitBtn) await user.click(submitBtn); // Should show collecting state await waitFor(() => { const collectingText = screen.queryByText(/collecting|Collecting|εŽι›†|Sammeln|Collecte|Raccolta|Coletando|攢集/i); expect(collectingText).toBeInTheDocument(); }); }); it('shows success state after successful submission', async () => { const user = userEvent.setup(); server.use( http.post('*/bug-report/submit', () => { return HttpResponse.json({ success: true, message: 'Bug report submitted successfully!', issue_url: 'https://github.com/maziggy/bambuddy/issues/42', issue_number: 42, }); }) ); render(); await user.click(screen.getByRole('button')); await user.type(getDescriptionTextarea(), 'Test bug'); const submitBtn = getSubmitButton(); if (submitBtn) await user.click(submitBtn); await waitFor( () => { expect(screen.getByText(/#42/)).toBeInTheDocument(); }, { timeout: 35000 } ); }); it('shows error state after failed submission', async () => { const user = userEvent.setup(); server.use( http.post('*/bug-report/submit', () => { return HttpResponse.json({ success: false, message: 'Relay not available', issue_url: null, issue_number: null, }); }) ); render(); await user.click(screen.getByRole('button')); await user.type(getDescriptionTextarea(), 'Test bug'); const submitBtn = getSubmitButton(); if (submitBtn) await user.click(submitBtn); await waitFor( () => { expect(screen.getByText(/Relay not available/)).toBeInTheDocument(); }, { timeout: 35000 } ); }); it('has expandable data collection notice', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button')); const details = document.querySelector('details'); expect(details).toBeInTheDocument(); }); });