Skip to content

Commit 5ef1fd0

Browse files
committed
fixes jest tests
1 parent 687844d commit 5ef1fd0

33 files changed

+279
-336
lines changed

src/components/form/Form.test.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { defaultMockDataElementId } from 'src/__mocks__/getInstanceDataMock';
77
import { defaultDataTypeMock } from 'src/__mocks__/getLayoutSetsMock';
88
import { Form } from 'src/components/form/Form';
99
import { type BackendValidationIssue, BackendValidationSeverity } from 'src/features/validation';
10+
import { fetchFormData } from 'src/queries/queries';
1011
import { renderWithInstanceAndLayout } from 'src/test/renderWithProviders';
1112
import type { CompExternal, ILayout } from 'src/layout/layout';
1213
import type { CompSummaryExternal } from 'src/layout/Summary/config.generated';
@@ -224,19 +225,20 @@ describe('Form', () => {
224225
});
225226

226227
async function render(layout = mockComponents, validationIssues: BackendValidationIssue[] = []) {
228+
jest.mocked(fetchFormData).mockImplementation(async () => ({
229+
Group: [
230+
{
231+
prop1: 'value1',
232+
prop2: 'value2',
233+
prop3: 'value3',
234+
},
235+
],
236+
}));
237+
227238
await renderWithInstanceAndLayout({
228239
renderer: () => <Form />,
229240
initialPage: 'FormLayout',
230241
queries: {
231-
fetchFormData: async () => ({
232-
Group: [
233-
{
234-
prop1: 'value1',
235-
prop2: 'value2',
236-
prop3: 'value3',
237-
},
238-
],
239-
}),
240242
fetchLayouts: () =>
241243
Promise.resolve({
242244
FormLayout: {

src/features/expressions/shared-context.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { screen } from '@testing-library/react';
66
import { getApplicationMetadataMock } from 'src/__mocks__/getApplicationMetadataMock';
77
import { getInstanceDataMock } from 'src/__mocks__/getInstanceDataMock';
88
import { getSharedTests } from 'src/features/expressions/shared';
9-
import { fetchApplicationMetadata } from 'src/queries/queries';
9+
import { fetchApplicationMetadata, fetchFormData } from 'src/queries/queries';
1010
import { renderWithInstanceAndLayout } from 'src/test/renderWithProviders';
1111
import { NodesInternal } from 'src/utils/layout/NodesContext';
1212
import { splitDashedKey } from 'src/utils/splitDashedKey';
@@ -93,6 +93,8 @@ describe('Expressions shared context tests', () => {
9393

9494
const applicationMetadata = getApplicationMetadataMock(instance ? {} : { onEntry: { show: 'stateless' } });
9595
jest.mocked(fetchApplicationMetadata).mockImplementation(async () => applicationMetadata);
96+
// TODO(Datamodels): add support for multiple data models
97+
jest.mocked(fetchFormData).mockImplementation(async () => dataModel ?? {});
9698

9799
if (instanceDataElements) {
98100
for (const element of instanceDataElements) {
@@ -111,8 +113,6 @@ describe('Expressions shared context tests', () => {
111113
renderer: () => <TestContexts />,
112114
queries: {
113115
fetchLayouts: async () => layouts!,
114-
// TODO(Datamodels): add support for multiple data models
115-
fetchFormData: async () => dataModel ?? {},
116116
...(instance ? { fetchInstanceData: async () => instance } : {}),
117117
...(frontendSettings ? { fetchApplicationSettings: async () => frontendSettings } : {}),
118118
},

src/features/expressions/shared-functions.test.tsx

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ import {
2020
isRepeatingComponent,
2121
RepeatingComponents,
2222
} from 'src/features/form/layout/utils/repeating';
23-
import { fetchApplicationMetadata, fetchInstanceData, fetchProcessState, fetchUserProfile } from 'src/queries/queries';
23+
import {
24+
fetchApplicationMetadata,
25+
fetchFormData,
26+
fetchInstanceData,
27+
fetchProcessState,
28+
fetchUserProfile,
29+
} from 'src/queries/queries';
2430
import { renderWithInstanceAndLayout, renderWithoutInstanceAndLayout } from 'src/test/renderWithProviders';
2531
import { DataModelLocationProvider } from 'src/utils/layout/DataModelLocation';
2632
import { useEvalExpression } from 'src/utils/layout/generator/useEvalExpression';
@@ -287,23 +293,6 @@ describe('Expressions shared function tests', () => {
287293
profile.profileSettingPreference.language = profileSettings.language;
288294
}
289295

290-
async function fetchFormData(url: string) {
291-
if (!dataModels) {
292-
return dataModel ?? {};
293-
}
294-
295-
const statelessDataType = url.match(/dataType=([\w-]+)&/)?.[1];
296-
const statefulDataElementId = url.match(/data\/([a-f0-9-]+)\?/)?.[1];
297-
298-
const model = dataModels.find(
299-
(dm) => dm.dataElement.dataType === statelessDataType || dm.dataElement.id === statefulDataElementId,
300-
);
301-
if (model) {
302-
return model.data;
303-
}
304-
throw new Error(`Datamodel ${url} not found in ${JSON.stringify(dataModels)}`);
305-
}
306-
307296
// Clear localstorage, because LanguageProvider uses it to cache selected languages
308297
localStorage.clear();
309298

@@ -327,6 +316,22 @@ describe('Expressions shared function tests', () => {
327316
}
328317
return instanceData;
329318
});
319+
jest.mocked(fetchFormData).mockImplementation(async (url: string) => {
320+
if (!dataModels) {
321+
return dataModel ?? {};
322+
}
323+
324+
const statelessDataType = url.match(/dataType=([\w-]+)&/)?.[1];
325+
const statefulDataElementId = url.match(/data\/([a-f0-9-]+)\?/)?.[1];
326+
327+
const model = dataModels.find(
328+
(dm) => dm.dataElement.dataType === statelessDataType || dm.dataElement.id === statefulDataElementId,
329+
);
330+
if (model) {
331+
return model.data;
332+
}
333+
throw new Error(`Datamodel ${url} not found in ${JSON.stringify(dataModels)}`);
334+
});
330335

331336
const renderFunc = stateless ? renderWithoutInstanceAndLayout : renderWithInstanceAndLayout;
332337
const { rerender } = await renderFunc({
@@ -343,7 +348,6 @@ describe('Expressions shared function tests', () => {
343348
sets: [{ id: 'layout-set', dataType: 'default', tasks: ['Task_1'] }, getSubFormLayoutSetMock()],
344349
}),
345350
fetchLayouts: async () => layouts,
346-
fetchFormData,
347351
...(frontendSettings ? { fetchApplicationSettings: async () => frontendSettings } : {}),
348352
fetchTextResources: async () => ({
349353
language: 'nb',

src/features/formData/FormData.test.tsx

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { GlobalFormDataReadersProvider } from 'src/features/formData/FormDataRea
2222
import { FD, FormDataWriteProvider } from 'src/features/formData/FormDataWrite';
2323
import { FormDataWriteProxyProvider } from 'src/features/formData/FormDataWriteProxies';
2424
import { useDataModelBindings } from 'src/features/formData/useDataModelBindings';
25-
import { fetchApplicationMetadata } from 'src/queries/queries';
25+
import { fetchApplicationMetadata, fetchFormData } from 'src/queries/queries';
2626
import {
2727
makeFormDataMethodProxies,
2828
renderWithInstanceAndLayout,
@@ -158,7 +158,6 @@ async function statelessRender(props: RenderProps) {
158158
),
159159
queries: {
160160
fetchDataModelSchema: async () => mockSchema,
161-
fetchFormData: async () => ({}),
162161
fetchLayouts: async () => ({}),
163162
...props.queries,
164163
},
@@ -175,7 +174,6 @@ async function statefulRender(props: RenderProps) {
175174
alwaysRouteToChildren: true,
176175
queries: {
177176
fetchDataModelSchema: async () => mockSchema,
178-
fetchFormData: async () => ({}),
179177
fetchLayouts: async () => ({}),
180178
...props.queries,
181179
},
@@ -216,6 +214,16 @@ describe('FormData', () => {
216214
}
217215

218216
async function render(props: MinimalRenderProps = {}) {
217+
jest.mocked(fetchFormData).mockImplementationOnce(async () => ({
218+
obj1: {
219+
prop1: 'value1',
220+
prop2: 'value2',
221+
},
222+
obj2: {
223+
prop1: 'value3',
224+
},
225+
}));
226+
219227
const renderCounts: RenderCounts = {
220228
ReaderObj1Prop1: 0,
221229
ReaderObj1Prop2: 0,
@@ -262,15 +270,6 @@ describe('FormData', () => {
262270
</>
263271
),
264272
queries: {
265-
fetchFormData: async () => ({
266-
obj1: {
267-
prop1: 'value1',
268-
prop2: 'value2',
269-
},
270-
obj2: {
271-
prop1: 'value3',
272-
},
273-
}),
274273
...props.queries,
275274
},
276275
...props,
@@ -382,6 +381,11 @@ describe('FormData', () => {
382381
}
383382

384383
async function render(props: MinimalRenderProps = {}) {
384+
jest.mocked(fetchFormData).mockImplementationOnce(async () => ({
385+
obj1: {
386+
prop1: 'value1',
387+
},
388+
}));
385389
return statefulRender({
386390
renderer: (
387391
<>
@@ -409,11 +413,6 @@ describe('FormData', () => {
409413
</>
410414
),
411415
queries: {
412-
fetchFormData: async () => ({
413-
obj1: {
414-
prop1: 'value1',
415-
},
416-
}),
417416
...props.queries,
418417
},
419418
...props,
@@ -611,7 +610,6 @@ describe('FormData', () => {
611610
</>
612611
),
613612
queries: {
614-
fetchFormData: async () => ({}),
615613
...props.queries,
616614
},
617615
...props,
@@ -649,13 +647,12 @@ describe('FormData', () => {
649647

650648
it('Navigating away and back again should restore the form data', async () => {
651649
const user = userEvent.setup({ delay: null });
652-
const { mutations, queries } = await render();
650+
const { mutations } = await render();
653651

654652
await user.type(screen.getByTestId('obj2.prop1'), 'a');
655653
expect(screen.getByTestId('obj2.prop1')).toHaveValue('a');
656654
expect(screen.getByTestId('hasUnsavedChanges')).toHaveTextContent('true');
657655

658-
expect(queries.fetchFormData).toHaveBeenCalledTimes(1);
659656
await user.click(screen.getByRole('button', { name: 'Navigate to a different page' }));
660657
await screen.findByText('something different');
661658

@@ -668,11 +665,6 @@ describe('FormData', () => {
668665
await user.click(screen.getByRole('button', { name: 'Navigate back' }));
669666
await screen.findByTestId('obj2.prop1');
670667

671-
// We tried to cache the form data, however that broke back button functionality for some apps.
672-
// See this issue: https://github.com/Altinn/app-frontend-react/issues/2564
673-
// Also see src/features/formData/useFormDataQuery.tsx where we prevent caching for statless apps
674-
expect(queries.fetchFormData).toHaveBeenCalledTimes(2);
675-
676668
// Our mock fetchFormData returns an empty object, so the form data should be reset. Realistically, the form data
677669
// would be restored when fetching it from the server, as we asserted that it was saved before navigating away.
678670
expect(screen.getByTestId('obj2.prop1')).toHaveValue('');
@@ -719,14 +711,15 @@ describe('FormData', () => {
719711
}
720712

721713
async function render(props: MinimalRenderProps = {}) {
714+
jest.mocked(fetchFormData).mockImplementationOnce(async () => ({
715+
obj3: {
716+
prop1: null,
717+
},
718+
}));
719+
722720
const utils = await statelessRender({
723721
renderer: <InvalidReadWrite path='obj3.prop1' />,
724722
queries: {
725-
fetchFormData: async () => ({
726-
obj3: {
727-
prop1: null,
728-
},
729-
}),
730723
...props.queries,
731724
},
732725
...props,

src/features/formData/FormDataReaders.test.tsx

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import React from 'react';
22

33
import { beforeAll, expect, jest } from '@jest/globals';
44
import { screen, waitFor } from '@testing-library/react';
5+
import { AxiosError } from 'axios';
56
import { v4 as uuidv4 } from 'uuid';
67

78
import { getIncomingApplicationMetadataMock } from 'src/__mocks__/getApplicationMetadataMock';
89
import { getInstanceDataMock } from 'src/__mocks__/getInstanceDataMock';
910
import { getLayoutSetsMock } from 'src/__mocks__/getLayoutSetsMock';
1011
import { DataModelFetcher } from 'src/features/formData/FormDataReaders';
1112
import { Lang } from 'src/features/language/Lang';
12-
import { fetchApplicationMetadata, fetchInstanceData } from 'src/queries/queries';
13+
import { fetchApplicationMetadata, fetchFormData, fetchInstanceData } from 'src/queries/queries';
1314
import { renderWithInstanceAndLayout } from 'src/test/renderWithProviders';
1415
import type { IRawTextResource } from 'src/features/language/textResources';
1516
import type { IData, IDataType } from 'src/types/shared';
@@ -57,6 +58,20 @@ async function render(props: TestProps) {
5758
}),
5859
);
5960
jest.mocked(fetchInstanceData).mockImplementationOnce(async () => instanceData);
61+
jest.mocked(fetchFormData).mockImplementation(async (url) => {
62+
const path = new URL(url).pathname;
63+
const id = path.split('/').pop();
64+
const modelName = idToNameMap[id!];
65+
const formData = props.dataModels[modelName];
66+
if (formData instanceof Error) {
67+
return Promise.reject(formData);
68+
}
69+
if (!formData) {
70+
throw new Error(`No form data mocked for testing (modelName = ${modelName})`);
71+
}
72+
73+
return formData;
74+
});
6075

6176
function generateDataElements(instanceId: string): IData[] {
6277
return dataModelNames.map((name) => {
@@ -124,19 +139,6 @@ async function render(props: TestProps) {
124139
resources: props.textResources,
125140
language: 'nb',
126141
}),
127-
fetchFormData: async (url) => {
128-
const path = new URL(url).pathname;
129-
const id = path.split('/').pop();
130-
const modelName = idToNameMap[id!];
131-
const formData = props.dataModels[modelName];
132-
if (formData instanceof Error) {
133-
return Promise.reject(formData);
134-
}
135-
if (!formData) {
136-
throw new Error(`No form data mocked for testing (modelName = ${modelName})`);
137-
}
138-
return formData;
139-
},
140142
},
141143
});
142144

@@ -162,7 +164,7 @@ describe('FormDataReaders', () => {
162164
it.each<string>(['someModel', 'someModel1.0'])(
163165
'simple, should render a resource with a variable lookup - %s',
164166
async (modelName: string) => {
165-
const { queries, urlFor } = await render({
167+
await render({
166168
ids: ['test'],
167169
textResources: [
168170
{
@@ -186,9 +188,6 @@ describe('FormDataReaders', () => {
186188

187189
await waitFor(() => expect(screen.getByTestId('test')).toHaveTextContent('Hello World'));
188190

189-
expect(queries.fetchFormData).toHaveBeenCalledTimes(1);
190-
expect(queries.fetchFormData).toHaveBeenCalledWith(urlFor(modelName), {});
191-
192191
expect(window.logError).not.toHaveBeenCalled();
193192
expect(window.logErrorOnce).not.toHaveBeenCalled();
194193
},
@@ -197,13 +196,13 @@ describe('FormDataReaders', () => {
197196
it('advanced, should fetch data from multiple models, handle failures', async () => {
198197
jest.useFakeTimers();
199198
const missingError = new Error('This should fail when fetching');
200-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
201-
(missingError as any).isAxiosError = true;
199+
200+
(missingError as AxiosError).isAxiosError = true;
202201

203202
const model2Promise = new Promise((resolve) => {
204203
setTimeout(() => resolve({ name: 'Universe' }), 100);
205204
});
206-
const { queries, urlFor } = await render({
205+
await render({
207206
ids: ['test1', 'test2', 'test3', 'testDefault', 'testMissing', 'testMissingWithDefault'],
208207
textResources: [
209208
{
@@ -310,11 +309,6 @@ describe('FormDataReaders', () => {
310309
await waitFor(() => expect(screen.getByTestId('test2')).toHaveTextContent('Hello Universe'));
311310
expect(screen.getByTestId('test3')).toHaveTextContent('You are [missing] year(s) old');
312311

313-
expect(queries.fetchFormData).toHaveBeenCalledTimes(3);
314-
expect(queries.fetchFormData).toHaveBeenCalledWith(urlFor('model1'), {});
315-
expect(queries.fetchFormData).toHaveBeenCalledWith(urlFor('model2'), {});
316-
expect(queries.fetchFormData).toHaveBeenCalledWith(urlFor('modelMissing'), {});
317-
318312
expect(window.logError).toHaveBeenCalledTimes(1);
319313
expect(window.logError).toHaveBeenCalledWith('Fetching form data failed:\n', missingError);
320314

src/features/formData/useDataModelBindings.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { userEvent } from '@testing-library/user-event';
77
import { defaultDataTypeMock } from 'src/__mocks__/getLayoutSetsMock';
88
import { FD } from 'src/features/formData/FormDataWrite';
99
import { useDataModelBindings } from 'src/features/formData/useDataModelBindings';
10+
import { fetchFormData } from 'src/queries/queries';
1011
import { renderWithInstanceAndLayout } from 'src/test/renderWithProviders';
1112
import type { IDataModelPatchResponse } from 'src/features/formData/types';
1213

@@ -92,10 +93,10 @@ describe('useDataModelBindings', () => {
9293
}
9394

9495
async function render({ formData = {} }: { formData?: object } = {}) {
96+
jest.mocked(fetchFormData).mockImplementationOnce(async () => formData);
9597
return await renderWithInstanceAndLayout({
9698
renderer: <DummyComponent />,
9799
queries: {
98-
fetchFormData: async () => formData,
99100
fetchDataModelSchema: async () => ({
100101
type: 'object',
101102
properties: {

0 commit comments

Comments
 (0)