Compare commits

...

5 Commits

11 changed files with 94 additions and 231 deletions

View File

@ -0,0 +1,38 @@
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 { listenerMiddleware } from 'app/listenerMiddleware';
import { TypeListResponseData } from 'features/Pokedex/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,
listenerMiddleware.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);
});
});
});

View File

@ -1,9 +1,6 @@
import { createApi } from '@reduxjs/toolkit/query/react';
import { pokeApiBaseQuery } from '../../services/pokeapi/paginationBaseQuery';
import {
RegionListResponseData,
TypeListResponseData,
} from '../Pokedex/types/api';
import { RegionListResponseData, TypeListResponseData } from 'types/api';
export const filterApi = createApi({
reducerPath: 'filterApi',

View File

@ -3,36 +3,38 @@ import {
sortPokemonCardsByIdOrName,
searchPokemonCardsByName,
} from 'features/Pokedex/Pokedex';
import { PokemonResponseData } from 'features/Pokedex/types/api';
import pokemon3_Venusaur from 'features/Pokedex/__test__/pokemon3_Venusaur.json';
import pokemon4_Charmander from 'features/Pokedex/__test__/pokemon4_charmander.json';
import { PokemonCardProps } from 'components/PokemonCard';
import pokemon3_venusaur_card from 'features/Pokedex/__test__/pokemon3_venusaur_Card.json';
import pokemon4_charmander_card from 'features/Pokedex/__test__/pokemon4_charmandar_Card.json';
import { AppStore } from 'app/store';
import { configureStore } from '@reduxjs/toolkit';
import { pokedexSlice } from 'features/Pokedex/pokedexSlice';
import { pokedexApi } from 'features/Pokedex/pokedexApi';
import { filterSlice } from 'features/Filters/filterSlice';
import { filterApi } from 'features/Filters/filterApi';
import { listenerMiddleware } from 'app/listenerMiddleware';
let store: AppStore;
describe('pokedex Component', () => {
describe('filterPokemonByType works correctly', () => {
beforeEach(() => {
store = configureStore({
reducer: {
pokedex: pokedexSlice.reducer,
[pokedexApi.reducerPath]: pokedexApi.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(
pokedexApi.middleware,
listenerMiddleware.middleware,
),
});
beforeEach(() => {
store = configureStore({
reducer: {
pokedex: pokedexSlice.reducer,
filter: filterSlice.reducer,
[filterApi.reducerPath]: filterApi.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(
filterApi.middleware,
listenerMiddleware.middleware,
),
});
});
const pokemonList: PokemonResponseData[] = [
pokemon3_Venusaur,
pokemon4_Charmander,
describe('filterPokemonByType works correctly', () => {
const pokemonList: PokemonCardProps[] = [
pokemon3_venusaur_card,
pokemon4_charmander_card,
];
it('should return all PokemonCard if the selected type is "All Types"', () => {
@ -45,69 +47,47 @@ describe('pokedex Component', () => {
const selectedType = 'fire';
const filteredList = filterPokemonCardsByType(pokemonList, selectedType);
const allPokemonAreOfTypeFire = filteredList.every(pokemon =>
pokemon.types.some(type => type.type.name === selectedType),
pokemon.types.some(type => type === selectedType),
);
expect(allPokemonAreOfTypeFire).toBe(true);
});
});
describe('sortPokemonsByIdOrName works correctly', () => {
beforeEach(() => {
store = configureStore({
reducer: {
pokedex: pokedexSlice.reducer,
[pokedexApi.reducerPath]: pokedexApi.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(
pokedexApi.middleware,
listenerMiddleware.middleware,
),
});
});
const pokemonList: PokemonResponseData[] = [
pokemon3_Venusaur,
pokemon4_Charmander,
const pokemonList: PokemonCardProps[] = [
pokemon3_venusaur_card,
pokemon4_charmander_card,
];
it('should sort by id if the selected sort is "id"', () => {
const selectedSort = 'id';
const sortedList = sortPokemonCardsByIdOrName(pokemonList, selectedSort);
expect(sortedList).toEqual([pokemon3_Venusaur, pokemon4_Charmander]);
expect(sortedList).toEqual([
pokemon3_venusaur_card,
pokemon4_charmander_card,
]);
});
it('should sort by name if the selected sort is "name"', () => {
const selectedSort = 'name';
const sortedList = sortPokemonCardsByIdOrName(pokemonList, selectedSort);
expect(sortedList).toEqual([pokemon4_Charmander, pokemon3_Venusaur]);
expect(sortedList).toEqual([
pokemon4_charmander_card,
pokemon3_venusaur_card,
]);
});
});
describe('searchPokemonByName works correctly', () => {
beforeEach(() => {
store = configureStore({
reducer: {
pokedex: pokedexSlice.reducer,
[pokedexApi.reducerPath]: pokedexApi.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(
pokedexApi.middleware,
listenerMiddleware.middleware,
),
});
});
const pokemonList: PokemonResponseData[] = [
pokemon3_Venusaur,
pokemon4_Charmander,
const pokemonList: PokemonCardProps[] = [
pokemon3_venusaur_card,
pokemon4_charmander_card,
];
it('should search by name correctly', () => {
const searchName = 'char';
const searchedList = searchPokemonCardsByName(pokemonList, searchName);
expect(searchedList).toHaveLength(1);
expect(searchedList[0]).toEqual(pokemon4_Charmander);
expect(searchedList[0]).toEqual(pokemon4_charmander_card);
});
});
});

View File

@ -1,122 +0,0 @@
import { pokedexApi } from 'features/Pokedex/pokedexApi';
import { pokedexSlice } from 'features/Pokedex/pokedexSlice';
import { configureStore } from '@reduxjs/toolkit';
import region1 from 'features/Pokedex/__test__/responses/region1.json';
import pokemon1 from 'features/Pokedex/__test__/responses/pokemon1.json';
import { RegionListResponseData, TypeListResponseData } from '../types/api';
import { AppStore } from 'app/store';
import { listenerMiddleware } from 'app/listenerMiddleware';
let store: AppStore;
describe('pokedexApi', () => {
beforeEach(() => {
store = configureStore({
reducer: {
pokedex: pokedexSlice.reducer,
[pokedexApi.reducerPath]: pokedexApi.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(
pokedexApi.middleware,
listenerMiddleware.middleware,
),
});
});
describe('JEST test against real API', () => {
test('visit https://pokeapi.co/api/v2/region/1 should return correct data', async () => {
await store.dispatch(pokedexApi.endpoints.getRegion.initiate(1));
const region1Data = pokedexApi.endpoints.getRegion.select(1)(
store.getState(),
).data;
expect(region1Data).toEqual(region1);
});
test('visit https://pokeapi.co/api/v2/region/kanto should return correct data', async () => {
await store.dispatch(pokedexApi.endpoints.getRegion.initiate('kanto'));
const region1Data = pokedexApi.endpoints.getRegion.select('kanto')(
store.getState(),
).data;
expect(region1Data).toEqual(region1);
});
test('visit https://pokeapi.co/api/v2/pokemon/1 should return correct data', async () => {
await store.dispatch(pokedexApi.endpoints.getPokemon.initiate(1));
const region1Data = pokedexApi.endpoints.getPokemon.select(1)(
store.getState(),
).data;
expect(region1Data).toEqual(pokemon1);
});
test('visit https://pokeapi.co/api/v2/pokemon/bulbasaur should return correct data', async () => {
await store.dispatch(
pokedexApi.endpoints.getPokemon.initiate('bulbasaur'),
);
const region1Data = pokedexApi.endpoints.getPokemon.select('bulbasaur')(
store.getState(),
).data;
expect(region1Data).toEqual(pokemon1);
});
});
describe('JEST test against mock API', () => {
test('visit https://pokeapi.co/api/v2/region/999999 should return correct data', async () => {
await store.dispatch(pokedexApi.endpoints.getRegion.initiate(999999));
const region1Data = pokedexApi.endpoints.getRegion.select(999999)(
store.getState(),
).data;
expect(region1Data).toEqual(region1);
});
test('visit https://pokeapi.co/api/v2/region/testregion should return correct data', async () => {
await store.dispatch(
pokedexApi.endpoints.getRegion.initiate('testregion'),
);
const region1Data = pokedexApi.endpoints.getRegion.select('testregion')(
store.getState(),
).data;
expect(region1Data).toEqual(region1);
});
test('visit https://pokeapi.co/api/v2/region should return correct data in list', async () => {
await store.dispatch(pokedexApi.endpoints.getRegionList.initiate());
const regionListData = pokedexApi.endpoints.getRegionList.select()(
store.getState(),
).data as RegionListResponseData;
expect(regionListData?.results).toHaveLength(regionListData.count);
});
test('visit https://pokeapi.co/api/v2/type should return correct data in list', async () => {
await store.dispatch(pokedexApi.endpoints.getTypeList.initiate());
const typeListData = pokedexApi.endpoints.getTypeList.select()(
store.getState(),
).data as TypeListResponseData;
expect(typeListData?.results).toHaveLength(typeListData.count + 1);
});
test('visit https://pokeapi.co/api/v2/pokemon should return correct data in list', async () => {
await store.dispatch(pokedexApi.endpoints.getPokemonList.initiate());
const pokemonListData = pokedexApi.endpoints.getPokemonList.select()(
store.getState(),
).data;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
expect(pokemonListData?.results).toHaveLength(pokemonListData.count);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
expect(pokemonListData?.next).toBeUndefined();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
expect(pokemonListData?.previous).toBeUndefined();
});
});
});

View File

@ -0,0 +1,6 @@
{
"id": 3,
"name": "venusaur",
"image": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/3.svg",
"types": ["grass", "poison"]
}

View File

@ -0,0 +1,6 @@
{
"id": 4,
"name": "charmander",
"image": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/4.svg",
"types": ["fire"]
}

View File

@ -4,7 +4,7 @@ import type { Slice, PayloadAction } from '@reduxjs/toolkit';
import { PokedexState } from 'features/Pokedex/types/slice';
import { getStartAndEndIdsForRegion } from './utils';
import { PokemonResponseData } from './types/api';
import { PokemonResponseData } from 'types/api';
import { RootState } from 'app/store';
export const fetchPokemonsInTheRegion = createAsyncThunk<

View File

@ -1,4 +1,3 @@
import { PokemonResponseData } from './api';
import { PokemonCardProps } from 'components/PokemonCard';
export type PokedexState = {

View File

@ -2,7 +2,7 @@ import { rest } from 'msw';
import region1 from 'features/Pokedex/__test__/responses/region1.json';
import regionList from 'features/Pokedex/__test__/responses/regionList.json';
import typeList from 'features/Pokedex/__test__/responses/typeList.json';
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';

View File

@ -1,3 +1,4 @@
// API Base types
export interface nameUrlPair {
name: string;
url: string;
@ -8,34 +9,11 @@ export interface ListResponse {
results: nameUrlPair[];
}
export type PokemonListResponseData = ListResponse;
// Filter API
export type RegionListResponseData = ListResponse;
export type TypeListResponseData = ListResponse;
export interface PokemonListItem {
name: string;
id: number;
type: string[];
image: string;
}
export interface PokemonList {
PokemonList: PokemonListItem[];
}
export interface RegionResponseData {
// many fields are ignored
id: number;
locations: nameUrlPair[];
name: string;
}
export interface TypeResponseData {
// many fields are ignored
id: number;
name: string;
}
// InfoDialog API
export interface AbilityItem {
ability: nameUrlPair;
is_hidden: boolean;
@ -82,22 +60,3 @@ export interface PokemonResponseData {
};
};
}
export interface LocationResponseData {
// many fields are ignored
areas: nameUrlPair[];
id: number;
name: string;
region: nameUrlPair;
}
export interface PokemonEncounter {
// many fields are ignored
pokemon: nameUrlPair;
}
export interface AreaResponseData {
// many fields are ignored
id: number;
name: string;
pokemon_encounters: PokemonEncounter[];
}