Skip to content

Commit

Permalink
UILD-410: Fix Sonar issues and increase tests coverage (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
SKarolFolio authored Nov 13, 2024
1 parent d27f2db commit b771ec2
Show file tree
Hide file tree
Showing 15 changed files with 455 additions and 73 deletions.
22 changes: 21 additions & 1 deletion .github/workflows/ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,24 @@ jobs:
if: github.ref_name == github.event.repository.default_branch || github.event_name != 'push' || github.ref_type == 'tag'
secrets: inherit
with:
install-before-publish: true
install-before-publish: true
# extened default list of exclusions
sonar-exclusions: >
artifacts/**,
docs/**,
dist/**,
examples/**,
resources/bigtest/interactors/**,
resources/bigtest/network/**,
LICENSE,
ci/**,
node_modules/**,
src/test/**,
src/common/constants/**,
jest.config.*,
tsconfig.*,
jest.config.*,
vite.config.*,
**/*.md,
**/*.scss,
**/*.json
60 changes: 60 additions & 0 deletions src/common/hooks/useComplexLookupSearchResults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useCallback, useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { useIntl } from 'react-intl';
import { type Row } from '@components/Table';
import { useSearchContext } from '@common/hooks/useSearchContext';
import { ComplexLookupSearchResultsProps } from '@components/ComplexLookupField/ComplexLookupSearchResults';
import state from '@state';

export const useComplexLookupSearchResults = ({
onTitleClick,
tableConfig,
searchResultsFormatter,
}: ComplexLookupSearchResultsProps) => {
const { onAssignRecord } = useSearchContext();
const data = useRecoilValue(state.search.data);
const sourceData = useRecoilValue(state.search.sourceData);
const { formatMessage } = useIntl();

const applyActionItems = useCallback(
(rows: Row[]): Row[] =>
rows?.map(row => {
const formattedRow: Row = { ...row };

Object.entries(tableConfig.columns).forEach(([key, column]) => {
formattedRow[key] = {
...row[key],
children: column.formatter
? column.formatter({ row, formatMessage, onAssign: onAssignRecord, onTitleClick })
: row[key].label,
};
});

return formattedRow;
}),
[onAssignRecord, tableConfig],
);

const formattedData = useMemo(
() => applyActionItems(searchResultsFormatter(data || [], sourceData || [])),
[applyActionItems, data],
);

const listHeader = useMemo(
() =>
Object.keys(tableConfig.columns).reduce((accum, key) => {
const { label, position, className } = (tableConfig.columns[key] || {}) as SearchResultsTableColumn;

accum[key] = {
label: label ? formatMessage({ id: label }) : '',
position: position,
className: className,
};

return accum;
}, {} as Row),
[tableConfig],
);

return { formattedData, listHeader };
};
6 changes: 3 additions & 3 deletions src/common/hooks/useMarcData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { StatusType } from '@common/constants/status.constants';
import { UserNotificationFactory } from '@common/services/userNotification';
import state from '@state';

export const useMarcData = <T>(markState: RecoilState<T>) => {
const setMarcPreviewData = useSetRecoilState(markState);
const clearMarcData = useResetRecoilState(markState);
export const useMarcData = <T>(marcState: RecoilState<T>) => {
const setMarcPreviewData = useSetRecoilState(marcState);
const clearMarcData = useResetRecoilState(marcState);
const setIsLoading = useSetRecoilState(state.loadingState.isLoading);
const setStatus = useSetRecoilState(state.status.commonMessages);

Expand Down
8 changes: 4 additions & 4 deletions src/common/services/schema/schemaWithDuplicates.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export class SchemaWithDuplicatesService implements ISchemaWithDuplicatesService

constructor(
private schema: Map<string, SchemaEntry>,
private selectedEntriesService: ISelectedEntries,
private entryPropertiesGeneratorService?: IEntryPropertiesGeneratorService,
private readonly selectedEntriesService: ISelectedEntries,
private readonly entryPropertiesGeneratorService?: IEntryPropertiesGeneratorService,
) {
this.set(schema);
this.isManualDuplication = true;
Expand Down Expand Up @@ -107,7 +107,7 @@ export class SchemaWithDuplicatesService implements ISchemaWithDuplicatesService
if (!entry || entry.cloneOf) return;

const { children } = entry;
let updatedEntryUuid = newUuids?.[index] || uuidv4();
let updatedEntryUuid = newUuids?.[index] ?? uuidv4();
const isFirstAssociatedEntryElem = parentEntry?.dependsOn && newUuids && index === 0;

if (isFirstAssociatedEntryElem) {
Expand All @@ -126,7 +126,7 @@ export class SchemaWithDuplicatesService implements ISchemaWithDuplicatesService
newUuids,
});

if (controlledByEntry && controlledByEntry?.uuid) {
if (controlledByEntry?.uuid) {
this.schema.set(controlledByEntry.uuid, controlledByEntry);
}

Expand Down
10 changes: 5 additions & 5 deletions src/common/services/userValues/userValueTypes/simpleLookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export class SimpleLookupUserValueService extends UserValueType implements IUser
private contents?: UserValueContents[];

constructor(
private apiClient: IApiClient,
private cacheService: ILookupCacheService,
private readonly apiClient: IApiClient,
private readonly cacheService: ILookupCacheService,
) {
super();
}
Expand Down Expand Up @@ -60,7 +60,7 @@ export class SimpleLookupUserValueService extends UserValueType implements IUser
}

this.value = {
uuid: uuid || '',
uuid: uuid ?? '',
contents: this.contents,
};

Expand All @@ -70,7 +70,7 @@ export class SimpleLookupUserValueService extends UserValueType implements IUser
private checkDefaultGroupValues(groupUri?: string, itemUri?: string) {
if (!groupUri || !itemUri) return false;

return (DEFAULT_GROUP_VALUES as DefaultGroupValues)[groupUri as string]?.value === itemUri;
return (DEFAULT_GROUP_VALUES as DefaultGroupValues)[groupUri]?.value === itemUri;
}

private generateContentItem({
Expand Down Expand Up @@ -100,7 +100,7 @@ export class SimpleLookupUserValueService extends UserValueType implements IUser
?.find(
({ label: optionLabel, value }) => value.uri === mappedUri || value.label === label || optionLabel === label,
);
const selectedLabel = typesMap && itemUri ? loadedOption?.label || itemUri : loadedOption?.label || label;
const selectedLabel = typesMap && itemUri ? (loadedOption?.label ?? itemUri) : (loadedOption?.label ?? label);

const contentItem = {
label: selectedLabel,
Expand Down
58 changes: 8 additions & 50 deletions src/components/ComplexLookupField/ComplexLookupSearchResults.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { FC, useCallback, useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { FormattedMessage, useIntl } from 'react-intl';
import { FC } from 'react';
import { TableFlex, type Row } from '@components/Table';
import { useSearchContext } from '@common/hooks/useSearchContext';
import state from '@state';
import { useComplexLookupSearchResults } from '@common/hooks/useComplexLookupSearchResults';

type ComplexLookupSearchResultsProps = {
export type ComplexLookupSearchResultsProps = {
onTitleClick?: (id: string, title?: string, headingType?: string) => void;
tableConfig: SearchResultsTableConfig;
searchResultsFormatter: (data: any[], sourceData?: SourceDataDTO) => Row[];
Expand All @@ -16,50 +13,11 @@ export const ComplexLookupSearchResults: FC<ComplexLookupSearchResultsProps> = (
tableConfig,
searchResultsFormatter,
}) => {
const { onAssignRecord } = useSearchContext();
const data = useRecoilValue(state.search.data);
const sourceData = useRecoilValue(state.search.sourceData);
const { formatMessage } = useIntl();

const applyActionItems = useCallback(
(rows: Row[]): Row[] =>
rows.map(row => {
const formattedRow: Row = { ...row };

Object.entries(tableConfig.columns).forEach(([key, column]) => {
formattedRow[key] = {
...row[key],
children: column.formatter
? column.formatter({ row, formatMessage, onAssign: onAssignRecord, onTitleClick })
: row[key].label,
};
});

return formattedRow;
}),
[onAssignRecord, tableConfig],
);

const formattedData = useMemo(
() => applyActionItems(searchResultsFormatter(data || [], sourceData || [])),
[applyActionItems, data],
);

const listHeader = useMemo(
() =>
Object.keys(tableConfig.columns).reduce((accum, key) => {
const { label, position, className } = (tableConfig.columns[key] || {}) as SearchResultsTableColumn;

accum[key] = {
label: label ? <FormattedMessage id={label} /> : '',
position: position,
className: className,
};

return accum;
}, {} as Row),
[tableConfig],
);
const { listHeader, formattedData } = useComplexLookupSearchResults({
onTitleClick,
tableConfig,
searchResultsFormatter,
});

return (
<div className="search-result-list">
Expand Down
11 changes: 10 additions & 1 deletion src/components/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@ import './DatePicker.scss';

type DatePickerProps = {
id: string;
['data-testid']?: string;
placeholder?: string;
name?: string;
value?: string;
onChange?: (value: string) => void | Dispatch<SetStateAction<string>>;
};

export const DatePicker: FC<DatePickerProps> = ({ id, value, onChange, name, placeholder }) => {
export const DatePicker: FC<DatePickerProps> = ({
id,
value,
onChange,
name,
placeholder,
'data-testid': dataTestId,
}) => {
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange?.(event.target.value);
};
Expand All @@ -18,6 +26,7 @@ export const DatePicker: FC<DatePickerProps> = ({ id, value, onChange, name, pla
<div className="date-picker">
<input
id={id}
data-testid={dataTestId ?? `date-picker-input-${id}`}
type="date"
className="date-picker-input"
value={value}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const Modal: FC<Props> = ({
return isOpen && portalElement
? createPortal(
<>
<div className="overlay" onClick={onClose} data-testid="modal-overlay" />
<div className="overlay" onClick={onClose} role="presentation" data-testid="modal-overlay" />
<div className={classNames(['modal', className])} role="dialog" data-testid="modal">
<div className={classNames(['modal-header', classNameHeader])}>
{showCloseIconButton && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useRecoilValue } from 'recoil';
import { renderHook } from '@testing-library/react';
import { useSearchContext } from '@common/hooks/useSearchContext';
import { useComplexLookupSearchResults } from '@common/hooks/useComplexLookupSearchResults';
import { ComplexLookupSearchResultsProps } from '@components/ComplexLookupField/ComplexLookupSearchResults';
import { Row } from '@components/Table';

jest.mock('recoil');
jest.mock('@common/hooks/useSearchContext', () => ({
useSearchContext: jest.fn(),
}));

const data = [
{
id: '1',
name: { label: 'Item 1' },
description: { label: 'Description 1' },
},
];
const sourceData = [
{
id: '1',
name: 'Item 1',
description: 'Description 1',
},
];
const tableConfig = {
columns: {
name: {
label: 'name.label',
position: 1,
formatter: ({ row }: any) => row.name.label,
},
description: {
label: 'description.label',
position: 2,
},
},
};
const searchResultsFormatter = (data: Row[]) => data;

describe('useComplesLookupSearchResults', () => {
beforeEach(() => {
(useSearchContext as jest.Mock).mockReturnValue({
onAssignRecord: jest.fn(),
});
(useRecoilValue as jest.Mock).mockReturnValueOnce(data).mockReturnValueOnce(sourceData);
});

it('returns "formattedData" and "listHeader"', () => {
const props: ComplexLookupSearchResultsProps = {
onTitleClick: jest.fn(),
tableConfig,
searchResultsFormatter,
};

const { result } = renderHook(() => useComplexLookupSearchResults(props));

expect(result.current.formattedData).toEqual([
{
id: '1',
name: {
label: 'Item 1',
children: 'Item 1',
},
description: {
label: 'Description 1',
children: 'Description 1',
},
},
]);

expect(result.current.listHeader).toEqual({
name: {
label: 'name.label',
position: 1,
className: undefined,
},
description: {
label: 'description.label',
position: 2,
className: undefined,
},
});
});
});
Loading

0 comments on commit b771ec2

Please sign in to comment.