/** * Tests for InstallAppButton - the in-app PWA install trigger (#1460). */ import { describe, it, expect, vi } from 'vitest'; import { act } from 'react'; import { render, screen, waitFor } from '../utils'; import userEvent from '@testing-library/user-event'; import { InstallAppButton } from '../../components/InstallAppButton'; // Scoped so the assertion ignores unrelated buttons (e.g. a toast's dismiss). const INSTALL_BUTTON = { name: 'Install app' } as const; /** Build a fake beforeinstallprompt event with a controllable userChoice. */ function makeInstallPromptEvent(outcome: 'accepted' | 'dismissed') { const event = new Event('beforeinstallprompt') as Event & { prompt: () => Promise; userChoice: Promise<{ outcome: string; platform: string }>; }; event.prompt = vi.fn().mockResolvedValue(undefined); event.userChoice = Promise.resolve({ outcome, platform: 'web' }); return event; } describe('InstallAppButton', () => { it('renders nothing until the browser fires beforeinstallprompt', () => { render(); expect(screen.queryByRole('button', INSTALL_BUTTON)).toBeNull(); }); it('shows the install button once beforeinstallprompt fires', async () => { render(); await act(async () => { window.dispatchEvent(makeInstallPromptEvent('accepted')); }); expect(await screen.findByRole('button', INSTALL_BUTTON)).toBeInTheDocument(); }); it('fires the captured prompt on click and hides itself afterwards', async () => { const user = userEvent.setup(); const event = makeInstallPromptEvent('accepted'); render(); await act(async () => { window.dispatchEvent(event); }); await user.click(await screen.findByRole('button', INSTALL_BUTTON)); expect(event.prompt).toHaveBeenCalledTimes(1); // A captured prompt can only be used once, so the button must disappear. await waitFor(() => expect(screen.queryByRole('button', INSTALL_BUTTON)).toBeNull() ); }); it('hides itself even when the user dismisses the prompt', async () => { const user = userEvent.setup(); const event = makeInstallPromptEvent('dismissed'); render(); await act(async () => { window.dispatchEvent(event); }); await user.click(await screen.findByRole('button', INSTALL_BUTTON)); await waitFor(() => expect(screen.queryByRole('button', INSTALL_BUTTON)).toBeNull() ); }); it('hides the button when the app reports it was installed', async () => { render(); await act(async () => { window.dispatchEvent(makeInstallPromptEvent('accepted')); }); expect(await screen.findByRole('button', INSTALL_BUTTON)).toBeInTheDocument(); await act(async () => { window.dispatchEvent(new Event('appinstalled')); }); await waitFor(() => expect(screen.queryByRole('button', INSTALL_BUTTON)).toBeNull() ); }); });