Skip to content

Commit 574ca91

Browse files
olemartinorgOle Martin Handeland
andcommitted
Bug/form data caching (#3974)
* Fixing the bug (turns out it was much easier than I first thought!) and adding a Cypress test that reliably reproduces it. * New text in app heading * Adding a failsafe to make sure sharedperson data model is not used in components --------- Co-authored-by: Ole Martin Handeland <git@olemartin.org>
1 parent 308757b commit 574ca91

3 files changed

Lines changed: 80 additions & 13 deletions

File tree

src/features/formData/useFormDataQuery.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,18 @@ export function useFormDataQueryDef(url: string | undefined): QueryDefinition<un
1616
const { fetchFormData } = useAppQueries();
1717
const queryKey = useFormDataQueryKey(url);
1818
const options = useFormDataQueryOptions();
19-
const isStateless = useApplicationMetadata().isStatelessApp;
20-
2119
const queryFn = url ? () => fetchFormData(url, options) : skipToken;
2220

23-
if (isStateless) {
24-
// We need to refetch for stateless apps as caching will break some apps.
25-
// See this issue: https://github.com/Altinn/app-frontend-react/issues/2564
26-
return {
27-
queryKey,
28-
queryFn,
29-
gcTime: 0,
30-
};
31-
}
32-
3321
return {
3422
queryKey,
3523
queryFn,
24+
25+
// We always have to refetch as caching will break some apps.
26+
// See this issue: https://github.com/Altinn/app-frontend-react/issues/2564
27+
// This is also important for https://github.com/Altinn/app-frontend-react/issues/3634, and without 'gcTime: 0'
28+
// old query cache will be used to initialize FormDataWrite even after invalidateFormDataQueries() has been called.
29+
gcTime: 0,
30+
3631
refetchInterval: false,
3732
};
3833
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { AppFrontend } from 'test/e2e/pageobjects/app-frontend';
2+
3+
import type { IRawDataModelBinding } from 'src/layout/common.generated';
4+
5+
const appFrontend = new AppFrontend();
6+
7+
describe('fetching new data from models', () => {
8+
/**
9+
* Reproduction and regression test for https://github.com/Altinn/app-frontend-react/issues/3634
10+
*
11+
* This bug relies on the following structure:
12+
* - A data model is created in Task_1, but there is no data in it
13+
* - A text resource (not a component!) references this data model, so it will be fetched in FormDataReaders in Task_1
14+
* - A backend IProcessTaskEnd handler updates the data model when ending Task_1
15+
* - When Task_2 starts, frontend doesn't notice that the data model it had in query cache was outdated, so it
16+
* fails to load it (again), thus getting stuck with the outdated data as initial data in FormDataWrite.
17+
*/
18+
it('should have fetched new data when entering Task_2', () => {
19+
cy.startAppInstance(appFrontend.apps.multipleDatamodelsTest);
20+
cy.findByRole('textbox', { name: /tekstfelt 1/i }).type('første');
21+
cy.findByRole('textbox', { name: /tekstfelt 2/i }).type('andre');
22+
23+
cy.gotoNavPage('Side3');
24+
25+
cy.findByRole('button', { name: /legg til ny/i }).click();
26+
cy.findByRole('textbox', { name: /fornavn/i }).type('Per');
27+
cy.findByRole('textbox', { name: /etternavn/i }).type('Hansen');
28+
cy.findAllByRole('button', { name: /lagre og lukk/i })
29+
.first()
30+
.click();
31+
32+
cy.changeLayout((component) => {
33+
for (const _dmb of Object.values(component.dataModelBindings ?? {})) {
34+
const binding = _dmb as IRawDataModelBinding;
35+
if (typeof binding === 'object' && binding.dataType === 'sharedperson') {
36+
// If any component starts referencing this data model, the reproduction breaks. This bug specifically relied
37+
// on this data model only being references via a text resource in Task_1.
38+
throw new Error('Found sharedperson data model binding in Task_1');
39+
}
40+
}
41+
});
42+
43+
cy.gotoNavPage('Side6');
44+
cy.findByRole('radio', { name: /kåre/i }).dsCheck();
45+
cy.waitUntilSaved();
46+
cy.findByText(/Du må rette disse feilene før du kan gå videre/i).should('not.exist');
47+
48+
cy.intercept('GET', '**/data/**', (req) => {
49+
req.reply((res) => {
50+
if (res.body.name === 'Ola Nordmann') {
51+
// Delaying the response for this data model specifically. When reproducing this bug, it relied on the query
52+
// cache for an earlier request to this data model, so if the request ends before the other data models are
53+
// fetched it might cover the underlying bug.
54+
res.setDelay(500);
55+
}
56+
});
57+
}).as('fetchData');
58+
59+
cy.findByRole('button', { name: /send inn/i }).click();
60+
61+
cy.findByRole('heading', { name: /fra forrige steg/i }).should('be.visible');
62+
cy.findByText(/Du må rette disse feilene før du kan gå videre/i).should('not.exist');
63+
cy.findByRole('button', { name: 'Neste' }).click();
64+
65+
// If the bug regresses, these fields will be empty
66+
cy.findByRole('textbox', { name: 'Navn' }).should('have.value', 'Ola Nordmann');
67+
cy.findByRole('textbox', { name: 'Adresse' }).should('have.value', 'Testveien 123');
68+
cy.findByRole('textbox', { name: 'Postnr' }).should('have.value', '4609');
69+
cy.findByRole('textbox', { name: 'Poststed' }).should('have.value', 'KARDEMOMME BY');
70+
cy.waitUntilSaved();
71+
});
72+
});

test/e2e/integration/multiple-datamodels-test/readonly.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ describe('readonly data models', () => {
116116

117117
cy.findByRole('button', { name: 'Neste' }).click();
118118

119-
cy.findByRole('heading', { name: /tittel/i }).should('be.visible');
119+
cy.findByRole('heading', { name: /Test av delt modell/ }).should('be.visible');
120120
cy.waitUntilSaved();
121121

122122
cy.then(() => expect(formDataRequests.length).to.be.eq(1));

0 commit comments

Comments
 (0)