Skip to content

Commit c56f7ed

Browse files
committed
Loading Players, error in api call, testing api calls y Game component test
1 parent b9a89c9 commit c56f7ed

File tree

10 files changed

+113
-18
lines changed

10 files changed

+113
-18
lines changed

.eslintrc.js

+3
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,8 @@ module.exports = {
2222
react: {
2323
version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
2424
}
25+
},
26+
env: {
27+
jest: true
2528
}
2629
};

package-lock.json

+7-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6-
"@testing-library/jest-dom": "^4.2.4",
76
"@testing-library/react": "^9.5.0",
87
"@testing-library/user-event": "^7.2.1",
98
"axios": "^0.19.2",
@@ -37,6 +36,7 @@
3736
]
3837
},
3938
"devDependencies": {
39+
"@testing-library/jest-dom": "^4.2.4",
4040
"@types/enzyme": "^3.10.5",
4141
"@types/enzyme-adapter-react-16": "^1.0.6",
4242
"@types/jest": "^24.9.1",

src/Components/CounterPanel/__tests__/CounterPanel.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ describe("<CounterPanel />", () => {
1616
.children()
1717
.at(5)
1818
.props(),
19-
).toEqual({ children: 6, inputColor: "grey" });
19+
).toEqual({ children: 6, inputColor: "yellow" });
2020
});
2121
});

