From eafed7a1200dd412dcb0bd88f83bac65bf7b21ef Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 30 May 2026 11:46:20 +0000
Subject: [PATCH 1/2] Add coverage-focused frontend tests
---
frontend/src/RespondentLayout.test.tsx | 17 ++++
frontend/src/admin/AdminApp.test.tsx | 116 +++++++++++++++++++++++++
frontend/src/ui/Primitives.test.tsx | 51 +++++++++++
3 files changed, 184 insertions(+)
create mode 100644 frontend/src/RespondentLayout.test.tsx
create mode 100644 frontend/src/admin/AdminApp.test.tsx
create mode 100644 frontend/src/ui/Primitives.test.tsx
diff --git a/frontend/src/RespondentLayout.test.tsx b/frontend/src/RespondentLayout.test.tsx
new file mode 100644
index 0000000..b186f5e
--- /dev/null
+++ b/frontend/src/RespondentLayout.test.tsx
@@ -0,0 +1,17 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+
+import { RespondentLayout } from './RespondentLayout';
+
+describe('RespondentLayout', () => {
+ it('renders the Stele header and page content', () => {
+ render(
+
+ Survey content
+ ,
+ );
+
+ expect(screen.getByText('Stele')).toBeInTheDocument();
+ expect(screen.getByText('Survey content')).toBeInTheDocument();
+ });
+});
diff --git a/frontend/src/admin/AdminApp.test.tsx b/frontend/src/admin/AdminApp.test.tsx
new file mode 100644
index 0000000..30565d1
--- /dev/null
+++ b/frontend/src/admin/AdminApp.test.tsx
@@ -0,0 +1,116 @@
+import { render, screen } from '@testing-library/react';
+import { MemoryRouter, Outlet, Route, Routes } from 'react-router-dom';
+import type { ReactNode } from 'react';
+import { afterEach, describe, expect, it, vi } from 'vitest';
+
+import type { User } from '../api';
+import AdminApp from './AdminApp';
+
+const authState = vi.hoisted(() => ({
+ user: null as User | null,
+}));
+
+vi.mock('../auth/AuthContext', () => ({
+ AuthProvider: ({ children }: { children: ReactNode }) => <>{children}>,
+ useAuth: () => ({ user: authState.user }),
+}));
+
+vi.mock('../auth/RequireAuth', () => ({
+ RequireAuth: () => ,
+}));
+
+vi.mock('./AdminLayout', () => ({
+ AdminLayout: () => ,
+}));
+
+vi.mock('./LoginView', () => ({
+ LoginView: () =>
Login view
,
+}));
+
+vi.mock('./SurveyListView', () => ({
+ SurveyListView: () => Survey list view
,
+}));
+
+vi.mock('./PiiReviewView', () => ({
+ PiiReviewView: () => PII review view
,
+}));
+
+vi.mock('./MyDbAccessView', () => ({
+ MyDbAccessView: () => My access view
,
+}));
+
+vi.mock('./SurveyEditorView', () => ({
+ SurveyEditorView: () => Survey editor view
,
+}));
+
+vi.mock('./GdprView', () => ({
+ GdprView: () => GDPR view
,
+}));
+
+vi.mock('./EtlView', () => ({
+ EtlView: () => ETL view
,
+}));
+
+vi.mock('./UsersView', () => ({
+ UsersView: () => Users view
,
+}));
+
+vi.mock('./DbCredentialsView', () => ({
+ DbCredentialsView: () => DB credentials view
,
+}));
+
+afterEach(() => {
+ authState.user = null;
+ vi.clearAllMocks();
+});
+
+function setRoles(roles: string[]) {
+ authState.user = {
+ id: 1,
+ email: 'user@example.com',
+ roles,
+ disabled: false,
+ created_at: 't',
+ };
+}
+
+function renderAt(path: string) {
+ return render(
+
+
+ } />
+
+ ,
+ );
+}
+
+describe('AdminApp', () => {
+ it('renders the login route without requiring auth', async () => {
+ renderAt('/admin/login');
+ expect(await screen.findByText('Login view')).toBeInTheDocument();
+ });
+
+ it('lands authors on the survey list', async () => {
+ setRoles(['researcher']);
+ renderAt('/admin');
+ expect(await screen.findByText('Survey list view')).toBeInTheDocument();
+ });
+
+ it('redirects reviewers to the pii review queue', async () => {
+ setRoles(['reviewer']);
+ renderAt('/admin');
+ expect(await screen.findByText('PII review view')).toBeInTheDocument();
+ });
+
+ it('redirects analysts to my database access', async () => {
+ setRoles(['analyst']);
+ renderAt('/admin');
+ expect(await screen.findByText('My access view')).toBeInTheDocument();
+ });
+
+ it('falls back to survey list for unknown role sets', async () => {
+ setRoles(['viewer']);
+ renderAt('/admin');
+ expect(await screen.findByText('Survey list view')).toBeInTheDocument();
+ });
+});
diff --git a/frontend/src/ui/Primitives.test.tsx b/frontend/src/ui/Primitives.test.tsx
new file mode 100644
index 0000000..4913616
--- /dev/null
+++ b/frontend/src/ui/Primitives.test.tsx
@@ -0,0 +1,51 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+
+import { Alert } from './Alert';
+import { Card, CardBody } from './Card';
+import { Field } from './Field';
+import { PageHeader } from './PageHeader';
+
+describe('UI primitives', () => {
+ it('renders alert tones with the expected aria roles', () => {
+ const { rerender } = render(Error);
+ expect(screen.getByRole('alert')).toHaveTextContent('Error');
+
+ rerender(Saved);
+ expect(screen.getByRole('status')).toHaveTextContent('Saved');
+ });
+
+ it('renders card and card body content with provided attributes', () => {
+ render(
+
+ Body
+ ,
+ );
+
+ const card = screen.getByTestId('card');
+ expect(card).toHaveTextContent('Body');
+ expect(card).toHaveClass('custom-card');
+ expect(screen.getByText('Body')).toHaveClass('custom-body');
+ });
+
+ it('associates field labels with inputs and renders hints', () => {
+ render();
+
+ expect(screen.getByLabelText('Email')).toHaveAttribute('id', 'email-field');
+ expect(screen.getByText('Use your work email')).toBeInTheDocument();
+ });
+
+ it('renders page headers with optional subtitle and actions', () => {
+ render(
+ Add user}
+ />,
+ );
+
+ expect(screen.getByRole('heading', { level: 1, name: 'Users' })).toBeInTheDocument();
+ expect(screen.getByText('Manage accounts')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Add user' })).toBeInTheDocument();
+ });
+});
From a8325b02f1b7001a7705a5e8766570f9b0845b5e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 30 May 2026 11:47:19 +0000
Subject: [PATCH 2/2] Validate new frontend coverage tests
---
frontend/src/ui/Primitives.test.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/frontend/src/ui/Primitives.test.tsx b/frontend/src/ui/Primitives.test.tsx
index 4913616..a1655e9 100644
--- a/frontend/src/ui/Primitives.test.tsx
+++ b/frontend/src/ui/Primitives.test.tsx
@@ -29,7 +29,9 @@ describe('UI primitives', () => {
});
it('associates field labels with inputs and renders hints', () => {
- render();
+ render(
+ ,
+ );
expect(screen.getByLabelText('Email')).toHaveAttribute('id', 'email-field');
expect(screen.getByText('Use your work email')).toBeInTheDocument();