Skip to content

Commit

Permalink
Add frontend tests for new schemaErrorsPanel component
Browse files Browse the repository at this point in the history
  • Loading branch information
standeren committed Dec 8, 2023
1 parent 4c01ba3 commit d09506e
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export const SchemaEditorWithToolbar = ({
/>
{schemaGenerationErrorMessages.length > 0 && (
<SchemaGenerationErrorsPanel
showSchemaGenerationErrors={schemaGenerationErrorMessages.length > 0}
onCloseErrorsPanel={() => setSchemaGenerationErrorMessages([])}
schemaGenerationErrorMessages={schemaGenerationErrorMessages}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from 'react';
import { renderWithMockStore } from '../../../test/mocks';
import {
SchemaGenerationErrorsPanel,
SchemaGenerationErrorsPanelProps,
} from './SchemaGenerationErrorsPanel';
import { ServicesContextProps } from 'app-shared/contexts/ServicesContext';
import { act, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { textMock } from '../../../../testing/mocks/i18nMock';

const user = userEvent.setup();
const schemaGenerationErrorMessages = ['custom error message', 'another custom error message'];
const defaultProps: SchemaGenerationErrorsPanelProps = {
onCloseErrorsPanel: jest.fn(),
schemaGenerationErrorMessages,
};

describe('SchemaGenerationErrorsPanel', () => {
it('Displays error title and list of errors when schemaGenerationErrorMessages contains errors', () => {
render({});
const errorTitle = screen.getByText(textMock('api_errors.DM_01'));
expect(errorTitle).toBeInTheDocument();
const errorMessage1 = screen.getByText(schemaGenerationErrorMessages[0]);
expect(errorMessage1).toBeInTheDocument();
const errorMessage2 = screen.getByText(schemaGenerationErrorMessages[1]);
expect(errorMessage2).toBeInTheDocument();
});

it('Displays list of text-mapped errors when schemaGenerationErrorMessages contains known errors', () => {
render(
{},
{
...defaultProps,
schemaGenerationErrorMessages: [
"'SomeFieldInSchema' member names cannot be the same as their enclosing type",
],
},
);
const errorTitle = screen.getByText(textMock('api_errors.DM_01'));
expect(errorTitle).toBeInTheDocument();
const knownErrorMessage = screen.getByText(
textMock('api_errors.DM_CsharpCompiler_NameCollision'),
);
expect(knownErrorMessage).toBeInTheDocument();
});

it('Displays text-mapped known schemaErrors and plaintext unknown errors', () => {
const unknownErrorMessage = 'an unknown error';
render(
{},
{
...defaultProps,
schemaGenerationErrorMessages: [
unknownErrorMessage,
"'SomeFieldInSchema' member names cannot be the same as their enclosing type",
],
},
);
const errorTitle = screen.getByText(textMock('api_errors.DM_01'));
expect(errorTitle).toBeInTheDocument();
const errorMessage = screen.getByText(unknownErrorMessage);
expect(errorMessage).toBeInTheDocument();
const knownErrorMessage = screen.getByText(
textMock('api_errors.DM_CsharpCompiler_NameCollision'),
);
expect(knownErrorMessage).toBeInTheDocument();
});

it('Calls onCloseErrorsPanel when close button is clicked', async () => {
const mockOnCloseErrorsPanel = jest.fn();
render({}, { ...defaultProps, onCloseErrorsPanel: mockOnCloseErrorsPanel });
const closeErrorPanelButton = screen.getByRole('button', { name: textMock('general.close') });
await act(() => user.click(closeErrorPanelButton));
expect(mockOnCloseErrorsPanel).toHaveBeenCalledTimes(1);
});
});

const render = (
queries: Partial<ServicesContextProps> = {},
props: Partial<SchemaGenerationErrorsPanelProps> = {},
) => renderWithMockStore({}, queries)(<SchemaGenerationErrorsPanel {...defaultProps} {...props} />);
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,51 @@ import { Trans, useTranslation } from 'react-i18next';
import { XMarkIcon } from '@navikt/aksel-icons';

export interface SchemaGenerationErrorsPanelProps {
showSchemaGenerationErrors: boolean;
onCloseErrorsPanel: () => void;
schemaGenerationErrorMessages: string[];
}

export const SchemaGenerationErrorsPanel = ({
showSchemaGenerationErrors,
onCloseErrorsPanel,
schemaGenerationErrorMessages,
}: SchemaGenerationErrorsPanelProps) => {
const { t } = useTranslation();

return (
<Alert severity='danger'>
<div className={classes.errorPanel}>
<div>
<Paragraph>{t('api_errors.DM_01')}</Paragraph>
<ul>
{schemaGenerationErrorMessages.map((errorMessage, index) => {
return (
<li key={index}>
<ErrorMessage>
{errorMessage.includes(
'member names cannot be the same as their enclosing type',
) ? (
<Trans
i18nKey={'api_errors.DM_CsharpCompiler_NameCollision'}
values={{ nodeName: errorMessage.match(/'([^']+)':/)?.[1] }}
components={{ bold: <strong /> }}
/>
) : (
<p>{errorMessage}</p>
)}
</ErrorMessage>
</li>
);
})}
</ul>
</div>
<Button
color='danger'
open={showSchemaGenerationErrors}
onClick={onCloseErrorsPanel}
variant='tertiary'
icon={<XMarkIcon />}
>
{t('general.close')}
</Button>
<Alert severity='danger' className={classes.errorPanel}>
<div>
<Paragraph>{t('api_errors.DM_01')}</Paragraph>
<ul>
{schemaGenerationErrorMessages?.map((errorMessage, index) => {
return (
<li key={index}>
<ErrorMessage>
{errorMessage.includes(
'member names cannot be the same as their enclosing type',
) ? (
<Trans
i18nKey={'api_errors.DM_CsharpCompiler_NameCollision'}
values={{ nodeName: errorMessage.match(/'([^']+)':/)?.[1] }}
components={{ bold: <strong /> }}
/>
) : (
<>{errorMessage}</>
)}
</ErrorMessage>
</li>
);
})}
</ul>
</div>
<Button
color='danger'
open={schemaGenerationErrorMessages.length > 0}
onClick={onCloseErrorsPanel}
variant='tertiary'
icon={<XMarkIcon />}
>
{t('general.close')}
</Button>
</Alert>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ const texts = {
};
const setCreateNewOpen = jest.fn();
const setSelectedOption = jest.fn();
const onSetSchemaGenerationErrorMessages = jest.fn();
const selectedOption: MetadataOption = convertMetadataToOption(jsonMetadata1Mock);
const defaultProps: TopToolbarProps = {
createNewOpen: false,
datamodels: [jsonMetadata1Mock],
selectedOption,
setCreateNewOpen,
setSelectedOption,
onSetSchemaGenerationErrorMessages,
};
const org = 'org';
const app = 'app';
Expand All @@ -51,7 +53,10 @@ const renderToolbar = (
) => {
const TopToolbarWithInitData = () => {
const queryClient = useQueryClient();
queryClient.setQueryData([QueryKey.JsonSchema, org, app, modelPath], buildJsonSchema(uiSchemaNodesMock));
queryClient.setQueryData(
[QueryKey.JsonSchema, org, app, modelPath],
buildJsonSchema(uiSchemaNodesMock),
);
return <TopToolbar {...defaultProps} {...props} />;
};

Expand Down Expand Up @@ -97,9 +102,12 @@ describe('TopToolbar', () => {
});

it('Shows error message when the "generate" button is clicked and a schema error is provided', async () => {
renderToolbar({}, {
generateModels: jest.fn().mockImplementation(() => Promise.reject()),
});
renderToolbar(
{},
{
generateModels: jest.fn().mockImplementation(() => Promise.reject()),
},
);
await act(() => user.click(screen.getByRole('button', { name: generateText })));
expect(await screen.findByRole('alert')).toHaveTextContent(generalErrorMessage);
});
Expand Down
36 changes: 15 additions & 21 deletions frontend/packages/shared/src/contexts/ServicesContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,20 @@ import { queriesMock } from 'app-shared/mocks/queriesMock';
import { useQuery } from '@tanstack/react-query';
import { textMock } from '../../../../testing/mocks/i18nMock';
import { createApiErrorMock } from 'app-shared/mocks/apiErrorMock';

const customErrorMessages = ['a specific error message'];
import { KeyValuePairs } from 'app-shared/types/KeyValuePairs';

const unknownErrorCode = 'unknownErrorCode';
// Mocks:
jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string, variables?: KeyValuePairs<string>) => textMock(key, variables),
i18n: {
exists: (key: string) =>
key !== `api_errors.${unknownErrorCode}` ? textMock(key) : undefined,
},
}),
Trans: ({ i18nKey }: { i18nKey: any }) => textMock(i18nKey),
}));

const wrapper = ({
children,
Expand Down Expand Up @@ -79,30 +91,12 @@ describe('ServicesContext', () => {
expect(await screen.findByText(textMock('api_errors.DM_01'))).toBeInTheDocument();
});

it('displays a list of custom error messages if API returns an error code and the corresponding custom error messages', async () => {
const { result } = renderHook(
() =>
useQuery({
queryKey: ['fetchData'],
queryFn: () => Promise.reject(createApiErrorMock(400, 'DM_01', customErrorMessages)),
retry: false,
}),
{ wrapper },
);

await waitFor(() => result.current.isError);

expect(await screen.findByText(textMock('api_errors.DM_01'))).toBeInTheDocument();
//expect(await screen.findByText(customErrorMessages[0])).toBeInTheDocument();
//expect(await screen.findByText(customErrorMessages[1])).toBeInTheDocument();
});

it('displays a default error message if API returns an error code but the error message does not exist', async () => {
const { result } = renderHook(
() =>
useQuery({
queryKey: ['fetchData'],
queryFn: () => Promise.reject(createApiErrorMock(500, 'DM_02')),
queryFn: () => Promise.reject(createApiErrorMock(500, unknownErrorCode)),
retry: false,
}),
{ wrapper },
Expand Down
3 changes: 0 additions & 3 deletions frontend/testing/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ jest.mock('react-i18next', () => ({
Trans: ({ i18nKey }) => textMock(i18nKey),
useTranslation: () => ({
t: (key: string, variables?: KeyValuePairs<string>) => textMock(key, variables),
i18n: {
exists: (key: string) => textMock(key) !== undefined,
},
}),
withTranslation: () => (Component: ReactNode) => Component,
}));
Expand Down

0 comments on commit d09506e

Please sign in to comment.