src/Components/Game/StyledComponents/index.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ export const HeaderText = styled.span`
4848
justify-content: center;
4949
align-items: center;
5050
`;
51+
export const LoadingText = styled.span`
52+
margin: 5px 5px 0 5px;
53+
font-size: 18px;
54+
color: aliceblue;
55+
`;
5156
export const BottomContainer = styled.div`
5257
display: flex;
5358
height: 25px;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[
2+
{"first_name":"Stephen","fixture":{"_members":["112160"],"_ref":"fixtures.id"},"fppg":47.94303797468354,"id":"15475-9524","images":{"default":{"height":200,"url":"https://d17odppiik753x.cloudfront.net/playerimages/nba/9524.png","width":200}},"injured":false,"injury_details":"knee","injury_status":"o","last_name":"Curry","news":{"latest":"2016-05-02T18:35:15Z"},"played":79,"player_card_url":"https://www.fanduel.com/e/Player/9524/Stats/15475","position":"PG","removed":false,"salary":10600,"starting_order":null,"team":{"_members":["687"],"_ref":"teams.id"}},
3+
{"first_name":"Draymond","fixture":{"_members":["112160"],"_ref":"fixtures.id"},"fppg":38.9604938271605,"id":"15475-15860","images":{"default":{"height":200,"url":"https://d17odppiik753x.cloudfront.net/playerimages/nba/15860.png","width":200}},"injured":false,"injury_details":null,"injury_status":null,"last_name":"Green","news":{"latest":"2016-05-02T01:24:54Z"},"played":81,"player_card_url":"https://www.fanduel.com/e/Player/15860/Stats/15475","position":"PF","removed":false,"salary":9300,"starting_order":null,"team":{"_members":["687"],"_ref":"teams.id"}},
4+
{"first_name":"Damian","fixture":{"_members":["112160"],"_ref":"fixtures.id"},"fppg":39.37866666666667,"id":"15475-20848","images":{"default":{"height":200,"url":"https://d17odppiik753x.cloudfront.net/playerimages/nba/20848.png","width":200}},"injured":false,"injury_details":null,"injury_status":null,"last_name":"Lillard","news":{"latest":"2016-05-02T01:13:36Z"},"played":75,"player_card_url":"https://www.fanduel.com/e/Player/20848/Stats/15475","position":"PG","removed":false,"salary":8400,"starting_order":null,"team":{"_members":["703"],"_ref":"teams.id"}},
5+
{"first_name":"Hassan","fixture":{"_members":["112164"],"_ref":"fixtures.id"},"fppg":35.75342465753425,"id":"15475-12363","images":{"default":{"height":200,"url":"https://d17odppiik753x.cloudfront.net/playerimages/nba/12363.png","width":200}},"injured":false,"injury_details":null,"injury_status":null,"last_name":"Whiteside","news":{"latest":"2016-05-02T01:04:53Z"},"played":73,"player_card_url":"https://www.fanduel.com/e/Player/12363/Stats/15475","position":"C","removed":false,"salary":8000,"starting_order":null,"team":{"_members":["693"],"_ref":"teams.id"}},
6+
{"first_name":"Klay","fixture":{"_members":["112160"],"_ref":"fixtures.id"},"fppg":30.839999999999996,"id":"15475-14509","images":{"default":{"height":200,"url":"https://d17odppiik753x.cloudfront.net/playerimages/nba/14509.png","width":200}},"injured":false,"injury_details":null,"injury_status":null,"last_name":"Thompson","news":{"latest":"2016-05-02T01:29:23Z"},"played":80,"player_card_url":"https://www.fanduel.com/e/Player/14509/Stats/15475","position":"SG","removed":false,"salary":8000,"starting_order":null,"team":{"_members":["687"],"_ref":"teams.id"}},
7+
{"first_name":"Kyle","fixture":{"_members":["112164"],"_ref":"fixtures.id"},"fppg":38.5974025974026,"id":"15475-9535","images":{"default":{"height":200,"url":"https://d17odppiik753x.cloudfront.net/playerimages/nba/9535.png","width":200}},"injured":false,"injury_details":null,"injury_status":null,"last_name":"Lowry","news":{"latest":"2016-04-30T14:04:05Z"},"played":77,"player_card_url":"https://www.fanduel.com/e/Player/9535/Stats/15475","position":"PG","removed":false,"salary":7800,"starting_order":null,"team":{"_members":["706"],"_ref":"teams.id"}},
8+
{"first_name":"Dwyane","fixture":{"_members":["112164"],"_ref":"fixtures.id"},"fppg":31.43783783783784,"id":"15475-9585","images":{"default":{"height":200,"url":"https://d17odppiik753x.cloudfront.net/playerimages/nba/9585.png","width":200}},"injured":false,"injury_details":null,"injury_status":null,"last_name":"Wade","news":{"latest":"2016-04-30T14:15:19Z"},"played":74,"player_card_url":"https://www.fanduel.com/e/Player/9585/Stats/15475","position":"SG","removed":false,"salary":7500,"starting_order":null,"team":{"_members":["693"],"_ref":"teams.id"}},
9+
{"first_name":"DeMar","fixture":{"_members":["112164"],"_ref":"fixtures.id"},"fppg":35.26025641025641,"id":"15475-9714","images":{"default":{"height":200,"url":"https://d17odppiik753x.cloudfront.net/playerimages/nba/9714.png","width":200}},"injured":false,"injury_details":null,"injury_status":null,"last_name":"DeRozan","news":{"latest":"2016-05-02T12:29:25Z"},"played":78,"player_card_url":"https://www.fanduel.com/e/Player/9714/Stats/15475","position":"SG","removed":false,"salary":7400,"starting_order":null,"team":{"_members":["706"],"_ref":"teams.id"}},
10+
{"first_name":"C.J.","fixture":{"_members":["112160"],"_ref":"fixtures.id"},"fppg":31.653750000000002,"id":"15475-19067","images":{"default":{"height":200,"url":"https://d17odppiik753x.cloudfront.net/playerimages/nba/19067.png","width":200}},"injured":false,"injury_details":null,"injury_status":null,"last_name":"McCollum","news":{"latest":"2016-05-02T01:18:45Z"},"played":80,"player_card_url":"https://www.fanduel.com/e/Player/19067/Stats/15475","position":"SG","removed":false,"salary":7000,"starting_order":null,"team":{"_members":["703"],"_ref":"teams.id"}}
11+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from "react";
2+
import { render, cleanup, act, wait, waitForElement } from "@testing-library/react";
3+
import axios from "axios";
4+
import Game from "./../../Game";
5+
import tenPlayers from "./Data/tenPlayers.json";
6+
7+
beforeEach(() => {
8+
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
9+
// @ts-ignore
10+
axios.get = jest.fn(() => Promise.resolve({ data: { players: tenPlayers } }));
11+
});
12+
afterEach(cleanup);
13+
describe("Game", () => {
14+
it("displays text “Loading Players...” while fetching players", () => {
15+
const { getByText } = render(<Game />);
16+
17+
getByText("Loading Players...");
18+
});
19+
it("removes text “Loading Players...” after fetching players", () => {
20+
act(async () => {
21+
const { getByText } = render(<Game />);
22+
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
23+
// @ts-ignore
24+
await wait(() => expect(getByText("Loading Players...")).not.toBeInTheDocument());
25+
});
26+
});
27+
it("displays error in the api call", async () => {
28+
axios.get = jest.fn(() => Promise.resolve({ data: { players: [] } }));
29+
const { getByText } = render(<Game />);
30+
await wait(() => expect(getByText("Error Loading Players")).toBeTruthy());
31+
});
32+
it("displays the players received from the api call", async () => {
33+
await act(async () => {
34+
const { getAllByText } = render(<Game />);
35+
await waitForElement(() => getAllByText("Name: Stephen Curry"));
36+
getAllByText("Name: Stephen Curry");
37+
});
38+
});
39+
});

src/Components/Game/index.tsx

+44-13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
GlobalHeader,
1414
HeaderContainer,
1515
HeaderText,
16+
LoadingText,
1617
PlayersCardContainer,
1718
RestartButtonContainer,
1819
TopLogoImage,
@@ -25,23 +26,14 @@ const Game: React.FC = () => {
2526
const initialState = {
2627
score: 0,
2728
showResult: false,
29+
queryPerformed: false,
2830
gameOver: false,
2931
players: [],
3032
playersRaw: [],
3133
guessRight: PENDING_GUESS,
3234
};
3335

3436
const [state, setState] = useState<StateType>(initialState);
35-
const loadPlayers = async (): Promise<void> => {
36-
if (state.playersRaw.length === 0) {
37-
const players = await ApiService.getPlayers();
38-
setState({
39-
...state,
40-
players: players,
41-
playersRaw: players,
42-
});
43-
}
44-
};
4537
const handlePlayerClick = (
4638
playersToPlay: Array<Player>,
4739
playerOne: Player,
@@ -69,11 +61,44 @@ const Game: React.FC = () => {
6961
});
7062
};
7163
const handleRestartGameClick = (): void => {
72-
setState(initialState);
64+
setState({
65+
...state,
66+
score: 0,
67+
showResult: false,
68+
queryPerformed: true,
69+
gameOver: false,
70+
players: state.playersRaw,
71+
guessRight: PENDING_GUESS,
72+
});
7373
};
7474
useEffect(() => {
75-
loadPlayers();
76-
});
75+
// Basic implementation to handle race conditions
76+
// When component might unmount before API call finishes
77+
let isStopped = false;
78+
if (!isStopped) {
79+
const loadPlayers = async (): Promise<void> => {
80+
try {
81+
const players = await ApiService.getPlayers();
82+
if (!isStopped && players) {
83+
setState({
84+
...state,
85+
queryPerformed: true,
86+
players: players,
87+
playersRaw: players,
88+
});
89+
}
90+
} catch (error) {
91+
console.error(error);
92+
}
93+
};
94+
loadPlayers();
95+
}
96+
return () => {
97+
isStopped = true;
98+
};
99+
// eslint-disable-next-line react-hooks/exhaustive-deps
100+
}, []);
101+
77102
const playersToPlay = state.players.slice();
78103
const score = state.score;
79104
const playerOne = playersToPlay.shift();
@@ -90,6 +115,12 @@ const Game: React.FC = () => {
90115
</HeaderContainer>
91116
<CounterPanel score={state.score} />
92117
<PlayersCardContainer>
118+
{state.players.length === 0 &&
119+
(state.queryPerformed ? (
120+
<LoadingText>Error Loading Players</LoadingText>
121+
) : (
122+
<LoadingText>Loading Players...</LoadingText>
123+
))}
93124
{score < MAX_SCORE && playerOne && playerTwo && (
94125
<PlayerCard
95126
firstName={playerOne.first_name}

src/Components/interfaces.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export interface StateType {
4242
players: Array<Player>;
4343
playersRaw: Array<Player>;
4444
showResult: boolean;
45+
queryPerformed: boolean;
4546
guessRight: number;
4647
gameOver: boolean;
4748
}

src/Components/utils.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import { Player } from "./interfaces";
55
export default class ApiService {
66
static async getPlayers(): Promise<Array<Player>> {
77
const response = await axios.get(DATA_URL);
8-
return response.data.players;
8+
return response.data.players ? response.data.players : [];
99
}
1010
}

0 commit comments

Comments
 (0)