style: update linter validation and fix lint issues

This commit is contained in:
Diana Olarte
2025-09-29 18:18:03 +10:00
committed by Adolfo R. Brandes
parent 30dd1604d1
commit 66fb56e198
20 changed files with 94 additions and 109 deletions

View File

@@ -12,8 +12,8 @@
"scripts": {
"build": "fedx-scripts webpack",
"i18n_extract": "fedx-scripts formatjs extract",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx .",
"lint": "fedx-scripts eslint --ext .js --ext .jsx --ext .ts --ext .tsx .",
"lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx --ext .ts --ext .tsx .",
"snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
"start:with-theme": "paragon install-theme && npm start && npm install",

View File

@@ -1,4 +1,4 @@
export const ROUTES = {
LIBRARIES_TEAM_PATH: '/libraries/:libraryId',
LIBRARIES_USER_PATH: '/libraries/user/:username'
};
LIBRARIES_USER_PATH: '/libraries/user/:username',
};

View File

@@ -14,7 +14,6 @@ export const getTeamMembers = async (object: string): Promise<TeamMember[]> => {
return camelCaseObject(data.results);
};
// TODO: this should be replaced in the future with Console API
export const getLibrary = async (libraryId: string): Promise<LibraryMetadata> => {
const { data } = await getAuthenticatedHttpClient().get(getStudioApiUrl(`/api/libraries/v2/${libraryId}/`));

View File

@@ -113,9 +113,9 @@ describe('useLibrary', () => {
const wrapper = createWrapper();
try {
act(()=>{
act(() => {
renderHook(() => useLibrary('lib123'), { wrapper });
})
});
} catch (e) {
expect(e).toEqual(new Error('Not found'));
}

View File

@@ -3,7 +3,6 @@ import { appId } from '@src/constants';
import { LibraryMetadata, TeamMember } from '@src/types';
import { getLibrary, getTeamMembers } from './api';
const authzQueryKeys = {
all: [appId, 'authz'] as const,
teamMembers: (object: string) => [...authzQueryKeys.all, 'teamMembers', object] as const,
@@ -36,10 +35,8 @@ export const useTeamMembers = (object: string) => useQuery<TeamMember[], Error>(
* const { data } = useLibrary('lib:123',);
*
*/
export const useLibrary = (libraryId: string) => {
return useSuspenseQuery<LibraryMetadata, Error>({
queryKey: authzQueryKeys.library(libraryId),
queryFn: () => getLibrary(libraryId),
retry: false,
});
}
export const useLibrary = (libraryId: string) => useSuspenseQuery<LibraryMetadata, Error>({
queryKey: authzQueryKeys.library(libraryId),
queryFn: () => getLibrary(libraryId),
retry: false,
});

View File

@@ -4,22 +4,16 @@ import { MemoryRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import AuthZModule from './index';
jest.mock('./libraries-manager/LibrariesTeamManager', () => {
return lazy(() =>
new Promise<{ default: ComponentType<any> }>(resolve =>
setTimeout(() => resolve({ default: () => <div data-testid="libraries-manager">Loaded</div> }), 100)
)
);
});
// eslint-disable-next-line no-promise-executor-return
jest.mock('./libraries-manager/LibrariesTeamManager', () => lazy(() => new Promise<{ default: ComponentType<any> }>(resolve => setTimeout(() => resolve({ default: () => <div data-testid="libraries-manager">Loaded</div> }), 100))));
const createTestQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
const createTestQueryClient = () => new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
});
},
});
describe('AuthZModule', () => {
it('renders LoadingPage then LibrariesTeamManager when route matches', async () => {
@@ -31,7 +25,7 @@ describe('AuthZModule', () => {
<MemoryRouter initialEntries={[path]}>
<AuthZModule />
</MemoryRouter>
</QueryClientProvider>
</QueryClientProvider>,
);
expect(screen.getByTestId('loading-page')).toBeInTheDocument();

View File

@@ -2,7 +2,7 @@ import { Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
import { ErrorBoundary } from '@edx/frontend-platform/react';
import LoadingPage from '@src/components/LoadingPage';
import { LibrariesTeamManager } from './libraries-manager/';
import { LibrariesTeamManager } from './libraries-manager';
import { ROUTES } from './constants';
import './index.scss';

View File

@@ -1,9 +1,9 @@
import { screen } from '@testing-library/react';
import LibrariesTeamManager from './LibrariesTeamManager';
import { useLibraryAuthZ } from './context';
import { renderWrapper } from '@src/setupTest';
import { initializeMockApp } from '@edx/frontend-platform/testing';
import { useLibrary } from '@src/authz-module/data/hooks';
import { useLibraryAuthZ } from './context';
import LibrariesTeamManager from './LibrariesTeamManager';
jest.mock('./context', () => {
const actual = jest.requireActual('./context');
@@ -28,9 +28,9 @@ describe('LibrariesTeamManager', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
username: 'admin'
}
})
username: 'admin',
},
});
mockedUseLibraryAuthZ.mockReturnValue({
libraryId: 'lib-001',
libraryName: 'Mock Library',

View File

@@ -1,8 +1,8 @@
import { useIntl } from '@edx/frontend-platform/i18n';
import { Tab, Tabs } from '@openedx/paragon';
import { useLibrary } from '@src/authz-module/data/hooks';
import TeamTable from './components/TeamTable';
import AuthZLayout from '../components/AuthZLayout';
import { useLibrary } from '@src/authz-module/data/hooks';
import { LibraryAuthZProvider, useLibraryAuthZ } from './context';
import messages from './messages';
@@ -10,7 +10,7 @@ import messages from './messages';
const LibrariesAuthZTeamView = () => {
const intl = useIntl();
const { libraryId } = useLibraryAuthZ();
const { data: library } = useLibrary(libraryId)
const { data: library } = useLibrary(libraryId);
const rootBradecrumb = intl.formatMessage(messages['library.authz.breadcrumb.root']) || '';
const pageTitle = intl.formatMessage(messages['library.authz.manage.page.title']);
return (

View File

@@ -2,13 +2,13 @@ import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ROUTES } from '@src/authz-module/constants';
import { renderWrapper } from '@src/setupTest';
import TeamTable from './TeamTable';
import { useTeamMembers } from '@src/authz-module/data/hooks';
import TeamTable from './TeamTable';
import { useLibraryAuthZ } from '../context';
const mockNavigate = jest.fn();
jest.mock('react-router', () => ({
...jest.requireActual('react-router'),
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockNavigate,
}));
@@ -93,7 +93,7 @@ describe('TeamTable', () => {
await userEvent.click(editButtons[0]);
expect(mockNavigate).toHaveBeenCalledWith(
`/authz/${ROUTES.LIBRARIES_USER_PATH.replace(':username', 'alice')}`,
`/authz/${ROUTES.LIBRARIES_USER_PATH.replace(':username', 'bob')}`,
);
});

View File

@@ -1,5 +1,4 @@
import { useMemo } from 'react';
import { useNavigate } from 'react-router';
import { useNavigate } from 'react-router-dom';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
DataTable, Button, Chip, Skeleton,
@@ -46,31 +45,13 @@ const TeamTable = () => {
// TODO: Display error in the notification system
const {
data: teamMembers, isLoading, isError
data: teamMembers, isLoading, isError,
} = useTeamMembers(libraryId);
const rows = isError ? [] : (teamMembers || SKELETON_ROWS);
const navigate = useNavigate();
const columns = useMemo(() => [
{
Header: intl.formatMessage(messages['library.authz.team.table.display.name']),
accessor: 'displayName',
Cell: NameCell,
},
{
Header: intl.formatMessage(messages['library.authz.team.table.email']),
accessor: 'email',
Cell: EmailCell,
},
{
Header: intl.formatMessage(messages['library.authz.team.table.roles']),
accessor: 'roles',
Cell: RolesCell,
},
], [isLoading]);
return (
<DataTable
isPaginated
@@ -88,7 +69,7 @@ const TeamTable = () => {
variant="link"
size="sm"
// TODO: update the view with the team member view
onClick={() => navigate(`/authz/${ROUTES.LIBRARIES_USER_PATH.replace(':username', username)}`)}
onClick={() => navigate(`/authz/${ROUTES.LIBRARIES_USER_PATH.replace(':username', row.original.username)}`)}
>
{intl.formatMessage(messages['authz.libraries.team.table.edit.action'])}
</Button>
@@ -98,7 +79,25 @@ const TeamTable = () => {
initialState={{
pageSize: 10,
}}
columns={columns}
columns={
[
{
Header: intl.formatMessage(messages['library.authz.team.table.display.name']),
accessor: 'displayName',
Cell: NameCell,
},
{
Header: intl.formatMessage(messages['library.authz.team.table.email']),
accessor: 'email',
Cell: EmailCell,
},
{
Header: intl.formatMessage(messages['library.authz.team.table.roles']),
accessor: 'roles',
Cell: RolesCell,
},
]
}
/>
);
};

View File

@@ -25,7 +25,6 @@ const TestComponent = () => {
};
describe('LibraryAuthZProvider', () => {
beforeEach(() => {
jest.clearAllMocks();
(useParams as jest.Mock).mockReturnValue({ libraryId: 'lib123' });
@@ -42,7 +41,7 @@ describe('LibraryAuthZProvider', () => {
renderWrapper(
<LibraryAuthZProvider>
<TestComponent />
</LibraryAuthZProvider>
</LibraryAuthZProvider>,
);
expect(screen.getByTestId('username')).toHaveTextContent('testuser');
@@ -62,7 +61,7 @@ describe('LibraryAuthZProvider', () => {
renderWrapper(
<LibraryAuthZProvider>
<TestComponent />
</LibraryAuthZProvider>
</LibraryAuthZProvider>,
);
}).toThrow('NoAccess');
});
@@ -70,7 +69,7 @@ describe('LibraryAuthZProvider', () => {
it('provides context when user can view but not manage team', () => {
(useValidateUserPermissions as jest.Mock).mockReturnValue({
data: [
{ allowed: true }, // canViewTeam
{ allowed: true }, // canViewTeam
{ allowed: false }, // canManageTeam
],
});
@@ -78,7 +77,7 @@ describe('LibraryAuthZProvider', () => {
renderWrapper(
<LibraryAuthZProvider>
<TestComponent />
</LibraryAuthZProvider>
</LibraryAuthZProvider>,
);
expect(screen.getByTestId('canManageTeam')).toHaveTextContent('false');
@@ -91,8 +90,8 @@ describe('LibraryAuthZProvider', () => {
renderWrapper(
<LibraryAuthZProvider>
<TestComponent />
</LibraryAuthZProvider>
);;
</LibraryAuthZProvider>,
);
}).toThrow('MissingLibrary');
});

View File

@@ -4,7 +4,6 @@ import {
import { useParams } from 'react-router-dom';
import { AppContext } from '@edx/frontend-platform/react';
import { useValidateUserPermissions } from '@src/data/hooks';
import { useLibrary } from '../data/hooks';
const LIBRARY_TEAM_PERMISSIONS = ['act:view_library_team', 'act:manage_library_team'];
@@ -29,7 +28,7 @@ type AuthZProviderProps = {
children: ReactNode;
};
export const LibraryAuthZProvider: React.FC<AuthZProviderProps> = ({ children }) => {
export const LibraryAuthZProvider: React.FC<AuthZProviderProps> = ({ children }:AuthZProviderProps) => {
const { libraryId } = useParams<{ libraryId: string }>();
const { authenticatedUser } = useContext(AppContext) as AppContextType;

View File

@@ -2,4 +2,4 @@ import LibrariesTeamManager from './LibrariesTeamManager';
export {
LibrariesTeamManager,
}
};

View File

@@ -1,16 +1,14 @@
import { Spinner, Container } from '@openedx/paragon';
const LoadingPage = () => {
return (
<Container className="d-flex vh-100" data-testid="loading-page">
<Spinner
variant="primary"
animation="border"
role="status"
className="mb-3 m-auto"
/>
</Container>
);
};
const LoadingPage = () => (
<Container className="d-flex vh-100" data-testid="loading-page">
<Spinner
variant="primary"
animation="border"
role="status"
className="mb-3 m-auto"
/>
</Container>
);
export default LoadingPage;

View File

@@ -2,7 +2,9 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { PermissionValidationRequest, PermissionValidationResponse } from '@src/types';
import { getApiUrl } from './utils';
export const validateUserPermissions = async (validations: PermissionValidationRequest[]): Promise<PermissionValidationResponse[]> => {
const { data } = await getAuthenticatedHttpClient().post(getApiUrl(`/api/authz/v1/permissions/validate/me`), validations);
export const validateUserPermissions = async (
validations: PermissionValidationRequest[],
): Promise<PermissionValidationResponse[]> => {
const { data } = await getAuthenticatedHttpClient().post(getApiUrl('/api/authz/v1/permissions/validate/me'), validations);
return data;
};

View File

@@ -90,10 +90,8 @@ describe('useValidateUserPermissions', () => {
wrapper: createWrapper(),
});
});
} catch (error) {
expect(error).toEqual(mockError); // Check for the expected error
return;
expect(error).toEqual(mockError); // Check for the expected error
}
});
});

View File

@@ -1,7 +1,7 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { PermissionValidationRequest, PermissionValidationResponse } from '@src/types';
import { validateUserPermissions } from './api';
import { appId } from '@src/constants';
import { validateUserPermissions } from './api';
const adminConsoleQueryKeys = {
all: [appId] as const,
@@ -25,10 +25,10 @@ const adminConsoleQueryKeys = {
* if (data[0].allowed) { ... }
*
*/
export const useValidateUserPermissions = (permissions: PermissionValidationRequest[]) => {
return useSuspenseQuery<PermissionValidationResponse[], Error>({
queryKey: adminConsoleQueryKeys.permissions(permissions),
queryFn: () => validateUserPermissions(permissions),
retry: false,
});
};
export const useValidateUserPermissions = (
permissions: PermissionValidationRequest[],
) => useSuspenseQuery<PermissionValidationResponse[], Error>({
queryKey: adminConsoleQueryKeys.permissions(permissions),
queryFn: () => validateUserPermissions(permissions),
retry: false,
});

View File

@@ -6,11 +6,11 @@ import { AppProvider, ErrorPage } from '@edx/frontend-platform/react';
import {
APP_INIT_ERROR, APP_READY, subscribe, initialize,
} from '@edx/frontend-platform';
import AuthZModule from 'authz-module';
import messages from './i18n';
import './index.scss';
import AuthZModule from 'authz-module';
const queryClient = new QueryClient();
@@ -24,7 +24,7 @@ subscribe(APP_READY, () => {
<Routes>
<Route path="/authz/*" element={<AuthZModule />} />
</Routes>
</ AppProvider>
</AppProvider>
</QueryClientProvider>
</StrictMode>,
);

View File

@@ -1,12 +1,12 @@
export interface PermissionValidationRequest {
action: string;
object?: string;
scope?: string;
};
action: string;
object?: string;
scope?: string;
}
export interface PermissionValidationResponse extends PermissionValidationRequest{
allowed: boolean;
};
export interface PermissionValidationResponse extends PermissionValidationRequest {
allowed: boolean;
}
// Libraries AuthZ types
export interface TeamMember {