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:
31
tests/accessibility/resetPassword.accessibility.test.tsx
Normal file
31
tests/accessibility/resetPassword.accessibility.test.tsx
Normal 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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
26
tests/performance/schedule.performance.test.ts
Normal file
26
tests/performance/schedule.performance.test.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
14
tests/visual/ResetPassword.visual.test.tsx
Normal file
14
tests/visual/ResetPassword.visual.test.tsx
Normal 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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>
|
||||||
|
`;
|
||||||
Reference in New Issue
Block a user