diff --git a/src/App.jsx b/src/App.jsx index 94746af2e5..4dc9fd4502 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,24 +1,29 @@ -import React from 'react' -import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' -import { useApi } from './useApi' -import LoadingSpinner from './LoadingSpinner' -import ErrorMessage from './ErrorMessage' -import PokemonPage from './PokemonPage' -import PokemonList from './PokemonList' +import React from "react"; +import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; +import { useApi } from "./useApi"; +import LoadingSpinner from "./LoadingSpinner"; +import ErrorMessage from "./ErrorMessage"; +import PokemonPage from "./PokemonPage"; +import PokemonList from "./PokemonList"; -const mapResults = (({ results }) => results.map(({ url, name }) => ({ - url, - name, - id: parseInt(url.match(/\/(\d+)\//)[1]) -}))) +const mapResults = ({ results }) => + results.map(({ url, name }) => ({ + url, + name, + id: parseInt(url.match(/\/(\d+)\//)[1]), + })); const App = () => { - const { data: pokemonList, error, isLoading } = useApi('https://pokeapi.co/api/v2/pokemon/?limit=50', mapResults) + const { + data: pokemonList, + error, + isLoading, + } = useApi("https://pokeapi.co/api/v2/pokemon/?limit=50", mapResults); if (isLoading) { - return + return ; } if (error) { - return + return ; } return ( @@ -27,15 +32,26 @@ const App = () => { - { - const pokemonId = pokemonList.find(({ name }) => name === routeParams.match.params.name).id - const previous = pokemonList.find(({ id }) => id === pokemonId - 1) - const next = pokemonList.find(({ id }) => id === pokemonId + 1) - return - }} /> + { + const pokemonId = pokemonList.find( + ({ name }) => name === routeParams.match.params.name + ).id; + const previous = pokemonList.find(({ id }) => id === pokemonId - 1); + const next = pokemonList.find(({ id }) => id === pokemonId + 1); + return ( + + ); + }} + /> - ) -} + ); +}; -export default App +export default App; diff --git a/src/ErrorMessage.jsx b/src/ErrorMessage.jsx index 98db6b219c..ccacb4cfc2 100644 --- a/src/ErrorMessage.jsx +++ b/src/ErrorMessage.jsx @@ -1,7 +1,7 @@ -import React from 'react' +import React from "react"; const ErrorMessage = ({ error }) => (
An error occured: {error.toString()}
-) +); -export default ErrorMessage +export default ErrorMessage; diff --git a/src/LoadingSpinner.jsx b/src/LoadingSpinner.jsx index 2ce79c3a64..c935b03e86 100644 --- a/src/LoadingSpinner.jsx +++ b/src/LoadingSpinner.jsx @@ -1,7 +1,11 @@ -import React from 'react' +import React from "react"; const LoadingSpinner = () => ( - Loading... -) + Loading... +); -export default LoadingSpinner +export default LoadingSpinner; diff --git a/src/PokemonAbility.jsx b/src/PokemonAbility.jsx index d6d7633e36..5c7a8c02bb 100644 --- a/src/PokemonAbility.jsx +++ b/src/PokemonAbility.jsx @@ -1,12 +1,10 @@ -import React from 'react' +import React from "react"; const PokemonAbility = ({ abilityName }) => (
Hidden ability
-
- {abilityName} -
+
{abilityName}
-) +); -export default PokemonAbility +export default PokemonAbility; diff --git a/src/PokemonList.jsx b/src/PokemonList.jsx index ceea840c09..22704d2842 100644 --- a/src/PokemonList.jsx +++ b/src/PokemonList.jsx @@ -1,20 +1,23 @@ -import React from 'react' -import { Link } from 'react-router-dom' +import React from "react"; +import { Link } from "react-router-dom"; const PokemonList = ({ pokemonList }) => { return (
{pokemonList.map(({ id, name }) => ( - -
- {name} -
+ +
{name}
))}
- ) -} + ); +}; -export default PokemonList +export default PokemonList; diff --git a/src/PokemonPage.jsx b/src/PokemonPage.jsx index a37849fb4d..77479b0f7f 100644 --- a/src/PokemonPage.jsx +++ b/src/PokemonPage.jsx @@ -1,32 +1,40 @@ -import React from 'react' -import { Link, useParams } from 'react-router-dom' -import LoadingSpinner from './LoadingSpinner' -import { useApi } from './useApi' -import PokemonAbility from './PokemonAbility' -import ErrorMessage from './ErrorMessage' +import React from "react"; +import { Link, useParams } from "react-router-dom"; +import LoadingSpinner from "./LoadingSpinner"; +import { useApi } from "./useApi"; +import PokemonAbility from "./PokemonAbility"; +import ErrorMessage from "./ErrorMessage"; -const formatName = (nameWithDash) => nameWithDash.replace('-', ' ') +const formatName = (nameWithDash) => nameWithDash.replace("-", " "); const PokemonPage = ({ previous, next }) => { - const { name } = useParams() - const { data: pokemon, error, isLoading } = useApi(`https://pokeapi.co/api/v2/pokemon/${name}`) + const { name } = useParams(); + const { + data: pokemon, + error, + isLoading, + } = useApi(`https://pokeapi.co/api/v2/pokemon/${name}`); if (isLoading) { - return + return ; } if (error) { - return + return ; } - const { type } = pokemon.types.find((type) => type.slot === 1) - const stats = pokemon.stats.map((stat) => ({ - name: formatName(stat.stat.name), - value: stat.base_stat - })).reverse() - const normalAbility = pokemon.abilities.find((ability) => !ability.is_hidden) - const hiddenAbility = pokemon.abilities.find((ability) => ability.is_hidden === true) + const { type } = pokemon.types.find((type) => type.slot === 1); + const stats = pokemon.stats + .map((stat) => ({ + name: formatName(stat.stat.name), + value: stat.base_stat, + })) + .reverse(); + const normalAbility = pokemon.abilities.find((ability) => !ability.is_hidden); + const hiddenAbility = pokemon.abilities.find( + (ability) => ability.is_hidden === true + ); - console.log('hiddenAbility=', hiddenAbility) + // console.log("hiddenAbility=", hiddenAbility); return ( <>
@@ -35,7 +43,10 @@ const PokemonPage = ({ previous, next }) => { {next && Next}
-
+
{pokemon.name}
@@ -51,13 +62,21 @@ const PokemonPage = ({ previous, next }) => {
- {normalAbility && } - {hiddenAbility && } + {normalAbility && ( + + )} + {hiddenAbility && ( + + )}
- ) -} + ); +}; -export default PokemonPage +export default PokemonPage; diff --git a/src/index.jsx b/src/index.jsx index 2fae7c4d5b..f369abedb1 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -1,6 +1,6 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import App from './App' -import './styles.css' +import React from "react"; +import ReactDOM from "react-dom"; +import App from "./App"; +import "./styles.css"; -ReactDOM.render(, document.getElementById('app')) +ReactDOM.render(, document.getElementById("app")); diff --git a/src/useApi.js b/src/useApi.js index e079e024ad..cdc23d0ccb 100644 --- a/src/useApi.js +++ b/src/useApi.js @@ -1,20 +1,20 @@ -import { useEffect, useState } from 'react' -import axios from 'axios' +import { useEffect, useState } from "react"; +import axios from "axios"; const useApi = (url, mapResults = (result) => result) => { - const [data, setData] = useState() - const [isLoading, setIsLoading] = useState(true) - const [error, setError] = useState() + const [data, setData] = useState(); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(); useEffect(() => { - setIsLoading(true) + setIsLoading(true); axios .get(url) - .then(response => setData(mapResults(response.data))) + .then((response) => setData(mapResults(response.data))) .catch(setError) - .finally(() => setIsLoading(false)) - }, [url]) + .finally(() => setIsLoading(false)); + }, [url]); - return { data, isLoading, error } -} + return { data, isLoading, error }; +}; -export { useApi } +export { useApi }; diff --git a/test/App.jest.spec.jsx b/test/App.jest.spec.jsx index f06c24ae7a..238d483b5e 100644 --- a/test/App.jest.spec.jsx +++ b/test/App.jest.spec.jsx @@ -1,41 +1,47 @@ -import React from 'react' -import { render, screen } from '@testing-library/react' -import axiosMock from 'axios' -import { act } from 'react-dom/test-utils' -import '@testing-library/jest-dom/extend-expect' -import App from '../src/App' +import React from "react"; +import { render, screen } from "@testing-library/react"; +import axiosMock from "axios"; +import { act } from "react-dom/test-utils"; +import "@testing-library/jest-dom/extend-expect"; +import App from "../src/App"; -jest.mock('axios') +jest.mock("axios"); -describe('', () => { - it('fetches data', async () => { - axiosMock.get.mockResolvedValueOnce( - { - data: { - results: [{ url: 'https://pokeapi.co/api/v2/pokemon/1/', name: 'bulbasaur', id: 1 }] - } - } - ) +describe("", () => { + it("fetches data", async () => { + axiosMock.get.mockResolvedValueOnce({ + data: { + results: [ + { + url: "https://pokeapi.co/api/v2/pokemon/1/", + name: "bulbasaur", + id: 1, + }, + ], + }, + }); await act(async () => { - render() - }) - expect(axiosMock.get).toHaveBeenCalledTimes(1) - expect(axiosMock.get).toHaveBeenCalledWith('https://pokeapi.co/api/v2/pokemon/?limit=50') - }) + render(); + }); + expect(axiosMock.get).toHaveBeenCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + "https://pokeapi.co/api/v2/pokemon/?limit=50" + ); + }); - it('shows LoadingSpinner', async () => { - axiosMock.get.mockResolvedValueOnce({}) + it("shows LoadingSpinner", async () => { + axiosMock.get.mockResolvedValueOnce({}); await act(async () => { - const { getByAltText } = render() - expect(getByAltText('Loading...')).toBeVisible() - }) - }) + const { getByAltText } = render(); + expect(getByAltText("Loading...")).toBeVisible(); + }); + }); - it('shows error', async () => { - axiosMock.get.mockRejectedValueOnce(new Error()) + it("shows error", async () => { + axiosMock.get.mockRejectedValueOnce(new Error()); await act(async () => { - render() - }) - expect(screen.getByTestId('error')).toBeVisible() - }) -}) + render(); + }); + expect(screen.getByTestId("error")).toBeVisible(); + }); +}); diff --git a/test/PokemonList.jest.spec.jsx b/test/PokemonList.jest.spec.jsx index b11ad72a89..6c78fd3126 100644 --- a/test/PokemonList.jest.spec.jsx +++ b/test/PokemonList.jest.spec.jsx @@ -1,28 +1,30 @@ -import React from 'react' -import { render, screen } from '@testing-library/react' -import { BrowserRouter } from 'react-router-dom' -import '@testing-library/jest-dom/extend-expect' -import PokemonList from '../src/PokemonList' +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { BrowserRouter } from "react-router-dom"; +import "@testing-library/jest-dom/extend-expect"; +import PokemonList from "../src/PokemonList"; +const pokemonList = [ + { + url: "https://pokeapi.co/api/v2/pokemon/1/", + name: "bulbasaur", + id: 1, + }, + { + url: "https://pokeapi.co/api/v2/pokemon/133/", + name: "eevee", + id: 133, + }, +]; -const pokemonList = [{ - url: 'https://pokeapi.co/api/v2/pokemon/1/', - name: 'bulbasaur', - id: 1 -}, { - url: 'https://pokeapi.co/api/v2/pokemon/133/', - name: 'eevee', - id: 133 -}] - -describe('', () => { - it('should render items', () => { +describe("", () => { + it("should render items", () => { render( - ) - expect(screen.getByText('bulbasaur')).toBeVisible() - expect(screen.getByText('eevee')).toBeVisible() - }) -}) + ); + expect(screen.getByText("bulbasaur")).toBeVisible(); + expect(screen.getByText("eevee")).toBeVisible(); + }); +}); diff --git a/test/PokemonPage.jest.spec.jsx b/test/PokemonPage.jest.spec.jsx index 5c0ebddae1..a8538460e5 100644 --- a/test/PokemonPage.jest.spec.jsx +++ b/test/PokemonPage.jest.spec.jsx @@ -1,139 +1,145 @@ -import React from 'react' -import { render, screen } from '@testing-library/react' -import { Router } from 'react-router-dom' -import { createMemoryHistory } from 'history' -import axiosMock from 'axios' -import { act } from 'react-dom/test-utils' -import '@testing-library/jest-dom/extend-expect' -import PokemonPage from '../src/PokemonPage' +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { Router } from "react-router-dom"; +import { createMemoryHistory } from "history"; +import axiosMock from "axios"; +import { act } from "react-dom/test-utils"; +import "@testing-library/jest-dom/extend-expect"; +import PokemonPage from "../src/PokemonPage"; -jest.mock('axios') +jest.mock("axios"); const previous = { - url: 'https://pokeapi.co/api/v2/pokemon/132/', - name: 'ditto', - id: 132 -} + url: "https://pokeapi.co/api/v2/pokemon/132/", + name: "ditto", + id: 132, +}; const next = { - url: 'https://pokeapi.co/api/v2/pokemon/134/', - name: 'vaporeon', - id: 134 -} + url: "https://pokeapi.co/api/v2/pokemon/134/", + name: "vaporeon", + id: 134, +}; const pokemonList = { id: 133, abilities: [ { ability: { - name: 'anticipation', - url: 'https://pokeapi.co/api/v2/ability/107/' + name: "anticipation", + url: "https://pokeapi.co/api/v2/ability/107/", }, is_hidden: true, - slot: 3 + slot: 3, }, { ability: { - name: 'adaptability', - url: 'https://pokeapi.co/api/v2/ability/91/' + name: "adaptability", + url: "https://pokeapi.co/api/v2/ability/91/", }, is_hidden: false, - slot: 2 - } + slot: 2, + }, ], - name: 'eevee', + name: "eevee", stats: [ { base_stat: 55, effort: 0, stat: { - name: 'attack', - url: 'https://pokeapi.co/api/v2/stat/2/' - } + name: "attack", + url: "https://pokeapi.co/api/v2/stat/2/", + }, }, { base_stat: 55, effort: 0, stat: { - name: 'hp', - url: 'https://pokeapi.co/api/v2/stat/1/' - } - } + name: "hp", + url: "https://pokeapi.co/api/v2/stat/1/", + }, + }, ], types: [ { slot: 1, type: { - name: 'normal', - url: 'https://pokeapi.co/api/v2/type/1/' - } - } + name: "normal", + url: "https://pokeapi.co/api/v2/type/1/", + }, + }, ], - sprites: { front_default: 'URL' } -} + sprites: { front_default: "URL" }, +}; -const history = createMemoryHistory() +const history = createMemoryHistory(); -describe('', () => { +describe("", () => { beforeEach(() => { - history.push('/pokemon/eevee') - }) + history.push("/pokemon/eevee"); + }); - it('should render abilities', async () => { - axiosMock.get.mockResolvedValueOnce({ data: pokemonList }) + it("should render abilities", async () => { + axiosMock.get.mockResolvedValueOnce({ data: pokemonList }); await act(async () => { render( - ) - }) + ); + }); - expect(screen.getByText('adaptability')).toBeVisible() - expect(screen.getByText('anticipation')).toBeVisible() - }) + expect(screen.getByText("adaptability")).toBeVisible(); + expect(screen.getByText("anticipation")).toBeVisible(); + }); - it('should render stats', async () => { - axiosMock.get.mockResolvedValueOnce({ data: pokemonList }) + it("should render stats", async () => { + axiosMock.get.mockResolvedValueOnce({ data: pokemonList }); await act(async () => { render( - ) - }) + ); + }); - expect(screen.getByTestId('stats')).toHaveTextContent('hp55attack55') - }) + expect(screen.getByTestId("stats")).toHaveTextContent("hp55attack55"); + }); - it('should render previous and next urls if they exist', async () => { - axiosMock.get.mockResolvedValueOnce({ data: pokemonList }) + it("should render previous and next urls if they exist", async () => { + axiosMock.get.mockResolvedValueOnce({ data: pokemonList }); await act(async () => { render( - ) - }) - - expect(screen.getByText('Previous')).toHaveAttribute('href', '/pokemon/ditto') - expect(screen.getByText('Next')).toHaveAttribute('href', '/pokemon/vaporeon') - }) - - it('should not render previous and next urls if none exist', async () => { - axiosMock.get.mockResolvedValueOnce({ data: pokemonList }) + ); + }); + + expect(screen.getByText("Previous")).toHaveAttribute( + "href", + "/pokemon/ditto" + ); + expect(screen.getByText("Next")).toHaveAttribute( + "href", + "/pokemon/vaporeon" + ); + }); + + it("should not render previous and next urls if none exist", async () => { + axiosMock.get.mockResolvedValueOnce({ data: pokemonList }); await act(async () => { render( - ) - }) + ); + }); - expect(screen.queryByText('Previous')).toBeNull() - expect(screen.queryByText('Next')).toBeNull() - }) -}) + expect(screen.queryByText("Previous")).toBeNull(); + expect(screen.queryByText("Next")).toBeNull(); + }); +});