Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import '@testing-library/jest-dom'
import 'jest-axe/extend-expect'

// Mock Next.js router
jest.mock('next/navigation', () => ({
Expand Down
153 changes: 153 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"react-dom": "19.1.0"
},
"devDependencies": {
"@axe-core/react": "^4.10.2",
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@testing-library/jest-dom": "^6.6.3",
Expand All @@ -31,6 +32,7 @@
"eslint": "^9",
"eslint-config-next": "15.4.4",
"jest": "^30.0.5",
"jest-axe": "^10.0.0",
"jest-environment-jsdom": "^30.0.5",
"tailwindcss": "^4",
"typescript": "^5"
Expand Down
119 changes: 119 additions & 0 deletions src/__tests__/accessibility.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React from 'react';
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import Home from '../app/page';

expect.extend(toHaveNoViolations);

describe('Accessibility Tests', () => {
describe('Home Page', () => {
it('should not have any accessibility violations on initial render', async () => {
const { container } = render(<Home />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should not have accessibility violations with users added', async () => {
const { container } = render(<Home />);

// The axe check will run on the default state which may include
// example data or other loaded content
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should not have accessibility violations with example data loaded', async () => {
// This test checks accessibility with the example data that gets loaded
// The component renders example data in various states
const { container } = render(<Home />);

const results = await axe(container);
expect(results).toHaveNoViolations();
});
});

describe('Form Accessibility', () => {
it('should have proper form labels and structure', async () => {
const { container } = render(<Home />);

// Test the accessibility of form elements specifically
const results = await axe(container, {
rules: {
'form-field-multiple-labels': { enabled: true },
'label': { enabled: true },
'label-title-only': { enabled: true },
'input-button-name': { enabled: true }
}
});

expect(results).toHaveNoViolations();
});
});

describe('Table Accessibility', () => {
it('should have proper table structure when expenses are displayed', async () => {
const { container } = render(<Home />);

// Focus on table-related accessibility rules
const results = await axe(container, {
rules: {
'table-fake-caption': { enabled: true },
'td-headers-attr': { enabled: true },
'th-has-data-cells': { enabled: true },
'scope-attr-valid': { enabled: true }
}
});

expect(results).toHaveNoViolations();
});
});

describe('Color and Contrast', () => {
it('should meet color contrast requirements', async () => {
const { container } = render(<Home />);

const results = await axe(container, {
rules: {
'color-contrast': { enabled: true },
'color-contrast-enhanced': { enabled: true }
}
});

expect(results).toHaveNoViolations();
});
});

describe('Keyboard Navigation', () => {
it('should support keyboard navigation', async () => {
const { container } = render(<Home />);

const results = await axe(container, {
rules: {
'focus-order-semantics': { enabled: true },
'tabindex': { enabled: true },
'accesskeys': { enabled: true }
}
});

expect(results).toHaveNoViolations();
});
});

describe('Semantic Structure', () => {
it('should have proper heading hierarchy and semantic structure', async () => {
const { container } = render(<Home />);

const results = await axe(container, {
rules: {
'heading-order': { enabled: true },
'page-has-heading-one': { enabled: true },
'bypass': { enabled: true },
'landmark-one-main': { enabled: true },
'region': { enabled: true }
}
});

expect(results).toHaveNoViolations();
});
});
});
Loading