This repository has been archived by the owner on Jun 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add async fetch flow to Counter component
- Loading branch information
Showing
6 changed files
with
222 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,135 @@ | ||
import { screen, fireEvent } from "@testing-library/react"; | ||
import { screen, fireEvent, wait } from "@testing-library/react"; | ||
import Counter from "./Counter"; | ||
import React from "react"; | ||
import counterReducer, { increment, decrement } from "./CounterSlice"; | ||
import counterReducer, { | ||
increment, | ||
decrement, | ||
fetchValueStart, | ||
fetchValueSuccess, | ||
fetchValueError | ||
} from "./CounterSlice"; | ||
import { renderWithRedux, rootInitialState } from "utils/test-helpers"; | ||
import axios from "axios"; | ||
|
||
test("displays zero as initial value", () => { | ||
renderWithRedux(<Counter />); | ||
expect(screen.getByText("0")).toBeVisible(); | ||
}); | ||
jest.mock("axios"); | ||
const mockedAxios = axios as jest.Mocked<typeof axios>; | ||
|
||
test("calls increment on click", () => { | ||
const { mockStore } = renderWithRedux(<Counter />); | ||
fireEvent.click(screen.getByText(/increment/i)); | ||
expect(mockStore.getActions()).toEqual([{ type: increment.type }]); | ||
}); | ||
describe("<Counter />", () => { | ||
test("shows zero as initial value", () => { | ||
renderWithRedux(<Counter />); | ||
expect(screen.getByText("0")).toBeVisible(); | ||
}); | ||
|
||
test("calls decrement on click", () => { | ||
const { mockStore } = renderWithRedux(<Counter />); | ||
fireEvent.click(screen.getByText(/decrement/i)); | ||
expect(mockStore.getActions()).toEqual([{ type: decrement.type }]); | ||
}); | ||
test("shows loading message", () => { | ||
renderWithRedux(<Counter />, { | ||
...rootInitialState, | ||
counter: { ...rootInitialState.counter, loading: true } | ||
}); | ||
expect(screen.getByText(/loading/i)).toBeVisible(); | ||
}); | ||
|
||
test("shows error message", () => { | ||
const errorMessage = "An error occured"; | ||
renderWithRedux(<Counter />, { | ||
...rootInitialState, | ||
counter: { ...rootInitialState.counter, error: errorMessage } | ||
}); | ||
expect(screen.getByText(errorMessage)).toBeVisible(); | ||
}); | ||
|
||
test("calls increment on click", () => { | ||
const { mockStore } = renderWithRedux(<Counter />); | ||
fireEvent.click(screen.getByText(/increment/i)); | ||
expect(mockStore.getActions()).toEqual([{ type: increment.type }]); | ||
}); | ||
|
||
test("calls decrement on click", () => { | ||
const { mockStore } = renderWithRedux(<Counter />); | ||
fireEvent.click(screen.getByText(/decrement/i)); | ||
expect(mockStore.getActions()).toEqual([{ type: decrement.type }]); | ||
}); | ||
|
||
test("slow fetch success", async () => { | ||
const name = "cra-template-typekit"; | ||
mockedAxios.get.mockResolvedValueOnce({ status: 200, data: { name } }); | ||
jest.useFakeTimers(); | ||
|
||
const { mockStore } = renderWithRedux(<Counter />); | ||
|
||
fireEvent.click(screen.getByText(/slow fetch/i)); | ||
|
||
jest.runAllTimers(); | ||
// Normally we would wait for an element to show up | ||
// https://github.com/testing-library/react-testing-library#complex-example | ||
await wait(() => null, { timeout: 500 }); | ||
|
||
test("increases number by one", () => { | ||
expect( | ||
counterReducer({ ...rootInitialState.counter, value: 1 }, increment) | ||
).toEqual({ ...rootInitialState.counter, value: 2 }); | ||
expect(mockStore.getActions()).toEqual([ | ||
{ type: fetchValueStart.type, payload: undefined }, | ||
{ type: fetchValueSuccess.type, payload: name.length } | ||
]); | ||
}); | ||
|
||
test("slow fetch error", async () => { | ||
mockedAxios.get.mockResolvedValueOnce({ status: 500 }); | ||
jest.useFakeTimers(); | ||
|
||
const { mockStore } = renderWithRedux(<Counter />); | ||
|
||
fireEvent.click(screen.getByText(/slow fetch/i)); | ||
|
||
jest.runAllTimers(); | ||
// Normally we would wait for an element to show up | ||
// https://github.com/testing-library/react-testing-library#complex-example | ||
await wait(() => null, { timeout: 500 }); | ||
|
||
expect(mockStore.getActions()).toEqual([ | ||
{ type: fetchValueStart.type, payload: undefined }, | ||
{ type: fetchValueError.type, payload: "Something went wrong." } | ||
]); | ||
}); | ||
}); | ||
|
||
test("decreases number by one", () => { | ||
expect( | ||
counterReducer({ ...rootInitialState.counter, value: 1 }, decrement) | ||
).toEqual({ ...rootInitialState.counter, value: 0 }); | ||
describe("CounterSlice", () => { | ||
test("sets loading on fetch start", () => { | ||
expect( | ||
counterReducer( | ||
{ ...rootInitialState.counter, loading: false }, | ||
fetchValueStart | ||
) | ||
).toEqual({ ...rootInitialState.counter, loading: true }); | ||
}); | ||
|
||
test("sets value and stop loading on fetch success", () => { | ||
expect( | ||
counterReducer( | ||
{ ...rootInitialState.counter, loading: true }, | ||
{ type: fetchValueSuccess, payload: 100 } | ||
) | ||
).toEqual({ ...rootInitialState.counter, loading: false, value: 100 }); | ||
}); | ||
|
||
test("sets error and stop loading on fetch error", () => { | ||
expect( | ||
counterReducer( | ||
{ ...rootInitialState.counter, loading: true }, | ||
{ type: fetchValueError, payload: "Some error message." } | ||
) | ||
).toEqual({ | ||
...rootInitialState.counter, | ||
loading: false, | ||
error: "Some error message." | ||
}); | ||
}); | ||
|
||
test("increases number by one", () => { | ||
expect( | ||
counterReducer({ ...rootInitialState.counter, value: 1 }, increment) | ||
).toEqual({ ...rootInitialState.counter, value: 2 }); | ||
}); | ||
|
||
test("decreases number by one", () => { | ||
expect( | ||
counterReducer({ ...rootInitialState.counter, value: 1 }, decrement) | ||
).toEqual({ ...rootInitialState.counter, value: 0 }); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { AppDispatch, AppThunk } from "../../store"; | ||
import { | ||
fetchValueStart, | ||
fetchValueSuccess, | ||
fetchValueError | ||
} from "./CounterSlice"; | ||
import axios from "axios"; | ||
|
||
export const sleep = (t = Math.random() * 200 + 300) => | ||
new Promise(resolve => setTimeout(resolve, t)); | ||
|
||
export const fetchInitialValue = (): AppThunk => async ( | ||
dispatch: AppDispatch | ||
) => { | ||
dispatch(fetchValueStart()); | ||
try { | ||
// For demo purpose let's say out value is the length | ||
// of project name in manifest and the api call is slow | ||
await sleep(); | ||
const response = await axios.get("/manifest.json"); | ||
const value = response.data.name.length; | ||
dispatch(fetchValueSuccess(value)); | ||
} catch (e) { | ||
dispatch(fetchValueError("Something went wrong.")); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/* istanbul ignore file */ | ||
export const sleep = (t = Math.random() * 200 + 300) => | ||
new Promise(resolve => setTimeout(resolve, t)); |