@jmruthers/pace-core 0.5.61 → 0.5.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DataTable-5M6MV2VY.js → DataTable-7BER7PDS.js} +6 -6
- package/dist/{DataTable-DqDDvBfI.d.ts → DataTable-D15XipLZ.d.ts} +7 -0
- package/dist/{PublicLoadingSpinner-CrMOrhNz.d.ts → PublicLoadingSpinner-CXJ-W9wZ.d.ts} +2 -2
- package/dist/{chunk-44SAHU2N.js → chunk-2LPYEFXI.js} +5 -5
- package/dist/{chunk-XMTHMOOM.js → chunk-BTCA3ENN.js} +4 -4
- package/dist/{chunk-ESXTFEE6.js → chunk-C7GUF747.js} +3 -3
- package/dist/{chunk-W7PPXKTZ.js → chunk-CKNY7HYS.js} +2 -2
- package/dist/{chunk-5MLDIGHB.js → chunk-FVDOEGGG.js} +3 -3
- package/dist/{chunk-HFDYTSAP.js → chunk-QVEOQVD4.js} +3 -3
- package/dist/{chunk-XDXG6QVH.js → chunk-S66AJVI2.js} +13 -6
- package/dist/chunk-S66AJVI2.js.map +1 -0
- package/dist/{chunk-E4FPK232.js → chunk-T2MQY57J.js} +2 -2
- package/dist/{chunk-4ULBJNIT.js → chunk-T6HVDA24.js} +2 -2
- package/dist/{chunk-STT7INZR.js → chunk-ULBI5JGB.js} +2 -1
- package/dist/{chunk-CGSYCF2W.js → chunk-VTJ5HCZB.js} +2 -2
- package/dist/components.d.ts +82 -5
- package/dist/components.js +258 -9
- package/dist/components.js.map +1 -1
- package/dist/hooks.d.ts +3 -2
- package/dist/hooks.js +5 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +11 -11
- package/dist/{organisation-DD0yBbGU.d.ts → organisation-t-vvQC3g.d.ts} +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +4 -4
- package/dist/rbac/index.js +6 -6
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-Cu6oKazv.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +2 -63
- package/dist/useToast-Bm6TnSK-.d.ts +63 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +44 -18
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +3 -3
- package/docs/implementation-guides/data-tables.md +67 -0
- package/package.json +1 -1
- package/src/components/DataTable/DataTable.tsx +13 -0
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +414 -0
- package/src/components/DataTable/components/DataTableCore.tsx +19 -2
- package/src/components/DataTable/types.ts +9 -0
- package/src/components/Dialog/examples/__tests__/SmartDialogExample.unit.test.tsx +151 -0
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +287 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +861 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +628 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +777 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +901 -0
- package/src/components/Toast/index.ts +2 -0
- package/src/components/index.ts +15 -0
- package/src/hooks/useFileReference.ts +37 -0
- package/src/styles/base.css +208 -0
- package/src/styles/semantic.css +24 -0
- package/dist/chunk-XDXG6QVH.js.map +0 -1
- /package/dist/{DataTable-5M6MV2VY.js.map → DataTable-7BER7PDS.js.map} +0 -0
- /package/dist/{chunk-44SAHU2N.js.map → chunk-2LPYEFXI.js.map} +0 -0
- /package/dist/{chunk-XMTHMOOM.js.map → chunk-BTCA3ENN.js.map} +0 -0
- /package/dist/{chunk-ESXTFEE6.js.map → chunk-C7GUF747.js.map} +0 -0
- /package/dist/{chunk-W7PPXKTZ.js.map → chunk-CKNY7HYS.js.map} +0 -0
- /package/dist/{chunk-5MLDIGHB.js.map → chunk-FVDOEGGG.js.map} +0 -0
- /package/dist/{chunk-HFDYTSAP.js.map → chunk-QVEOQVD4.js.map} +0 -0
- /package/dist/{chunk-E4FPK232.js.map → chunk-T2MQY57J.js.map} +0 -0
- /package/dist/{chunk-4ULBJNIT.js.map → chunk-T6HVDA24.js.map} +0 -0
- /package/dist/{chunk-STT7INZR.js.map → chunk-ULBI5JGB.js.map} +0 -0
- /package/dist/{chunk-CGSYCF2W.js.map → chunk-VTJ5HCZB.js.map} +0 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
4
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
5
|
+
|
|
6
|
+
// Mock React Router hooks
|
|
7
|
+
const mockNavigate = vi.fn();
|
|
8
|
+
vi.mock('react-router-dom', async () => {
|
|
9
|
+
const actual = await vi.importActual('react-router-dom');
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
useNavigate: () => mockNavigate,
|
|
13
|
+
Outlet: () => <div data-testid="mock-outlet">Mock Outlet Content</div>
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Mock UnifiedAuthProvider
|
|
18
|
+
const mockSignOut = vi.fn().mockResolvedValue({ error: null });
|
|
19
|
+
const mockUpdatePassword = vi.fn().mockResolvedValue({ error: null });
|
|
20
|
+
const mockUser = {
|
|
21
|
+
id: 'test-user-id',
|
|
22
|
+
email: 'test@example.com',
|
|
23
|
+
user_metadata: { display_name: 'Test User' }
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
vi.mock('../../../providers/UnifiedAuthProvider', () => ({
|
|
27
|
+
useUnifiedAuth: () => ({
|
|
28
|
+
user: mockUser,
|
|
29
|
+
signOut: mockSignOut,
|
|
30
|
+
updatePassword: mockUpdatePassword
|
|
31
|
+
})
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
// Mock OrganisationProvider
|
|
35
|
+
const mockOrganisation = {
|
|
36
|
+
id: 'test-org-id',
|
|
37
|
+
name: 'Test Organisation',
|
|
38
|
+
display_name: 'Test Organisation',
|
|
39
|
+
description: 'Test organisation for testing',
|
|
40
|
+
subscription_tier: 'basic',
|
|
41
|
+
settings: {},
|
|
42
|
+
is_active: true,
|
|
43
|
+
created_at: '2023-01-01T00:00:00Z',
|
|
44
|
+
updated_at: '2023-01-01T00:00:00Z'
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const mockOrganisationContext = {
|
|
48
|
+
selectedOrganisation: mockOrganisation,
|
|
49
|
+
organisations: [mockOrganisation],
|
|
50
|
+
userMemberships: [{
|
|
51
|
+
id: 'test-membership-id',
|
|
52
|
+
user_id: 'test-user-id',
|
|
53
|
+
organisation_id: 'test-org-id',
|
|
54
|
+
role: 'org_admin',
|
|
55
|
+
granted_at: '2023-01-01T00:00:00Z',
|
|
56
|
+
status: 'active' as const,
|
|
57
|
+
created_at: '2023-01-01T00:00:00Z',
|
|
58
|
+
updated_at: '2023-01-01T00:00:00Z'
|
|
59
|
+
}],
|
|
60
|
+
isLoading: false,
|
|
61
|
+
error: null,
|
|
62
|
+
hasValidOrganisationContext: true,
|
|
63
|
+
setSelectedOrganisation: vi.fn(),
|
|
64
|
+
switchOrganisation: vi.fn().mockResolvedValue(undefined),
|
|
65
|
+
getUserRole: vi.fn().mockReturnValue('member'),
|
|
66
|
+
validateOrganisationAccess: vi.fn().mockReturnValue(true),
|
|
67
|
+
ensureOrganisationContext: vi.fn().mockReturnValue(mockOrganisation),
|
|
68
|
+
refreshOrganisations: vi.fn().mockResolvedValue(undefined),
|
|
69
|
+
getPrimaryOrganisation: vi.fn().mockReturnValue(mockOrganisation),
|
|
70
|
+
isOrganisationSecure: vi.fn().mockReturnValue(true)
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
vi.mock('../../../providers/OrganisationProvider', () => ({
|
|
74
|
+
OrganisationProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
|
75
|
+
useOrganisations: () => mockOrganisationContext
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
// Mock usePermissionCache
|
|
79
|
+
vi.mock('../../../hooks/usePermissionCache', () => ({
|
|
80
|
+
usePermissionCache: () => ({
|
|
81
|
+
checkPermission: vi.fn().mockResolvedValue(true),
|
|
82
|
+
hasPermission: vi.fn().mockReturnValue(true),
|
|
83
|
+
getPermissions: vi.fn().mockReturnValue(['read', 'write']),
|
|
84
|
+
refreshPermissions: vi.fn().mockResolvedValue(undefined)
|
|
85
|
+
})
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
// Mock child components with proper accessibility attributes
|
|
89
|
+
vi.mock('../../Header', () => ({
|
|
90
|
+
Header: vi.fn(({ appName, user, onSignOut, onChangePassword, onNavigate, currentPath }) => (
|
|
91
|
+
<header data-testid="mock-header" role="banner" aria-label={`${appName ?? 'Test App'} header`}>
|
|
92
|
+
<div data-testid="app-name" aria-label="Application name">{appName ?? 'Test App'}</div>
|
|
93
|
+
<div data-testid="user-info" aria-label="User information">
|
|
94
|
+
{user?.user_metadata?.display_name || user?.email}
|
|
95
|
+
</div>
|
|
96
|
+
<button
|
|
97
|
+
data-testid="sign-out-button"
|
|
98
|
+
onClick={() => onSignOut()}
|
|
99
|
+
aria-label="Sign out of application"
|
|
100
|
+
>
|
|
101
|
+
Sign Out
|
|
102
|
+
</button>
|
|
103
|
+
<button
|
|
104
|
+
data-testid="change-password-button"
|
|
105
|
+
onClick={() => onChangePassword('newpassword123')}
|
|
106
|
+
aria-label="Change password"
|
|
107
|
+
>
|
|
108
|
+
Change Password
|
|
109
|
+
</button>
|
|
110
|
+
<button
|
|
111
|
+
data-testid="navigate-button"
|
|
112
|
+
onClick={() => onNavigate({ id: 'home', label: 'Home', href: '/' })}
|
|
113
|
+
aria-label="Navigate to home"
|
|
114
|
+
>
|
|
115
|
+
Navigate
|
|
116
|
+
</button>
|
|
117
|
+
<div data-testid="current-path" aria-label="Current page path">{currentPath}</div>
|
|
118
|
+
</header>
|
|
119
|
+
))
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
vi.mock('../../Footer', () => ({
|
|
123
|
+
Footer: vi.fn(() => (
|
|
124
|
+
<footer data-testid="mock-footer" role="contentinfo" aria-label="Application footer">
|
|
125
|
+
Mock Footer
|
|
126
|
+
</footer>
|
|
127
|
+
))
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
// Mock window.location
|
|
131
|
+
Object.defineProperty(window, 'location', {
|
|
132
|
+
value: {
|
|
133
|
+
pathname: '/test-path'
|
|
134
|
+
},
|
|
135
|
+
writable: true
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
import { PaceAppLayout } from '../PaceAppLayout';
|
|
139
|
+
|
|
140
|
+
// Wrapper component to provide Router context
|
|
141
|
+
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
142
|
+
<BrowserRouter>
|
|
143
|
+
{children}
|
|
144
|
+
</BrowserRouter>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
describe('PaceAppLayout Accessibility', () => {
|
|
148
|
+
beforeEach(() => {
|
|
149
|
+
vi.clearAllMocks();
|
|
150
|
+
mockNavigate.mockClear();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('Semantic HTML Structure', () => {
|
|
154
|
+
it('renders with proper semantic HTML elements', () => {
|
|
155
|
+
render(
|
|
156
|
+
<TestWrapper>
|
|
157
|
+
<PaceAppLayout appName="Test App" />
|
|
158
|
+
</TestWrapper>
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// Check for proper semantic elements
|
|
162
|
+
expect(screen.getByRole('banner')).toBeInTheDocument();
|
|
163
|
+
expect(screen.getByRole('contentinfo')).toBeInTheDocument();
|
|
164
|
+
expect(screen.getByRole('main')).toBeInTheDocument();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('provides proper main content area', () => {
|
|
168
|
+
render(
|
|
169
|
+
<TestWrapper>
|
|
170
|
+
<PaceAppLayout appName="Test App" />
|
|
171
|
+
</TestWrapper>
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const main = screen.getByRole('main');
|
|
175
|
+
expect(main).toBeInTheDocument();
|
|
176
|
+
expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('ARIA Attributes and Labels', () => {
|
|
181
|
+
it('provides proper ARIA labels for header elements', () => {
|
|
182
|
+
render(
|
|
183
|
+
<TestWrapper>
|
|
184
|
+
<PaceAppLayout appName="Test App" />
|
|
185
|
+
</TestWrapper>
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(screen.getByLabelText('Application name')).toBeInTheDocument();
|
|
189
|
+
expect(screen.getByLabelText('User information')).toBeInTheDocument();
|
|
190
|
+
expect(screen.getByLabelText('Current page path')).toBeInTheDocument();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('provides proper ARIA labels for interactive elements', () => {
|
|
194
|
+
render(
|
|
195
|
+
<TestWrapper>
|
|
196
|
+
<PaceAppLayout appName="Test App" />
|
|
197
|
+
</TestWrapper>
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
expect(screen.getByLabelText('Sign out of application')).toBeInTheDocument();
|
|
201
|
+
expect(screen.getByLabelText('Change password')).toBeInTheDocument();
|
|
202
|
+
expect(screen.getByLabelText('Navigate to home')).toBeInTheDocument();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('provides proper ARIA labels for footer', () => {
|
|
206
|
+
render(
|
|
207
|
+
<TestWrapper>
|
|
208
|
+
<PaceAppLayout appName="Test App" />
|
|
209
|
+
</TestWrapper>
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
expect(screen.getByLabelText('Application footer')).toBeInTheDocument();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe('Screen Reader Support', () => {
|
|
217
|
+
it('provides meaningful text for screen readers', () => {
|
|
218
|
+
render(
|
|
219
|
+
<TestWrapper>
|
|
220
|
+
<PaceAppLayout appName="Test App" />
|
|
221
|
+
</TestWrapper>
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Check that user information is properly displayed
|
|
225
|
+
expect(screen.getByLabelText('User information')).toHaveTextContent('Test User');
|
|
226
|
+
|
|
227
|
+
// Check that app name is properly displayed
|
|
228
|
+
expect(screen.getByLabelText('Application name')).toHaveTextContent('Test App');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('provides current path information for screen readers', () => {
|
|
232
|
+
render(
|
|
233
|
+
<TestWrapper>
|
|
234
|
+
<PaceAppLayout appName="Test App" />
|
|
235
|
+
</TestWrapper>
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
expect(screen.getByLabelText('Current page path')).toHaveTextContent('/test-path');
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('Keyboard Navigation', () => {
|
|
243
|
+
it('provides proper focus management', () => {
|
|
244
|
+
render(
|
|
245
|
+
<TestWrapper>
|
|
246
|
+
<PaceAppLayout appName="Test App" />
|
|
247
|
+
</TestWrapper>
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// Check that buttons can receive focus
|
|
251
|
+
const signOutButton = screen.getByLabelText('Sign out of application');
|
|
252
|
+
signOutButton.focus();
|
|
253
|
+
expect(document.activeElement).toBe(signOutButton);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('Color and Contrast', () => {
|
|
258
|
+
it('ensures proper contrast ratios are maintained', () => {
|
|
259
|
+
render(
|
|
260
|
+
<TestWrapper>
|
|
261
|
+
<PaceAppLayout appName="Test App" />
|
|
262
|
+
</TestWrapper>
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// This would typically be tested with a visual regression test
|
|
266
|
+
// or with a tool like axe-core, but we can verify the structure
|
|
267
|
+
const main = screen.getByRole('main');
|
|
268
|
+
expect(main).toBeInTheDocument();
|
|
269
|
+
expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('Responsive Design Accessibility', () => {
|
|
274
|
+
it('maintains accessibility on different screen sizes', () => {
|
|
275
|
+
render(
|
|
276
|
+
<TestWrapper>
|
|
277
|
+
<PaceAppLayout appName="Test App" />
|
|
278
|
+
</TestWrapper>
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Check that the layout structure remains accessible
|
|
282
|
+
const main = screen.getByRole('main');
|
|
283
|
+
expect(main).toBeInTheDocument();
|
|
284
|
+
expect(main).toHaveClass('px-4', 'w-[min(var(--app-width),100%)]', 'mx-auto', 'py-8');
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|