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();