Skip to content

Commit

Permalink
Merge pull request #1085 from InseeFr/feat/codes-panel-add-button
Browse files Browse the repository at this point in the history
feat: convert some files to typescript
  • Loading branch information
PierreVasseur authored Dec 2, 2024
2 parents 0b630b1 + 3fdbb2d commit 223d892
Show file tree
Hide file tree
Showing 13 changed files with 279 additions and 20 deletions.
8 changes: 4 additions & 4 deletions src/packages/auth/open-id-connect-auth/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const isContributor = (
};
export const isConceptCreator = (
roles: string[],
stamp: string,
conceptCreator: string,
stamp?: string,
conceptCreator?: string,
) => roles.includes(R.CONCEPTS_CREATOR) && stamp === conceptCreator;

export const filterConceptsToValidate = (
Expand Down Expand Up @@ -49,8 +49,8 @@ export const isAdminOrContributor = (roles: string[]) =>

export const isAdminOrConceptCreator = (
roles: string[],
stamp: string,
conceptCreator: string,
stamp?: string,
conceptCreator?: string,
) => isAdmin(roles) || isConceptCreator(roles, stamp, conceptCreator);

export const isAdminOrContributorOrConceptCreator = (
Expand Down
5 changes: 5 additions & 0 deletions src/packages/model/concepts/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ export type Collection = {
prefLabelLg1: string;
creator: string;
};

export type PartialCollection = {
id: string;
label: string;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { Mock, vi } from 'vitest';

import { ADMIN, CODELIST_CONTRIBUTOR } from '../../../auth/roles';
import { usePermission } from '../../../redux/hooks/usePermission';
import { CodesPanelAddButton } from './codes-panel-add-button';

vi.mock('../../../redux/hooks/usePermission', () => ({
usePermission: vi.fn(),
}));

describe('CodesPanelAddButton', () => {
const mockOnHandlePanel = vi.fn();

afterEach(() => {
vi.clearAllMocks();
});

it('should not render if codelist.lastCodeUriSegment is missing', () => {
(usePermission as Mock).mockReturnValue({
stamp: 'test-stamp',
roles: [ADMIN],
});

render(
<CodesPanelAddButton codelist={{}} onHandlePanel={mockOnHandlePanel} />,
);

expect(screen.queryByRole('button', { name: /add/i })).toBeNull();
});

it('should render the button if user is an admin', () => {
(usePermission as Mock).mockReturnValue({
stamp: 'test-stamp',
roles: [ADMIN],
});

render(
<CodesPanelAddButton
codelist={{ lastCodeUriSegment: 'segment' }}
onHandlePanel={mockOnHandlePanel}
/>,
);

screen.getByRole('button', { name: /add/i });
});

it('should render the button if user has contributor rights based on stamp', () => {
(usePermission as Mock).mockReturnValue({
stamp: 'test-contributor',
roles: [CODELIST_CONTRIBUTOR],
});

render(
<CodesPanelAddButton
codelist={{
lastCodeUriSegment: 'segment',
contributor: 'test-contributor',
}}
onHandlePanel={mockOnHandlePanel}
/>,
);

screen.getByRole('button', { name: /add/i });
});

it('should not render the button if user lacks the required permissions', () => {
(usePermission as Mock).mockReturnValue({
stamp: 'test-stamp',
roles: ['OTHER_ROLE'],
});

render(
<CodesPanelAddButton
codelist={{
lastCodeUriSegment: 'segment',
contributor: 'test-contributor',
}}
onHandlePanel={mockOnHandlePanel}
/>,
);

expect(screen.queryByRole('button', { name: /add/i })).toBeNull();
});

it('should trigger onHandlePanel when the button is clicked', () => {
(usePermission as Mock).mockReturnValue({
stamp: 'test-contributor',
roles: [CODELIST_CONTRIBUTOR],
});

render(
<CodesPanelAddButton
codelist={{
lastCodeUriSegment: 'segment',
contributor: 'test-contributor',
}}
onHandlePanel={mockOnHandlePanel}
/>,
);

const button = screen.getByRole('button', { name: /add/i });
fireEvent.click(button);

expect(mockOnHandlePanel).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import { AddButton } from '@components/buttons/add';
import { ADMIN, CODELIST_CONTRIBUTOR } from '../../../auth/roles';
import { usePermission } from '../../../redux/hooks/usePermission';

export const CodesPanelAddButton = ({ codelist, onHandlePanel }) => {
type CodesPanelAddButtonTypes = {
codelist: any;
onHandlePanel: () => void;
};
export const CodesPanelAddButton = ({
codelist,
onHandlePanel,
}: Readonly<CodesPanelAddButtonTypes>) => {
const permission = usePermission();

if (!codelist.lastCodeUriSegment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,25 @@ import { UNPUBLISHED } from '../../..//model/ValidationState';
import { ADMIN, CODELIST_CONTRIBUTOR } from '../../../auth/roles';
import { usePermission } from '../../../redux/hooks/usePermission';

type ViewMenuTypes = {
handleUpdate: () => void;
handleDelete: () => void;
handleBack: () => void;
publish: () => void;
updatable: boolean;
deletable: boolean;
codelist: any;
};

export const ViewMenu = ({
col,
handleUpdate,
handleDelete,
handleBack,
codelist,
publish,
updatable,
deletable,
}) => {
}: Readonly<ViewMenuTypes>) => {
const permission = usePermission();

const hasRightsBasedOnStamp =
Expand All @@ -29,7 +38,7 @@ export const ViewMenu = ({

return (
<ActionToolbar>
<ReturnButton action={handleBack} col={col} />
<ReturnButton action={handleBack} />

{(isAdmin || hasRightsBasedOnStamp) && (
<ValidationButton callback={publish} object={codelist} />
Expand All @@ -39,11 +48,11 @@ export const ViewMenu = ({
(isAdmin ||
(hasRightsBasedOnStamp &&
codelist.validationState === UNPUBLISHED)) && (
<DeleteButton action={handleDelete} col={col} />
<DeleteButton action={handleDelete} />
)}

{updatable && (isAdmin || hasRightsBasedOnStamp) && (
<UpdateButton action={handleUpdate} col={col} />
<UpdateButton action={handleUpdate} />
)}
</ActionToolbar>
);
Expand Down
72 changes: 72 additions & 0 deletions src/packages/modules-concepts/collections/home-container.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { render, screen, waitFor } from '@testing-library/react';
import { vi } from 'vitest';

import { CollectionApi } from '@sdk/collection-api';

import CollectionsHome from './home';
import { Component } from './home-container';

vi.mock('@components/loading', () => ({
Loading: vi.fn(() => <div>Loading...</div>),
}));

vi.mock('./home', () => ({
default: vi.fn(() => <div>CollectionsHome</div>),
}));

vi.mock('@sdk/collection-api', () => ({
CollectionApi: {
getCollectionList: vi.fn(),
},
}));

describe('Component', () => {
const mockCollections = [
{ id: '1', label: 'Collection 1' },
{ id: '2', label: 'Collection 2' },
];

beforeEach(() => {
vi.clearAllMocks();
});

it('should show the loading spinner initially', () => {
// Simuler une promesse qui ne se résout pas immédiatement
CollectionApi.getCollectionList.mockImplementation(
() => new Promise(() => {}),
);

render(<Component />);

screen.getByText('Loading...');
});

it('should render CollectionsHome after data is loaded', async () => {
CollectionApi.getCollectionList.mockResolvedValue(mockCollections);

render(<Component />);

screen.getByText('Loading...');

await waitFor(() =>
expect(CollectionApi.getCollectionList).toHaveBeenCalled(),
);

screen.getByText('CollectionsHome');
});

it('should pass the collections data to CollectionsHome', async () => {
CollectionApi.getCollectionList.mockResolvedValue(mockCollections);

render(<Component />);

await waitFor(() => screen.getByText('CollectionsHome'));

expect(CollectionsHome).toHaveBeenCalledWith(
expect.objectContaining({
collections: mockCollections,
}),
{},
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { Loading } from '@components/loading';

import { CollectionApi } from '@sdk/collection-api';

import { PartialCollection } from '../../model/concepts/collection';
import CollectionsHome from './home';

export const Component = () => {
const [loading, setLoading] = useState(true);
const [collections, setCollections] = useState([]);
const [collections, setCollections] = useState<PartialCollection[]>([]);
useEffect(() => {
CollectionApi.getCollectionList()
.then(setCollections)
Expand Down
58 changes: 58 additions & 0 deletions src/packages/modules-concepts/collections/home.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { render, screen } from '@testing-library/react';
import { vi } from 'vitest';

import { SearchableList } from '@components/searchable-list';

import { useTitle } from '@utils/hooks/useTitle';

import D from '../../deprecated-locales';
import CollectionsHome from './home';

vi.mock('@components/page-title', () => ({
PageTitle: vi.fn(() => <div>PageTitle</div>),
}));
vi.mock('@components/layout', () => ({
Row: vi.fn(({ children }) => <div>{children}</div>),
}));
vi.mock('@components/searchable-list', () => ({
SearchableList: vi.fn(() => <div>SearchableList</div>),
}));

vi.mock('@utils/hooks/useTitle');

vi.mock('./menu', () => ({
Menu: vi.fn(() => <div>Menu</div>),
}));

describe('CollectionsHome', () => {
const collectionsMock = [
{ id: '1', label: 'Collection 1' },
{ id: '2', label: 'Collection 2' },
];

it('should render the page title and the searchable list with correct data', () => {
render(<CollectionsHome collections={collectionsMock} />);

expect(useTitle).toHaveBeenCalledWith(D.conceptsTitle, D.collectionsTitle);

screen.getByText('PageTitle');
screen.getByText('SearchableList');
});

it('should render the Menu component', () => {
render(<CollectionsHome collections={collectionsMock} />);
screen.getByText('Menu');
});

it('should pass collections prop to SearchableList', () => {
render(<CollectionsHome collections={collectionsMock} />);
expect(SearchableList).toHaveBeenCalledWith(
expect.objectContaining({
items: collectionsMock,
childPath: 'concepts/collections',
autoFocus: true,
}),
{},
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import { SearchableList } from '@components/searchable-list';
import { useTitle } from '@utils/hooks/useTitle';

import D from '../../deprecated-locales';
import { PartialCollection } from '../../model/concepts/collection';
import { Menu } from './menu';

const CollectionsHome = ({ collections }) => {
type CollectionsHomeTypes = {
collections: PartialCollection[];
};

const CollectionsHome = ({ collections }: Readonly<CollectionsHomeTypes>) => {
useTitle(D.conceptsTitle, D.collectionsTitle);
return (
<div className="container">
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { usePermission } from '../redux/hooks/usePermission';

export const Menu = () => {
const permission = usePermission();
const { authType, roles } = permission;
const authImpl = check(authType);
const { roles } = permission;
const authImpl = check();
const adminOrCreator = authImpl.isAdminOrConceptCreator(roles);

return (
Expand All @@ -23,12 +23,7 @@ export const Menu = () => {
</Auth>
<ExportButton action="/concepts/export" wrapper={false} />
{adminOrCreator && (
<PublishButton
action="/concepts/validation"
col={8}
offset={2}
wrapper={false}
/>
<PublishButton action="/concepts/validation" wrapper={false} />
)}
</VerticalMenu>
);
Expand Down

0 comments on commit 223d892

Please sign in to comment.