Compare commits
3 Commits
804c145b11
...
f8fb41e837
Author | SHA1 | Date | |
---|---|---|---|
f8fb41e837 | |||
08c8af5f4f | |||
a0adfb0268 |
81
src/app/services/__test__/responses/evolution-chain_2.json
Normal file
81
src/app/services/__test__/responses/evolution-chain_2.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
"baby_trigger_item": null,
|
||||
"chain": {
|
||||
"evolution_details": [],
|
||||
"evolves_to": [
|
||||
{
|
||||
"evolution_details": [
|
||||
{
|
||||
"gender": null,
|
||||
"held_item": null,
|
||||
"item": null,
|
||||
"known_move": null,
|
||||
"known_move_type": null,
|
||||
"location": null,
|
||||
"min_affection": null,
|
||||
"min_beauty": null,
|
||||
"min_happiness": null,
|
||||
"min_level": 16,
|
||||
"needs_overworld_rain": false,
|
||||
"party_species": null,
|
||||
"party_type": null,
|
||||
"relative_physical_stats": null,
|
||||
"time_of_day": "",
|
||||
"trade_species": null,
|
||||
"trigger": {
|
||||
"name": "level-up",
|
||||
"url": "https://pokeapi.co/api/v2/evolution-trigger/1/"
|
||||
},
|
||||
"turn_upside_down": false
|
||||
}
|
||||
],
|
||||
"evolves_to": [
|
||||
{
|
||||
"evolution_details": [
|
||||
{
|
||||
"gender": null,
|
||||
"held_item": null,
|
||||
"item": null,
|
||||
"known_move": null,
|
||||
"known_move_type": null,
|
||||
"location": null,
|
||||
"min_affection": null,
|
||||
"min_beauty": null,
|
||||
"min_happiness": null,
|
||||
"min_level": 36,
|
||||
"needs_overworld_rain": false,
|
||||
"party_species": null,
|
||||
"party_type": null,
|
||||
"relative_physical_stats": null,
|
||||
"time_of_day": "",
|
||||
"trade_species": null,
|
||||
"trigger": {
|
||||
"name": "level-up",
|
||||
"url": "https://pokeapi.co/api/v2/evolution-trigger/1/"
|
||||
},
|
||||
"turn_upside_down": false
|
||||
}
|
||||
],
|
||||
"evolves_to": [],
|
||||
"is_baby": false,
|
||||
"species": {
|
||||
"name": "charizard",
|
||||
"url": "https://pokeapi.co/api/v2/pokemon-species/6/"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_baby": false,
|
||||
"species": {
|
||||
"name": "charmeleon",
|
||||
"url": "https://pokeapi.co/api/v2/pokemon-species/5/"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_baby": false,
|
||||
"species": {
|
||||
"name": "charmander",
|
||||
"url": "https://pokeapi.co/api/v2/pokemon-species/4/"
|
||||
}
|
||||
},
|
||||
"id": 2
|
||||
}
|
1337
src/app/services/__test__/responses/pokemon-species_6.json
Normal file
1337
src/app/services/__test__/responses/pokemon-species_6.json
Normal file
File diff suppressed because it is too large
Load Diff
9141
src/app/services/__test__/responses/pokemon_85.json
Normal file
9141
src/app/services/__test__/responses/pokemon_85.json
Normal file
File diff suppressed because it is too large
Load Diff
90
src/app/services/pokeApi.test.ts
Normal file
90
src/app/services/pokeApi.test.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { pokedexSlice } from 'features/Pokedex/pokedexSlice';
|
||||
import { pokeApi } from 'app/services/pokeApi';
|
||||
import { filterSlice } from '../../features/Filters/filterSlice';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
|
||||
import { AppStore } from 'app/store';
|
||||
import {
|
||||
EvolutionChainResponseData,
|
||||
PokemonResponseData,
|
||||
PokemonSpeciesResponseData,
|
||||
TypeListResponseData,
|
||||
} from 'types/api';
|
||||
|
||||
let store: AppStore;
|
||||
|
||||
describe('pokeApi', () => {
|
||||
beforeEach(() => {
|
||||
store = configureStore({
|
||||
reducer: {
|
||||
pokedex: pokedexSlice.reducer,
|
||||
filter: filterSlice.reducer,
|
||||
[pokeApi.reducerPath]: pokeApi.reducer,
|
||||
},
|
||||
middleware: getDefaultMiddleware =>
|
||||
getDefaultMiddleware().concat(pokeApi.middleware),
|
||||
});
|
||||
});
|
||||
|
||||
describe('JEST test against mock API', () => {
|
||||
describe('test getPokemon query', () => {
|
||||
test('visit https://pokeapi.co/api/v2/pokemon/85 returns Dodrio', async () => {
|
||||
await store.dispatch(pokeApi.endpoints.getPokemon.initiate(85));
|
||||
|
||||
const pokemon = pokeApi.endpoints.getPokemon.select(85)(
|
||||
store.getState(),
|
||||
).data as PokemonResponseData;
|
||||
expect(pokemon?.name).toBe('dodrio');
|
||||
expect(pokemon?.types).toHaveLength(2);
|
||||
expect(pokemon?.types[0].type.name).toBe('normal');
|
||||
expect(pokemon?.types[1].type.name).toBe('flying');
|
||||
expect(pokemon?.sprites.other.dream_world.front_default).toBe(
|
||||
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/85.svg',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test getPokemonSpecies query', () => {
|
||||
test('visit https://pokeapi.co/api/v2/pokemon-species/6/', async () => {
|
||||
await store.dispatch(pokeApi.endpoints.getPokemonSpecies.initiate(6));
|
||||
|
||||
const pokemonSpecies = pokeApi.endpoints.getPokemonSpecies.select(6)(
|
||||
store.getState(),
|
||||
).data as PokemonSpeciesResponseData;
|
||||
expect(pokemonSpecies?.evolution_chain.url).toBe(
|
||||
'https://pokeapi.co/api/v2/evolution-chain/2/',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test getTypeList query', () => {
|
||||
test('visit https://pokeapi.co/api/v2/type should return correct data in list', async () => {
|
||||
await store.dispatch(pokeApi.endpoints.getTypeList.initiate());
|
||||
|
||||
const typeListData = pokeApi.endpoints.getTypeList.select()(
|
||||
store.getState(),
|
||||
).data as TypeListResponseData;
|
||||
expect(typeListData?.results).toHaveLength(typeListData.count + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test getEvolutionChain query', () => {
|
||||
test('visit https://pokeapi.co/api/v2/evolution-chain/2/', async () => {
|
||||
await store.dispatch(pokeApi.endpoints.getEvolutionChain.initiate(2));
|
||||
|
||||
const evolutionChainData = pokeApi.endpoints.getEvolutionChain.select(
|
||||
2,
|
||||
)(store.getState()).data as EvolutionChainResponseData;
|
||||
expect(evolutionChainData?.chain.species.url).toBe(
|
||||
'https://pokeapi.co/api/v2/pokemon-species/4/',
|
||||
);
|
||||
expect(evolutionChainData?.chain.evolves_to[0].species.url).toBe(
|
||||
'https://pokeapi.co/api/v2/pokemon-species/5/',
|
||||
);
|
||||
expect(
|
||||
evolutionChainData?.chain.evolves_to[0].evolves_to[0].species.url,
|
||||
).toBe('https://pokeapi.co/api/v2/pokemon-species/6/');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,4 +1,15 @@
|
||||
import { fetchBaseQuery, FetchArgs } from '@reduxjs/toolkit/query/react';
|
||||
import {
|
||||
fetchBaseQuery,
|
||||
FetchArgs,
|
||||
createApi,
|
||||
} from '@reduxjs/toolkit/query/react';
|
||||
import {
|
||||
RegionListResponseData,
|
||||
TypeListResponseData,
|
||||
PokemonResponseData,
|
||||
EvolutionChainResponseData,
|
||||
PokemonSpeciesResponseData,
|
||||
} from 'types/api';
|
||||
|
||||
export interface pokeApiFullListFetchArgs extends FetchArgs {
|
||||
fetchAllPages?: boolean;
|
||||
@ -69,3 +80,38 @@ export const pokeApiBaseQuery = async (
|
||||
return fetchBaseQuery({ baseUrl })(args, api, extra);
|
||||
}
|
||||
};
|
||||
|
||||
export const pokeApi = createApi({
|
||||
reducerPath: 'pokeApi',
|
||||
baseQuery: pokeApiBaseQuery,
|
||||
endpoints: builder => ({
|
||||
getTypeList: builder.query<TypeListResponseData, void>({
|
||||
query: () => ({ url: 'type', fetchAllPages: true }),
|
||||
transformResponse: (response: RegionListResponseData) => {
|
||||
return {
|
||||
...response,
|
||||
results: [{ name: 'All Types', url: '' }, ...response.results],
|
||||
};
|
||||
},
|
||||
}),
|
||||
getPokemon: builder.query<PokemonResponseData, number | string>({
|
||||
query: IdOrName => ({ url: `pokemon/${IdOrName}` }),
|
||||
}),
|
||||
getPokemonSpecies: builder.query<
|
||||
PokemonSpeciesResponseData,
|
||||
number | string
|
||||
>({
|
||||
query: IdOrName => ({ url: `pokemon-species/${IdOrName}` }),
|
||||
}),
|
||||
getEvolutionChain: builder.query<EvolutionChainResponseData, number>({
|
||||
query: Id => ({ url: `evolution-chain/${Id}` }),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetTypeListQuery,
|
||||
useGetPokemonQuery,
|
||||
useGetPokemonSpeciesQuery,
|
||||
useGetEvolutionChainQuery,
|
||||
} = pokeApi;
|
@ -1,7 +1,7 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { pokedexSlice } from 'features/Pokedex/pokedexSlice';
|
||||
import { filterSlice } from 'features/Filters/filterSlice';
|
||||
import { filterApi } from 'features/Filters/filterApi';
|
||||
import { pokeApi } from './services/pokeApi';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
@ -10,10 +10,10 @@ export const store = configureStore({
|
||||
filter: filterSlice.reducer,
|
||||
|
||||
// api slices
|
||||
[filterApi.reducerPath]: filterApi.reducer,
|
||||
[pokeApi.reducerPath]: pokeApi.reducer,
|
||||
},
|
||||
middleware: getDefaultMiddleware =>
|
||||
getDefaultMiddleware().concat(filterApi.middleware),
|
||||
getDefaultMiddleware().concat(pokeApi.middleware),
|
||||
devTools: true,
|
||||
});
|
||||
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
setSortOptions,
|
||||
setSearchInput,
|
||||
} from './filterSlice';
|
||||
import { useGetTypeListQuery } from './filterApi';
|
||||
import { useGetTypeListQuery } from 'app/services/pokeApi';
|
||||
import { RegionPokemonRange } from './types/slice';
|
||||
import './Filters.css';
|
||||
|
||||
|
@ -1,34 +0,0 @@
|
||||
import { pokedexSlice } from 'features/Pokedex/pokedexSlice';
|
||||
import { filterApi } from './filterApi';
|
||||
import { filterSlice } from './filterSlice';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
|
||||
import { AppStore } from 'app/store';
|
||||
import { TypeListResponseData } from 'types/api';
|
||||
|
||||
let store: AppStore;
|
||||
|
||||
describe('filterApi', () => {
|
||||
beforeEach(() => {
|
||||
store = configureStore({
|
||||
reducer: {
|
||||
pokedex: pokedexSlice.reducer,
|
||||
filter: filterSlice.reducer,
|
||||
[filterApi.reducerPath]: filterApi.reducer,
|
||||
},
|
||||
middleware: getDefaultMiddleware =>
|
||||
getDefaultMiddleware().concat(filterApi.middleware),
|
||||
});
|
||||
});
|
||||
|
||||
describe('JEST test against mock API', () => {
|
||||
test('visit https://pokeapi.co/api/v2/type should return correct data in list', async () => {
|
||||
await store.dispatch(filterApi.endpoints.getTypeList.initiate());
|
||||
|
||||
const typeListData = filterApi.endpoints.getTypeList.select()(
|
||||
store.getState(),
|
||||
).data as TypeListResponseData;
|
||||
expect(typeListData?.results).toHaveLength(typeListData.count + 1);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,21 +0,0 @@
|
||||
import { createApi } from '@reduxjs/toolkit/query/react';
|
||||
import { pokeApiBaseQuery } from '../../services/pokeapi/paginationBaseQuery';
|
||||
import { RegionListResponseData, TypeListResponseData } from 'types/api';
|
||||
|
||||
export const filterApi = createApi({
|
||||
reducerPath: 'filterApi',
|
||||
baseQuery: pokeApiBaseQuery,
|
||||
endpoints: builder => ({
|
||||
getTypeList: builder.query<TypeListResponseData, void>({
|
||||
query: () => ({ url: 'type', fetchAllPages: true }),
|
||||
transformResponse: (response: RegionListResponseData) => {
|
||||
return {
|
||||
...response,
|
||||
results: [{ name: 'All Types', url: '' }, ...response.results],
|
||||
};
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetTypeListQuery } = filterApi;
|
@ -1,9 +1,9 @@
|
||||
import { createSlice, PayloadAction, Slice } from '@reduxjs/toolkit';
|
||||
import { FilterState } from './types/slice';
|
||||
import { RegionPokemonRange } from './types/slice';
|
||||
import { filterApi } from './filterApi';
|
||||
import { pokeApi } from 'app/services/pokeApi';
|
||||
|
||||
filterApi.endpoints.getTypeList.initiate(); // initialize type list fetching
|
||||
pokeApi.endpoints.getTypeList.initiate(); // initialize type list fetching
|
||||
|
||||
const initialState: FilterState = {
|
||||
regionOptions: [],
|
||||
@ -46,7 +46,7 @@ export const filterSlice: Slice<FilterState> = createSlice({
|
||||
},
|
||||
extraReducers: builder => {
|
||||
builder.addMatcher(
|
||||
filterApi.endpoints.getTypeList.matchFulfilled,
|
||||
pokeApi.endpoints.getTypeList.matchFulfilled,
|
||||
(state, action) => {
|
||||
if (action.payload && action.payload.results.length > 0) {
|
||||
const regionListResults = action.payload.results;
|
||||
|
@ -10,7 +10,7 @@ import { AppStore } from 'app/store';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { pokedexSlice } from 'features/Pokedex/pokedexSlice';
|
||||
import { filterSlice } from 'features/Filters/filterSlice';
|
||||
import { filterApi } from 'features/Filters/filterApi';
|
||||
import { pokeApi } from 'app/services/pokeApi';
|
||||
|
||||
let store: AppStore;
|
||||
|
||||
@ -20,10 +20,10 @@ describe('pokedex Component', () => {
|
||||
reducer: {
|
||||
pokedex: pokedexSlice.reducer,
|
||||
filter: filterSlice.reducer,
|
||||
[filterApi.reducerPath]: filterApi.reducer,
|
||||
[pokeApi.reducerPath]: pokeApi.reducer,
|
||||
},
|
||||
middleware: getDefaultMiddleware =>
|
||||
getDefaultMiddleware().concat(filterApi.middleware),
|
||||
getDefaultMiddleware().concat(pokeApi.middleware),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||
import type { Slice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { PokedexState } from 'features/Pokedex/types/slice';
|
||||
import { PokedexStateProps } from 'features/Pokedex/types/slice';
|
||||
|
||||
import { getStartAndEndIdsForRegion } from './utils';
|
||||
import { PokemonResponseData } from 'types/api';
|
||||
@ -39,12 +39,12 @@ export const fetchPokemonsInTheRegion = createAsyncThunk<
|
||||
return pokemonListData;
|
||||
});
|
||||
|
||||
const initialState: PokedexState = {
|
||||
export const initialState: PokedexStateProps = {
|
||||
isLoadingPokemons: true,
|
||||
pokemonCardList: [],
|
||||
};
|
||||
|
||||
export const pokedexSlice: Slice<PokedexState> = createSlice({
|
||||
export const pokedexSlice: Slice<PokedexStateProps> = createSlice({
|
||||
name: 'pokedex',
|
||||
initialState,
|
||||
reducers: {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { PokemonCardProps } from 'components/PokemonCard';
|
||||
|
||||
export type PokedexState = {
|
||||
export type PokedexStateProps = {
|
||||
isLoadingPokemons: boolean;
|
||||
pokemonCardList: PokemonCardProps[];
|
||||
};
|
||||
|
@ -6,6 +6,10 @@ import typeList from 'features/Filters/__test__/responses/typeList.json';
|
||||
import pokemonListPg1 from 'features/Pokedex/__test__/responses/pokemonListPage1.json';
|
||||
import pokemonListPg2 from 'features/Pokedex/__test__/responses/pokemonListPage2.json';
|
||||
|
||||
import dodrio from 'app/services/__test__/responses/pokemon_85.json';
|
||||
import pokemonSpecies6 from 'app/services/__test__/responses/pokemon-species_6.json';
|
||||
import evolutionChain2 from 'app/services/__test__/responses/evolution-chain_2.json';
|
||||
|
||||
export const handlers = [
|
||||
// mock https://pokeapi.co/api/v2/region/1
|
||||
rest.get('https://pokeapi.co/api/v2/region/999999', (req, res, ctx) => {
|
||||
@ -34,4 +38,19 @@ export const handlers = [
|
||||
return res(ctx.json(pokemonListPg2));
|
||||
}
|
||||
}),
|
||||
|
||||
// mock https://pokeapi.co/api/v2/pokemon/85
|
||||
rest.get('https://pokeapi.co/api/v2/pokemon/85', (req, res, ctx) => {
|
||||
return res(ctx.json(dodrio));
|
||||
}),
|
||||
|
||||
// mock https://pokeapi.co/api/v2/pokemon-species/6
|
||||
rest.get('https://pokeapi.co/api/v2/pokemon-species/6', (req, res, ctx) => {
|
||||
return res(ctx.json(pokemonSpecies6));
|
||||
}),
|
||||
|
||||
// mock https://pokeapi.co/api/v2/evolution-chain/2
|
||||
rest.get('https://pokeapi.co/api/v2/evolution-chain/2', (req, res, ctx) => {
|
||||
return res(ctx.json(evolutionChain2));
|
||||
}),
|
||||
];
|
||||
|
@ -60,3 +60,23 @@ export interface PokemonResponseData {
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type PokemonSpeciesResponseData = {
|
||||
// many fields are ignored
|
||||
evolution_chain: {
|
||||
url: string;
|
||||
};
|
||||
};
|
||||
|
||||
type EvolutionChain = {
|
||||
evolves_to: EvolutionChain[];
|
||||
species: {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type EvolutionChainResponseData = {
|
||||
// many fields are ignored
|
||||
chain: EvolutionChain;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user