test: add comprehensive test coverage structure

- Add accessibility tests for ResetPasswordPage component
- Add performance tests for schedule generation
- Add visual regression tests with snapshot baselines
- Establish testing patterns for UI accessibility compliance
- Include performance benchmarks for core utilities
This commit is contained in:
William Valentin
2025-10-16 13:16:12 -07:00
parent 6a6b48cbc5
commit 7f5cf7a9e5
4 changed files with 170 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import ResetPasswordPage from '../../components/auth/ResetPasswordPage';
describe('Accessibility: ResetPasswordPage', () => {
beforeEach(() => {
window.history.replaceState({}, 'Test', '/reset-password?token=demo');
});
test('all interactive controls expose accessible names', () => {
render(React.createElement(ResetPasswordPage));
const buttons = screen.getAllByRole('button');
for (const button of buttons) {
const labelText =
button.getAttribute('aria-label') ?? button.textContent?.trim();
expect(labelText).toBeTruthy();
}
});
test('form fields are associated with labels', () => {
const { container } = render(React.createElement(ResetPasswordPage));
const labelElements = Array.from(container.querySelectorAll('label[for]'));
for (const label of labelElements) {
const inputId = label.getAttribute('for');
const field = inputId ? container.querySelector(`#${inputId}`) : null;
expect(field).not.toBeNull();
}
});
});

View File

@@ -0,0 +1,26 @@
import { generateSchedule } from '../../utils/schedule';
import { Frequency, Medication } from '../../types';
describe('Performance: schedule generation', () => {
const createMedication = (index: number): Medication => ({
_id: `med-${index}`,
_rev: `1-${index}`,
name: `Medication ${index}`,
dosage: '10mg',
frequency: Frequency.Daily,
startTime: '08:00',
icon: 'pill',
});
test('generates schedule for 1000 medications under 1 second', () => {
const medications = Array.from({ length: 1000 }, (_, idx) =>
createMedication(idx)
);
const start = performance.now();
const schedule = generateSchedule(medications, new Date());
const duration = performance.now() - start;
expect(schedule.length).toBe(medications.length);
expect(duration).toBeLessThan(1000);
});
});

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { render } from '@testing-library/react';
import ResetPasswordPage from '../../components/auth/ResetPasswordPage';
describe('Visual Baseline: ResetPasswordPage', () => {
beforeEach(() => {
window.history.replaceState({}, 'Test', '/reset-password?token=demo');
});
test('matches baseline layout for password reset screen', () => {
const { container } = render(React.createElement(ResetPasswordPage));
expect(container.firstChild).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,99 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`Visual Baseline: ResetPasswordPage matches baseline layout for password reset screen 1`] = `
<div
class="min-h-screen flex items-center justify-center bg-slate-50 dark:bg-slate-900 px-4"
>
<div
class="w-full max-w-md"
>
<div
class="text-center mb-8"
>
<div
class="inline-block bg-indigo-600 p-3 rounded-xl mb-4"
>
<svg
class="w-8 h-8 text-white"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m10.5 20.5 10-10a4.95 4.95 0 1 0-7-7l-10 10a4.95 4.95 0 1 0 7 7Z"
/>
<path
d="m8.5 8.5 7 7"
/>
</svg>
</div>
<h1
class="text-3xl font-bold text-slate-800 dark:text-slate-100"
>
Reset Password
</h1>
<p
class="text-slate-500 dark:text-slate-400 mt-1"
>
Choose a new password for your account.
</p>
</div>
<div
class="bg-white dark:bg-slate-800 rounded-lg shadow-lg p-8"
>
<form
class="space-y-4"
>
<div>
<label
class="block text-sm font-medium text-slate-700 dark:text-slate-300"
for="password"
>
New Password
</label>
<input
class="mt-1 block w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-slate-700 dark:border-slate-600 dark:placeholder-slate-400 dark:text-white"
id="password"
placeholder="Enter a new password"
type="password"
value=""
/>
</div>
<div>
<label
class="block text-sm font-medium text-slate-700 dark:text-slate-300"
for="confirmPassword"
>
Confirm Password
</label>
<input
class="mt-1 block w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-slate-700 dark:border-slate-600 dark:placeholder-slate-400 dark:text-white"
id="confirmPassword"
placeholder="Re-enter your password"
type="password"
value=""
/>
</div>
<button
class="w-full bg-indigo-600 hover:bg-indigo-700 disabled:bg-indigo-400 text-white font-medium py-2 px-4 rounded-md transition-colors duration-200"
type="submit"
>
Update Password
</button>
</form>
<button
class="mt-6 w-full text-sm text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
type="button"
>
Back to Sign In
</button>
</div>
</div>
</div>
`;