Skip to content

Commit f72fa83

Browse files
AndrejGeorgievAndrej Gjeorgiev
andauthored
fix crash or not rendering when translations not available (#26)
* Default to previous translations in case a faulty translation is fetched or fetching fails. * Rename tests to reflect current code logic and squash one network failure tests into one that covers everything. Co-authored-by: Andrej Gjeorgiev <[email protected]>
1 parent d4b8553 commit f72fa83

File tree

2 files changed

+46
-7
lines changed

2 files changed

+46
-7
lines changed

src/lib/FetchingProvider.test.tsx

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,31 @@ describe('FetchingProvider', () => {
6464
expect(global.fetch).toHaveBeenCalledTimes(1);
6565
});
6666

67+
it('fetches text resources and defaults to previous state if result is undefined', async () => {
68+
// @ts-ignore
69+
global.fetch = jest.fn(() =>
70+
Promise.resolve({
71+
json: () => Promise.resolve(undefined),
72+
}),
73+
);
74+
75+
function TestComponent() {
76+
return (
77+
<FetchingProvider url="http://any.uri/texts?lang=en">
78+
<Spy />
79+
</FetchingProvider>
80+
);
81+
}
82+
83+
const { getByText } = RTL.render(<TestComponent />);
84+
const spyNode = await RTL.waitForElement(() => getByText('hello.world'));
85+
86+
expect(spyNode).toBeDefined();
87+
expect(spyNode.innerHTML).toBe('hello.world');
88+
// @ts-ignore
89+
expect(global.fetch).toHaveBeenCalledTimes(1);
90+
});
91+
6792
it('fetches text resources when url prop changes', async () => {
6893
const transform = jest.fn(x => x);
6994
const onFetchingStart = jest.fn();
@@ -98,15 +123,25 @@ describe('FetchingProvider', () => {
98123
expect(global.fetch).toHaveBeenCalledTimes(2);
99124
});
100125

101-
it('invokes onFetchingError lifecycle on network failure', async () => {
126+
it('invokes onFetchingError lifecycle and defaults to previous state on network failure', async () => {
102127
const onFetchingError = jest.fn();
103128
const faultyFetch = jest.fn(() => Promise.reject(new Error('Failure')));
104129
// @ts-ignore
105130
global.fetch = faultyFetch;
106131

107-
RTL.render(<FetchingProvider url="http://any.uri/texts" onFetchingError={onFetchingError} children={null} />);
108-
await RTL.wait(); // until fetch() rejects
132+
function TestComponent() {
133+
return (
134+
<FetchingProvider url="http://your.website.uri/texts?lang=en" onFetchingError={onFetchingError}>
135+
<Spy />
136+
</FetchingProvider>
137+
);
138+
}
139+
140+
const { getByText } = RTL.render(<TestComponent />);
141+
const spyNode = await RTL.waitForElement(() => getByText('hello.world'));
109142

143+
expect(spyNode).toBeDefined();
144+
expect(spyNode.innerHTML).toBe('hello.world');
110145
expect(faultyFetch).toHaveBeenCalledTimes(1);
111146
expect(onFetchingError).toHaveBeenCalledTimes(1);
112147
});

src/lib/FetchingProvider.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export interface FetchingProviderApi {
3939

4040
/**
4141
* Invoked when fetching fails.
42+
* Note that the previous translations or an empty translation map will be used in case translations fails by default.
4243
*/
4344
onFetchingError?: (e: Error) => void;
4445

@@ -85,14 +86,17 @@ export function FetchingProvider(props: FetchingProviderApi) {
8586
.then(r => r.json())
8687
.then(response => {
8788
if (isStillMounted) {
88-
setState({
89-
translations: transform(response),
89+
setState(prevState => ({
90+
translations: transform(response) || prevState.translations,
9091
isFetched: true,
91-
});
92+
}));
9293
onFetchingEnd();
9394
}
9495
})
95-
.catch(onFetchingError);
96+
.catch(e => {
97+
onFetchingError(e);
98+
setState(prevState => ({ ...prevState, isFetched: true }));
99+
});
96100

97101
return () => {
98102
isStillMounted = false;

0 commit comments

Comments
 (0)