Skip to content

Commit 062574c

Browse files
authored
fix: update app type declaration (#133)
* fix: solve type issues * refactor: remove unused react declarations * style: fix linter issues * chore: update typescript config and enable ci pipeline * refactor: update types import * refactor: consolidate permision constants in one place * refactor: implement feedback
1 parent e6079df commit 062574c

50 files changed

Lines changed: 304 additions & 1454 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ jobs:
2323
run: npm ci
2424
- name: Lint
2525
run: npm run lint
26+
- name: Types
27+
run: npm run types
2628
- name: Test
2729
run: npm run test
2830
- name: Build

src/authz-module/audit-user/index.test.tsx

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
import { AppContext } from '@edx/frontend-platform/react';
55
import userEvent from '@testing-library/user-event';
66
import { MemoryRouter, Route, Routes } from 'react-router-dom';
7-
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
7+
import { mockHttpClient, mockAppContext } from '@src/setupTest';
88
import { IntlProvider } from '@edx/frontend-platform/i18n';
99
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
1010
import { ToastManagerProvider } from '@src/components/ToastManager/ToastManagerContext';
@@ -76,24 +76,6 @@ const renderWithRouter = (route = '/audit/johndoe') => {
7676
},
7777
});
7878

79-
const mockAppContext = {
80-
authenticatedUser: {
81-
username: 'testuser',
82-
email: 'testuser@example.com',
83-
userId: 1,
84-
},
85-
config: {
86-
LMS_BASE_URL: 'http://localhost:18000',
87-
STUDIO_BASE_URL: 'http://localhost:18010',
88-
AUTHZ_MICROFRONTEND_URL: 'http://localhost:18012',
89-
ACCESS_TOKEN_COOKIE_NAME: 'edx-jwt-cookie-header-payload',
90-
BASE_URL: 'http://localhost:18012',
91-
ENVIRONMENT: 'test',
92-
LANGUAGE_PREFERENCE_COOKIE_NAME: 'openedx-language-preference',
93-
...process.env,
94-
},
95-
};
96-
9779
return render(
9880
<AppContext.Provider value={mockAppContext}>
9981
<QueryClientProvider client={queryClient}>
@@ -116,7 +98,7 @@ describe('AuditUserPage', () => {
11698
beforeEach(() => {
11799
jest.clearAllMocks();
118100
// Set up default mock behavior for useRevokeUserRoles
119-
mockRevokeUserRoles.mockImplementation((variables, { onSuccess }) => {
101+
mockRevokeUserRoles.mockImplementation((_variables, { onSuccess }) => {
120102
// Simulate successful deletion by default
121103
onSuccess({ errors: [], completed: ['role1'] });
122104
});
@@ -154,7 +136,7 @@ describe('AuditUserPage', () => {
154136
});
155137

156138
it('navigates to home if user is not found', async () => {
157-
(getAuthenticatedHttpClient as jest.Mock).mockReturnValue({
139+
mockHttpClient().mockReturnValue({
158140
get: jest
159141
.fn()
160142
.mockResolvedValueOnce({ data: null })
@@ -169,7 +151,7 @@ describe('AuditUserPage', () => {
169151
});
170152

171153
it('allows user to interact with Assign Role button', async () => {
172-
(getAuthenticatedHttpClient as jest.Mock).mockReturnValue({
154+
mockHttpClient().mockReturnValue({
173155
get: jest
174156
.fn()
175157
.mockResolvedValueOnce({ data: mockUser })
@@ -215,7 +197,7 @@ describe('AuditUserPage', () => {
215197
});
216198

217199
it('renders correct table headers', async () => {
218-
(getAuthenticatedHttpClient as jest.Mock).mockReturnValue({
200+
mockHttpClient().mockReturnValue({
219201
get: jest
220202
.fn()
221203
.mockResolvedValueOnce({ data: mockUser })
@@ -294,7 +276,7 @@ describe('AuditUserPage', () => {
294276
});
295277

296278
it('renders the breadcrumb navigation with home link', async () => {
297-
(getAuthenticatedHttpClient as jest.Mock).mockReturnValue({
279+
mockHttpClient().mockReturnValue({
298280
get: jest
299281
.fn()
300282
.mockResolvedValueOnce({ data: mockUser })
@@ -310,7 +292,7 @@ describe('AuditUserPage', () => {
310292
});
311293

312294
it('opens and closes the ConfirmDeletionModal when delete is clicked and cancel is pressed', async () => {
313-
(getAuthenticatedHttpClient as jest.Mock).mockReturnValue({
295+
mockHttpClient().mockReturnValue({
314296
get: jest
315297
.fn()
316298
.mockResolvedValueOnce({ data: mockUser })
@@ -342,7 +324,7 @@ describe('AuditUserPage', () => {
342324
});
343325

344326
it('calls onSave when confirming deletion in ConfirmDeletionModal', async () => {
345-
(getAuthenticatedHttpClient as jest.Mock).mockReturnValue({
327+
mockHttpClient().mockReturnValue({
346328
get: jest
347329
.fn()
348330
.mockResolvedValueOnce({ data: mockUser })
@@ -389,7 +371,7 @@ describe('AuditUserPage', () => {
389371
});
390372
});
391373

392-
(getAuthenticatedHttpClient as jest.Mock).mockReturnValue({
374+
mockHttpClient().mockReturnValue({
393375
get: jest
394376
.fn()
395377
.mockResolvedValueOnce({ data: mockUser })
@@ -424,12 +406,12 @@ describe('AuditUserPage', () => {
424406

425407
it('shows error toast with retry when role revocation fails', async () => {
426408
// Override mock for this specific test case
427-
mockRevokeUserRoles.mockImplementation((variables, { onError }) => {
409+
mockRevokeUserRoles.mockImplementation((_variables, { onError }) => {
428410
// Call onError immediately to simulate failure
429411
onError(new Error('Network error'));
430412
});
431413

432-
(getAuthenticatedHttpClient as jest.Mock).mockReturnValue({
414+
mockHttpClient().mockReturnValue({
433415
get: jest
434416
.fn()
435417
.mockResolvedValueOnce({ data: mockUser })
@@ -464,7 +446,7 @@ describe('AuditUserPage', () => {
464446
});
465447

466448
it('shows the extra warning when rolesCount is 1', async () => {
467-
(getAuthenticatedHttpClient as jest.Mock).mockReturnValue({
449+
mockHttpClient().mockReturnValue({
468450
get: jest
469451
.fn()
470452
.mockResolvedValueOnce({ data: mockUser })

src/authz-module/audit-user/index.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
} from 'react';
55
import { useIntl } from '@edx/frontend-platform/i18n';
66
import { AppContext } from '@edx/frontend-platform/react';
7-
import type { AppContextType } from '@edx/frontend-platform/react';
87
import {
98
Container, DataTable,
109
} from '@openedx/paragon';
@@ -23,7 +22,7 @@ import {
2322
} from '@src/authz-module/components/TableCells';
2423
import { useQuerySettings } from '@src/authz-module/hooks/useQuerySettings';
2524
import { useRevokeUserRoles, useUserAssignedRoles } from '@src/authz-module/data/hooks';
26-
import { RoleToDelete } from 'types';
25+
import { RoleToDelete } from '@src/types';
2726
import { useToastManager } from '@src/components/ToastManager/ToastManagerContext';
2827
import UserPermissions from '@src/authz-module/components/UserPermissions';
2928
import OrgFilter from '@src/authz-module/components/TableControlBar/OrgFilter';
@@ -37,7 +36,7 @@ const AuditUserPage = () => {
3736
const { formatMessage } = useIntl();
3837
const [columnsWithFiltersApplied, setColumnsWithFiltersApplied] = useState<string[]>([]);
3938
const { username } = useParams();
40-
const { authenticatedUser } = useContext(AppContext as React.Context<AppContextType>);
39+
const { authenticatedUser } = useContext(AppContext);
4140
const navigate = useNavigate();
4241
const {
4342
isLoading: isLoadingUser, data: user, isError: isErrorUser, error: errorUser,
@@ -112,10 +111,10 @@ const AuditUserPage = () => {
112111
Header: formatMessage(messages['authz.user.table.action.column.header']),
113112
Cell: createActionsCell({
114113
onClickDeleteButton: handleShowConfirmDeletionModal,
115-
isUserAuthenticatedPage: username === authenticatedUser.username,
114+
isUserAuthenticatedPage: username === authenticatedUser?.username,
116115
}),
117116
},
118-
], [authenticatedUser.username, formatMessage, handleShowConfirmDeletionModal, username]);
117+
], [authenticatedUser?.username, formatMessage, handleShowConfirmDeletionModal, username]);
119118

120119
const columns = useMemo(() => [
121120
{

src/authz-module/authz-home/index.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import React from 'react';
21
import { screen } from '@testing-library/react';
32
import { useAllRoleAssignments, useOrgs, useScopes } from '@src/authz-module/data/hooks';
43
import { renderWithAllProviders } from '@src/setupTest';

src/authz-module/components/AddRoleButton.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import React from 'react';
21
import { useIntl } from '@edx/frontend-platform/i18n';
32
import { Button } from '@openedx/paragon';
43
import { Plus } from '@openedx/paragon/icons';

src/authz-module/components/AuthZTitle.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { ReactNode } from 'react';
22
import { render, screen } from '@testing-library/react';
33
import userEvent from '@testing-library/user-event';
4-
import AuthZTitle, { AuthZTitleProps } from './AuthZTitle';
4+
import AuthZTitle from './AuthZTitle';
55

66
jest.mock('react-router-dom', () => ({
77
...jest.requireActual('react-router-dom'),
88
Link: ({ children, to }:{ children:ReactNode, to:string }) => <a href={to}>{children}</a>,
99
}));
1010

1111
describe('AuthZTitle', () => {
12-
const defaultProps: AuthZTitleProps = {
12+
const defaultProps = {
1313
activeLabel: 'Current Page',
1414
pageTitle: 'Page Title',
1515
pageSubtitle: 'Page Subtitle',

src/authz-module/components/PermissionTable.test.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const mockRoles: Role[] = [
1111
permissions: [],
1212
role: '',
1313
contextType: '',
14+
scope: '',
1415
},
1516
{
1617
name: 'Editor',
@@ -19,6 +20,7 @@ const mockRoles: Role[] = [
1920
permissions: [],
2021
role: '',
2122
contextType: '',
23+
scope: '',
2224
},
2325
{
2426
name: 'Viewer',
@@ -27,6 +29,7 @@ const mockRoles: Role[] = [
2729
permissions: [],
2830
role: '',
2931
contextType: '',
32+
scope: '',
3033
},
3134
];
3235

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,43 @@
11
import { Icon } from '@openedx/paragon';
2-
import { RolePermission } from 'types';
2+
import { PermissionItem } from '@src/types';
33
import ResourceTooltip from './ResourceTooltip';
44

5-
interface ExtendedRolePermission extends RolePermission {
6-
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
7-
}
8-
9-
interface PermissionItem {
10-
key: string;
11-
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
12-
label: string;
13-
description: string;
14-
perms: ExtendedRolePermission[];
15-
}
16-
175
interface RenderPermissionColumnProps {
186
items: PermissionItem[];
197
}
208

21-
const RenderPermissionColumn = ({ items }: RenderPermissionColumnProps) => items.map(({
22-
key, icon, label, description, perms,
23-
}) => (
24-
<div key={key} className="mb-4 col-12">
25-
<div className="d-flex align-items-center mb-2">
26-
<Icon src={icon} className="mr-2 text-primary" size="xs" />
27-
<h5 className="text-primary m-0">{label}</h5>
28-
<ResourceTooltip
29-
resourceGroup={{
30-
key, label, description, permissions: perms,
31-
}}
32-
/>
33-
</div>
34-
<ul className="mb-0 list-unstyled d-flex align-items-center">
35-
{perms.map((perm, index) => (
36-
<li
37-
key={perm.key}
38-
className={`d-flex align-items-center text-primary-400 ${index !== perms.length - 1 ? 'border-right pr-2' : ''
39-
} ${index !== 0 ? 'pl-2' : ''}`}
40-
>
41-
<Icon src={perm.icon} className="mr-2" size="xs" />
42-
<span className="text-primary small font-weight-light">
43-
{perm.label}
44-
</span>
45-
</li>
46-
))}
47-
</ul>
48-
</div>
49-
));
9+
const RenderPermissionColumn = ({ items }: RenderPermissionColumnProps) => (
10+
<>
11+
{items.map(({
12+
key, icon, label, description, perms,
13+
}) => (
14+
<div key={key} className="mb-4 col-12">
15+
<div className="d-flex align-items-center mb-2">
16+
<Icon src={icon} className="mr-2 text-primary" size="xs" />
17+
<h5 className="text-primary m-0">{label}</h5>
18+
<ResourceTooltip
19+
resourceGroup={{
20+
key, label, description, permissions: perms,
21+
}}
22+
/>
23+
</div>
24+
<ul className="mb-0 list-unstyled d-flex align-items-center">
25+
{perms.map((perm, index) => (
26+
<li
27+
key={perm.key}
28+
className={`d-flex align-items-center text-primary-400 ${index !== perms.length - 1 ? 'border-right pr-2' : ''
29+
} ${index !== 0 ? 'pl-2' : ''}`}
30+
>
31+
<Icon src={perm.icon} className="mr-2" size="xs" />
32+
<span className="text-primary small font-weight-light">
33+
{perm.label}
34+
</span>
35+
</li>
36+
))}
37+
</ul>
38+
</div>
39+
))}
40+
</>
41+
);
5042

5143
export default RenderPermissionColumn;

src/authz-module/components/RenderPermissionInLine.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,7 @@
11
import { Icon } from '@openedx/paragon';
2-
import { RolePermission } from 'types';
2+
import { PermissionItem } from '@src/types';
33
import ResourceTooltip from './ResourceTooltip';
44

5-
interface ExtendedRolePermission extends RolePermission {
6-
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
7-
}
8-
9-
interface PermissionItem {
10-
key: string;
11-
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
12-
label: string;
13-
description: string;
14-
perms: ExtendedRolePermission[];
15-
}
16-
175
interface RenderPermissionInLineProps {
186
items: PermissionItem[];
197
}

src/authz-module/components/ResourceTooltip.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import { Icon, OverlayTrigger, Popover } from '@openedx/paragon';
22
import { Info } from '@openedx/paragon/icons';
3-
import { PermissionsResourceGrouped, RoleResourceGroup } from '@src/types';
3+
import { PermissionMetadata } from '@src/types';
44

55
type ResourceTooltipProps = {
6-
resourceGroup: PermissionsResourceGrouped | RoleResourceGroup;
6+
resourceGroup: {
7+
key: string;
8+
label: string;
9+
description: string;
10+
permissions: PermissionMetadata[];
11+
};
712
};
813

914
const ResourceTooltip = ({ resourceGroup }:ResourceTooltipProps) => (
@@ -17,7 +22,7 @@ const ResourceTooltip = ({ resourceGroup }:ResourceTooltipProps) => (
1722
<p className="small">{resourceGroup.description}</p>
1823
<ul className="small">
1924
{resourceGroup.permissions.map(permission => (
20-
<li key={permission.key}><b>{permission.label.trim()}:</b> {permission.description}</li>
25+
<li key={permission.key}><b>{permission.label?.trim() ?? ''}:</b> {permission.description}</li>
2126
))}
2227
</ul>
2328
</Popover.Content>

0 commit comments

Comments
 (0)