Skip to content

Commit 11d30d5

Browse files
committed
Small improvements
- Trim search query - obvious in hindsight. Does away with the need to set isSearching to false if the search query is falsy. - Rename test spec + add inline snapshot comparison that checks for the existence of forecast items. - Run test coverage on pre-commit.
1 parent 59e3402 commit 11d30d5

File tree

3 files changed

+30
-19
lines changed

3 files changed

+30
-19
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
},
6060
"husky": {
6161
"hooks": {
62-
"pre-commit": "pretty-quick --staged && npm run lint && npm run format && npm run check && npm run test"
62+
"pre-commit": "pretty-quick --staged && npm run lint && npm run format && npm run check && npm run coverage"
6363
}
6464
},
6565
"jest": {

src/__tests__/app.test.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import App from '../components/app';
1212

1313
describe('<App />', () => {
1414
beforeAll(() => jest.useFakeTimers());
15-
1615
afterAll(() => jest.clearAllTimers());
1716

1817
const history = createMemoryHistory();
@@ -24,7 +23,7 @@ describe('<App />', () => {
2423
</Router>,
2524
);
2625

27-
test('renders the app', async () => {
26+
test('fetches and renders the current weather and a five day forecast', async () => {
2827
renderApp();
2928

3029
await waitForLoadingToFinish();
@@ -36,15 +35,28 @@ describe('<App />', () => {
3635
expect(
3736
screen.getByPlaceholderText(/search for a location/i),
3837
).toBeInTheDocument();
39-
await screen.findByText(/eldoret, ke/i);
38+
39+
expect(screen.getByText(/eldoret, ke/i)).toBeInTheDocument();
4040
expect(screen.getByText(/broken clouds/i)).toBeInTheDocument();
4141
expect(screen.getByText(/feels like 18°/i)).toBeInTheDocument();
4242
expect(screen.getByText(/30m\/s winds/i)).toBeInTheDocument();
4343
expect(screen.getByText(/49% humidity/i)).toBeInTheDocument();
4444
expect(
4545
screen.getByText(/'Netflix and chill' weather. It's pleasant outside/i),
4646
).toBeInTheDocument();
47-
expect(screen.getAllByRole('list').length).toEqual(5);
47+
expect(screen.getAllByRole('listitem').length).toEqual(5);
48+
const forecast = screen.getAllByRole('listitem').map((listItem) => {
49+
return listItem.textContent;
50+
});
51+
expect(forecast).toMatchInlineSnapshot(`
52+
Array [
53+
"Saturday22° / 22°",
54+
"Sunday22° / 22°",
55+
"Monday22° / 22°",
56+
"Tuesday20° / 20°",
57+
"Wednesday21° / 21°",
58+
]
59+
`);
4860
expect(screen.getByText(/Open source by/i)).toBeInTheDocument();
4961
expect(screen.getByText(/Dennis Kigen/i)).toBeInTheDocument();
5062
expect(screen.getByText(/©2020 - now/i)).toBeInTheDocument();

src/components/app.js

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ function viewStateReducer(state, action) {
4141
const App = () => {
4242
const [location, setLocation] = React.useState('Eldoret');
4343
const [debouncedSearchTerm, setDebouncedSearchTerm] = React.useState('');
44-
const [isSearching, setIsSearching] = React.useState(false);
4544
const [units, setUnits] = React.useState('metric');
46-
const [state, dispatch] = React.useReducer(viewStateReducer, {
45+
const [isSearching, setIsSearching] = React.useState(false);
46+
const [viewState, dispatch] = React.useReducer(viewStateReducer, {
4747
status: 'started',
4848
error: null,
4949
forecast: [],
@@ -59,12 +59,11 @@ const App = () => {
5959
);
6060

6161
const handleLocationChange = (event) => {
62-
if (event.target.value) {
62+
const query = event.target.value.trim();
63+
if (query) {
6364
setIsSearching(true);
64-
} else {
65-
setIsSearching(false);
6665
}
67-
debounceSearch(event.target.value);
66+
debounceSearch(query);
6867
};
6968

7069
const handleUnitsChange = (newUnits) => {
@@ -75,7 +74,7 @@ const App = () => {
7574
if (debouncedSearchTerm) {
7675
setLocation(debouncedSearchTerm);
7776
}
78-
}, [debouncedSearchTerm, isSearching]);
77+
}, [debouncedSearchTerm]);
7978

8079
React.useEffect(() => {
8180
async function getWeather() {
@@ -129,8 +128,8 @@ const App = () => {
129128
<NavBar />
130129
<Switch>
131130
<Route exact path="/">
132-
{state.status === 'started' ? <Loading /> : null}
133-
{state.status === 'rejected' ? (
131+
{viewState.status === 'started' ? <Loading /> : null}
132+
{viewState.status === 'rejected' ? (
134133
<div className="w-3/5 md:w-3/5 lg:w-1/2 m-auto">
135134
<div className="mx-auto sm:max-w-xl 2xl:max-w-2xl">
136135
<div
@@ -150,14 +149,14 @@ const App = () => {
150149
fillRule="evenodd"
151150
/>
152151
</svg>
153-
<span>{state.error.message}</span>
152+
<span>{viewState.error.message}</span>
154153
</div>
155154
</div>
156155
</div>
157156
</div>
158157
) : null}
159-
{(state.weather && Object.keys(state.weather).length) ||
160-
(state.forecast && Object.keys(state.forecast).length) ? (
158+
{(viewState.weather && Object.keys(viewState.weather).length) ||
159+
(viewState.forecast && Object.keys(viewState.forecast).length) ? (
161160
<main>
162161
<div className="mx-auto w-5/6 md:w-full 2xl:max-w-7xl xl:max-w-6xl">
163162
<Search
@@ -166,8 +165,8 @@ const App = () => {
166165
onLocationChange={handleLocationChange}
167166
/>
168167
<WeatherCard
169-
forecast={state.forecast}
170-
weather={state.weather}
168+
forecast={viewState.forecast}
169+
weather={viewState.weather}
171170
units={units}
172171
onUnitsChange={handleUnitsChange}
173172
/>

0 commit comments

Comments
 (0)