diff options
| author | Pawel Zelawski <pawel@pzelawski.com> | 2026-05-23 11:11:53 +0200 |
|---|---|---|
| committer | Pawel Zelawski <pawel@pzelawski.com> | 2026-05-23 11:11:53 +0200 |
| commit | 04d93d7d235d328ef40c9dae4e1f56dc8a5e893f (patch) | |
| tree | b1da6b01a7ce0acbeeba3d3b1b3c25b43864bcdb /tests/client/App.test.tsx | |
| parent | b4369d9d0f700869fd82f64bdc3af012a1ce5bd9 (diff) | |
feat: harden demo security and add full test suite
Diffstat (limited to 'tests/client/App.test.tsx')
| -rw-r--r-- | tests/client/App.test.tsx | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/tests/client/App.test.tsx b/tests/client/App.test.tsx new file mode 100644 index 0000000..960f383 --- /dev/null +++ b/tests/client/App.test.tsx @@ -0,0 +1,122 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import App from '../../src/client/App'; + +describe('App', () => { + const fetchMock = vi.fn(); + + beforeEach(() => { + fetchMock.mockReset(); + vi.stubGlobal('fetch', fetchMock); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it('shows qr code after successful start call', async () => { + fetchMock.mockResolvedValueOnce({ + ok: true, + json: async () => ({ + sessionId: 'session-1', + qrCodeDataUrl: 'data:image/png;base64,qr', + }), + }); + + render(<App />); + fireEvent.click( + screen.getByRole('button', { name: /sign in with digi-id/i }) + ); + + await waitFor(() => { + expect( + screen.getByRole('heading', { name: /scan the qr code/i }) + ).toBeInTheDocument(); + }); + expect(screen.getByAltText('Digi-ID QR Code')).toBeInTheDocument(); + }); + + it('transitions to success after polling confirms authentication', async () => { + fetchMock + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ + sessionId: 'session-2', + qrCodeDataUrl: 'data:image/png;base64,qr', + }), + }) + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ + status: 'success', + address: 'D123456789', + }), + }); + + render(<App />); + fireEvent.click( + screen.getByRole('button', { name: /sign in with digi-id/i }) + ); + + await waitFor(() => { + expect( + screen.getByText(/waiting for authentication/i) + ).toBeInTheDocument(); + }); + + await waitFor( + () => { + expect( + screen.getByText(/authentication successful/i) + ).toBeInTheDocument(); + }, + { timeout: 3000 } + ); + expect(screen.getByText('D123456789')).toBeInTheDocument(); + }); + + it('shows start errors in the initial state', async () => { + fetchMock.mockResolvedValueOnce({ + ok: false, + status: 500, + json: async () => ({ message: 'Backend unavailable' }), + }); + + render(<App />); + fireEvent.click( + screen.getByRole('button', { name: /sign in with digi-id/i }) + ); + + await waitFor(() => { + expect( + screen.getByText(/failed to initiate digi-id: backend unavailable/i) + ).toBeInTheDocument(); + }); + }); + + it('can cancel waiting flow and return to initial view', async () => { + fetchMock.mockResolvedValueOnce({ + ok: true, + json: async () => ({ + sessionId: 'session-3', + qrCodeDataUrl: 'data:image/png;base64,qr', + }), + }); + + render(<App />); + fireEvent.click( + screen.getByRole('button', { name: /sign in with digi-id/i }) + ); + + await waitFor(() => { + expect( + screen.getByRole('button', { name: /cancel/i }) + ).toBeInTheDocument(); + }); + + fireEvent.click(screen.getByRole('button', { name: /cancel/i })); + expect( + screen.getByRole('button', { name: /sign in with digi-id/i }) + ).toBeInTheDocument(); + }); +}); |
