Skip to content

Commit b42382a

Browse files
authored
Merge pull request #1163 from Cloudnest-Advisory/hotfix/new-master
Fix Species List Load 1161
2 parents 2aeb22a + 43e07f1 commit b42382a

5 files changed

Lines changed: 108 additions & 25 deletions

File tree

src/components/Species.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React, { useContext } from 'react';
1+
// import React, { useContext } from 'react';
2+
import React, { useContext, useEffect } from 'react';
23
import { TextField, CircularProgress } from '@material-ui/core';
34
import Autocomplete from '@material-ui/lab/Autocomplete';
45
import { withStyles } from '@material-ui/styles';
@@ -18,7 +19,13 @@ const styles = () => {
1819
function Species(props) {
1920
// Verify also uses speciesInput so keep it on context
2021
const speciesContext = useContext(SpeciesContext);
21-
22+
// Make sure the list is populated when we land on Verify directly.
23+
useEffect(() => {
24+
// Prefer ensureLoaded(); fall back to load() if that’s what your context exposes.
25+
speciesContext.ensureLoaded?.();
26+
speciesContext.load?.();
27+
// no await; Autocomplete will re-render when the list arrives
28+
}, []);
2229
return (
2330
<>
2431
{speciesContext.isLoading ? (

src/components/tests/species.test.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import '../../testMocks/mockSpeciesContext';
12
import React from 'react';
23
import { BrowserRouter } from 'react-router-dom';
34
import {
@@ -7,6 +8,7 @@ import {
78
within,
89
cleanup,
910
waitForElementToBeRemoved,
11+
waitFor,
1012
} from '@testing-library/react';
1113
import userEvent from '@testing-library/user-event';
1214
// import { session, hasPermission, POLICIES } from '../models/auth';
@@ -20,6 +22,22 @@ import { SPECIES } from './fixtures';
2022
import * as loglevel from 'loglevel';
2123
const log = loglevel.getLogger('../tests/species.test');
2224

25+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
26+
const qc = new QueryClient({
27+
defaultOptions: {
28+
queries: { retry: false, refetchOnWindowFocus: false },
29+
},
30+
});
31+
32+
const SpeciesProviderWithOverrides = ({ children, overrides }) => {
33+
const base = React.useContext(SpeciesContext);
34+
return (
35+
<SpeciesContext.Provider value={{ ...base, ...overrides }}>
36+
{children}
37+
</SpeciesContext.Provider>
38+
);
39+
};
40+
2341
describe('species management', () => {
2442
let api;
2543
let speciesValues;
@@ -67,13 +85,15 @@ describe('species management', () => {
6785
describe('<SpeciesView /> renders page', () => {
6886
beforeEach(async () => {
6987
render(
70-
<BrowserRouter>
71-
<AppProvider>
72-
<SpeciesProvider value={speciesValues}>
73-
<SpeciesView />
74-
</SpeciesProvider>
75-
</AppProvider>
76-
</BrowserRouter>
88+
<QueryClientProvider client={qc}>
89+
<BrowserRouter>
90+
<AppProvider>
91+
<SpeciesProvider value={speciesValues}>
92+
<SpeciesView />
93+
</SpeciesProvider>
94+
</AppProvider>
95+
</BrowserRouter>
96+
</QueryClientProvider>
7797
);
7898
await act(() => api.getSpecies());
7999
});
@@ -161,8 +181,10 @@ describe('species management', () => {
161181

162182
afterEach(cleanup);
163183

164-
it('api.createSpecies should be called with "water melon"', () => {
165-
expect(api.createSpecies.mock.calls[0][0].name).toBe('water melon');
184+
it('api.createSpecies should be called with "water melon"', async () => {
185+
await waitFor(() => {
186+
expect(api.createSpecies.mock.calls[0][0].name).toBe('water melon');
187+
});
166188
});
167189

168190
// it('species list should be 3 (1 added)', () => {

src/components/tests/verify.test.js

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ import {
4040
import * as loglevel from 'loglevel';
4141
const log = loglevel.getLogger('../tests/verify.test');
4242

43+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
44+
const qc = new QueryClient({
45+
defaultOptions: {
46+
queries: { retry: false, refetchOnWindowFocus: false },
47+
},
48+
});
49+
4350
jest.setTimeout(7000);
4451
jest.mock('../../api/growers');
4552
jest.mock('../../api/treeTrackerApi');
@@ -111,19 +118,21 @@ describe('Verify', () => {
111118
beforeEach(async () => {
112119
render(
113120
<ThemeProvider theme={theme}>
114-
<BrowserRouter>
115-
<AppProvider value={{ orgList: ORGS }}>
116-
<GrowerContext.Provider value={growerValues}>
117-
<VerifyProvider value={verifyValues}>
118-
<SpeciesProvider value={speciesValues}>
119-
<TagsContext.Provider value={tagsValues}>
120-
<Verify />
121-
</TagsContext.Provider>
122-
</SpeciesProvider>
123-
</VerifyProvider>
124-
</GrowerContext.Provider>
125-
</AppProvider>
126-
</BrowserRouter>
121+
<QueryClientProvider client={qc}>
122+
<BrowserRouter>
123+
<AppProvider value={{ orgList: ORGS }}>
124+
<GrowerContext.Provider value={growerValues}>
125+
<VerifyProvider value={verifyValues}>
126+
<SpeciesProvider value={speciesValues}>
127+
<TagsContext.Provider value={tagsValues}>
128+
<Verify />
129+
</TagsContext.Provider>
130+
</SpeciesProvider>
131+
</VerifyProvider>
132+
</GrowerContext.Provider>
133+
</AppProvider>
134+
</BrowserRouter>
135+
</QueryClientProvider>
127136
</ThemeProvider>
128137
);
129138

src/context/SpeciesContext.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,29 @@ export const SpeciesContext = createContext({
1414
editSpecies: () => {},
1515
deleteSpecies: () => {},
1616
combineSpecies: () => {},
17+
ensureLoaded: () => {},
1718
});
1819

1920
export function SpeciesProvider({ children }) {
2021
const [speciesInput, setSpeciesInput] = useState('');
2122
const queryClient = useQueryClient();
2223

23-
const { data: speciesList = [], isLoading } = useQuery({
24+
const { data: speciesList = [], isLoading, refetch } = useQuery({
2425
queryKey: ['species'],
2526
queryFn: () => api.getSpecies(), // API already has getSpecies
2627
staleTime: 1000 * 60 * 5, // cache for 5 mins
2728
refetchOnWindowFocus: false,
2829
enabled: false, // ✅ Disable automatic fetching until Home mounts
2930
});
3031

32+
// Manually trigger a load only when needed
33+
const ensureLoaded = async () => {
34+
const cached = queryClient.getQueryData(['species']);
35+
if (!cached || cached.length === 0) {
36+
await refetch();
37+
}
38+
};
39+
3140
// only used by Species dropdown
3241
const onChange = (text) => {
3342
console.log('on change:"', text, '"');
@@ -86,6 +95,7 @@ export function SpeciesProvider({ children }) {
8695
editSpecies,
8796
deleteSpecies,
8897
combineSpecies,
98+
ensureLoaded, // expose to components
8999
};
90100

91101
return (
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Ensure path points to ../context from inside testMocks folder
2+
jest.mock('../context/SpeciesContext', () => {
3+
const React = require('react');
4+
const actual = jest.requireActual('../context/SpeciesContext');
5+
6+
const mockLoadSpeciesList = jest.fn();
7+
const mockEnsureLoaded = jest.fn();
8+
9+
function WrappedSpeciesProvider(props) {
10+
return React.createElement(
11+
actual.SpeciesProvider,
12+
null,
13+
React.createElement(actual.SpeciesContext.Consumer, null, (ctx) =>
14+
React.createElement(
15+
actual.SpeciesContext.Provider,
16+
{
17+
value: {
18+
...ctx,
19+
loadSpeciesList: mockLoadSpeciesList, // injected
20+
ensureLoaded: mockEnsureLoaded, // optional
21+
},
22+
},
23+
props.children
24+
)
25+
)
26+
);
27+
}
28+
29+
return {
30+
...actual,
31+
SpeciesProvider: WrappedSpeciesProvider,
32+
__mocks: { mockLoadSpeciesList, mockEnsureLoaded },
33+
};
34+
});
35+
console.log('[TEST MOCK] SpeciesContext wrapper loaded');

0 commit comments

Comments
 (0)