diff --git a/package-lock.json b/package-lock.json index dc3eaaac..22cf67a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11832,7 +11832,6 @@ "version": "19.2.15", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -11842,7 +11841,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -14426,7 +14425,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, "node_modules/cwd": { @@ -22786,9 +22784,9 @@ } }, "node_modules/postcss": { - "version": "8.5.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz", - "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ { @@ -22806,7 +22804,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -26009,7 +26007,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/tests/unit/screens/SettingsScreen.test.tsx b/tests/unit/screens/SettingsScreen.test.tsx index bc41ebcb..4b63ecdd 100644 --- a/tests/unit/screens/SettingsScreen.test.tsx +++ b/tests/unit/screens/SettingsScreen.test.tsx @@ -1,5 +1,11 @@ import { server } from '@tests/mocks/node'; -import { fireEvent, render, screen, userEvent } from '@tests/mocks/test-utils'; +import { + fireEvent, + render, + screen, + userEvent, + within, +} from '@tests/mocks/test-utils'; import { HttpResponse, http } from 'msw'; import { beforeEach, describe, expect, it, vi } from 'vitest'; @@ -174,9 +180,11 @@ describe('SettingsScreen', () => { // Wait for results to render await screen.findByText('Results'); - // Click copy button (there are two — URL and code) - const copyButtons = screen.getAllByRole('button', { name: 'Copy' }); - await user.click(copyButtons[0]!); + // Scope to the invite-URL row and click its Copy button + const inviteUrlRow = screen.getByText('Invite URL').closest('div')!; + await user.click( + within(inviteUrlRow).getByRole('button', { name: 'Copy' }), + ); expect(writeText).toHaveBeenCalledWith( expect.stringContaining('/invite?code='), @@ -186,6 +194,38 @@ describe('SettingsScreen', () => { expect(screen.getByText('Copied!')).toBeInTheDocument(); }); + it('copies invite code to clipboard', async () => { + const writeText = vi.fn().mockResolvedValue(undefined); + navigator.clipboard.writeText = writeText; + + const user = userEvent.setup(); + render(); + + // Fill form and generate + await user.type( + screen.getByLabelText('Remote Archive URL'), + 'https://archive.example.com', + ); + await user.type(screen.getByLabelText('Bearer Token'), 'my-secret-token'); + await user.click(screen.getByRole('button', { name: 'Generate Invite' })); + + // Wait for results to render + await screen.findByText('Results'); + + // Scope to the invite-code row and click its Copy button + const inviteCodeRow = screen.getByText('Invite Code').closest('div')!; + await user.click( + within(inviteCodeRow).getByRole('button', { name: 'Copy' }), + ); + + expect(writeText).toHaveBeenCalledWith( + expect.stringContaining('mock-encrypted-code-'), + ); + + // Should show "Copied!" feedback + expect(screen.getByText('Copied!')).toBeInTheDocument(); + }); + describe('Backup & Restore', () => { it('renders Backup & Restore section heading', () => { render();