-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c92a8b2
commit 4a1eeea
Showing
29 changed files
with
674 additions
and
3 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, 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
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,159 @@ | ||
import { createMemoryHistory, MemoryHistory } from 'history'; | ||
import { render, screen} from '@testing-library/react'; | ||
import { withHistory } from '../../shared/providers'; | ||
import { withStore } from '../../shared/providers/with-store'; | ||
import { makeFakeStore } from '../../shared/mocks'; | ||
import { App } from '../../App'; | ||
import { AppRoute, SortName } from '../../types/types'; | ||
import LoginPage from '../Login/LoginPage'; | ||
import { AuthorizationStatus } from '../../const'; | ||
import { Cities } from '../../shared/api'; | ||
import MainPage from '../MainPage/MainPage'; | ||
import Favorite from '../Favorites/Favorite'; | ||
import NotFoundPage from '../NotFoundPage/NotFoundPage'; | ||
import Offer from '../Offer/Offer'; | ||
|
||
|
||
describe('Application Routing', () => { | ||
let mockHistory: MemoryHistory; | ||
|
||
beforeEach(() => { | ||
mockHistory = createMemoryHistory(); | ||
}); | ||
|
||
it('should render MainPage when user navigate to "/"', () => { | ||
// eslint-disable-next-line react/jsx-no-undef | ||
const withHistoryComponent = withHistory(<App />, mockHistory); | ||
const { withStoreComponent } = withStore(withHistoryComponent, makeFakeStore()); | ||
mockHistory.push(AppRoute.Main); | ||
|
||
render(withStoreComponent); | ||
|
||
expect(screen.getByText(/Cities/i)).toBeInTheDocument(); | ||
expect(screen.getAllByTestId('location_item')).toHaveLength(Object.values(Cities).length); | ||
}); | ||
|
||
it('should render LoginPage when user navigate to "/login"', () => { | ||
const withHistoryComponent = withHistory(<LoginPage />, mockHistory); | ||
const { withStoreComponent } = withStore(withHistoryComponent, makeFakeStore({ user: { | ||
authorizationStatus:AuthorizationStatus.NoAuth, | ||
user: null | ||
},})); | ||
mockHistory.push(AppRoute.Login); | ||
|
||
render(withStoreComponent); | ||
|
||
expect(screen.getAllByText(/Sign in/i)).toHaveLength(2); | ||
expect(screen.getByTestId('location_item-link')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render MainPage when authenticated user navigate to "/login"', () => { | ||
const withHistoryComponent = withHistory(<MainPage currentCity={{ | ||
title: '', | ||
lat: 0, | ||
lng: 0 | ||
}} cities={[]} offers={[]} | ||
// eslint-disable-next-line react/jsx-closing-bracket-location | ||
/>, mockHistory); | ||
const { withStoreComponent } = withStore(withHistoryComponent, makeFakeStore()); | ||
mockHistory.push(AppRoute.Login); | ||
|
||
render(withStoreComponent); | ||
|
||
expect(screen.getByText(/Cities/i)).toBeInTheDocument(); | ||
expect(screen.getAllByTestId('location_item')).toHaveLength(Object.values(Cities).length); | ||
}); | ||
|
||
it('should render FavoritesPage when user navigate to "/favorites"', () => { | ||
const withHistoryComponent = withHistory(<Favorite offers={null} />, mockHistory); | ||
const { withStoreComponent } = withStore(withHistoryComponent, makeFakeStore()); | ||
mockHistory.push(AppRoute.Favorites); | ||
|
||
render(withStoreComponent); | ||
|
||
expect(screen.getByText(/Saved listing/i)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render FavoritesPage when user navigate to "/favorites" with empty list', () => { | ||
const withHistoryComponent = withHistory(<Favorite offers={null} />, mockHistory); | ||
const { withStoreComponent } = withStore(withHistoryComponent, makeFakeStore({offer:{favorites:[],city: Cities.Paris, | ||
offers: [], | ||
nearOffers: [], | ||
sort: SortName.popular, | ||
isLoading: false, | ||
offerOnPage:null}})); | ||
mockHistory.push(AppRoute.Favorites); | ||
|
||
render(withStoreComponent); | ||
|
||
expect(screen.getByText(/Save properties to narrow down search or plan your future trips./i)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render NotFoundPage when user navigate to "/notFound"', () => { | ||
const withHistoryComponent = withHistory(<NotFoundPage />, mockHistory); | ||
const { withStoreComponent } = withStore(withHistoryComponent, makeFakeStore()); | ||
mockHistory.push('NotFoundPage'); | ||
|
||
render(withStoreComponent); | ||
|
||
expect(screen.getByText(/404 Not Found/i)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render NotFoundPage when user navigate to non-existent path', () => { | ||
const withHistoryComponent = withHistory(<NotFoundPage />, mockHistory); | ||
const { withStoreComponent } = withStore(withHistoryComponent, makeFakeStore()); | ||
mockHistory.push('123321'); | ||
|
||
render(withStoreComponent); | ||
|
||
expect(screen.getByText(/404 Not Found/i)).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render OfferPage when user navigate to "/offer/a20a52b2-efc2-4b0f-9396-4bdfbe5e9543"', () => { | ||
const withHistoryComponent = withHistory(<Offer offerdetails={{ | ||
id: '', | ||
title: '', | ||
type: '', | ||
price: 0, | ||
city: { | ||
name: '', | ||
location: { | ||
latitude: 0, | ||
longitude: 0, | ||
zoom: 0 | ||
} | ||
}, | ||
location: { | ||
latitude: 0, | ||
longitude: 0, | ||
zoom: 0 | ||
}, | ||
isFavorite: false, | ||
isPremium: false, | ||
rating: 0, | ||
description: '', | ||
bedrooms: 0, | ||
goods: [''], | ||
host: { | ||
name: '', | ||
avatarUrl: '', | ||
isPro: false | ||
}, | ||
images: [''], | ||
maxAdults: 0 | ||
}} offers={null} currentCity={{ | ||
title: '', | ||
lat: 0, | ||
lng: 0 | ||
// eslint-disable-next-line react/jsx-closing-bracket-location | ||
}} />, mockHistory); | ||
const { withStoreComponent } = withStore(withHistoryComponent, makeFakeStore()); | ||
mockHistory.push('/offer/a20a52b2-efc2-4b0f-9396-4bdfbe5e9543'); | ||
|
||
render(withStoreComponent); | ||
|
||
expect(screen.getByText(/Wood and stone place/i)).toBeInTheDocument(); | ||
expect(screen.getByText(/A quiet cozy and picturesque that hides behind a a river by the unique lightness of Amsterdam./i)).toBeInTheDocument(); | ||
expect(screen.getByText(/Other places in the neighbourhood/i)).toBeInTheDocument(); | ||
}); | ||
}); |
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,5 @@ | ||
import {createBrowserHistory} from 'history'; | ||
|
||
const browserHistory = createBrowserHistory(); | ||
|
||
export default browserHistory; |
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,46 @@ | ||
import { MockStore, configureMockStore } from '@jedmao/redux-mock-store'; | ||
import { AnyAction } from '@reduxjs/toolkit'; | ||
|
||
import { redirect } from './middlewares'; | ||
import { RootState } from './shared/lib/types'; | ||
import browserHistory from './config'; | ||
import { routesEnum } from './shared/config'; | ||
import { AppRoute } from './types/types'; | ||
import { redirectToRoute } from './action'; | ||
|
||
vi.mock('../../browser-history', () => ({ | ||
default: { | ||
location: { pathname: '' }, | ||
push(path: string) { | ||
this.location.pathname = path; | ||
}, | ||
}, | ||
})); | ||
|
||
describe('Redirect middleware', () => { | ||
let store: MockStore; | ||
|
||
beforeAll(() => { | ||
const middleware = [redirect]; | ||
const mockStoreCreator = configureMockStore<RootState, AnyAction>( | ||
middleware | ||
); | ||
store = mockStoreCreator(); | ||
}); | ||
|
||
beforeEach(() => { | ||
browserHistory.push(''); | ||
}); | ||
|
||
it('should redirect to "/login" with redirectToRoute action', () => { | ||
const redirectAction = redirectToRoute(AppRoute.Login); | ||
store.dispatch(redirectAction); | ||
expect(browserHistory.location.pathname).toBe(AppRoute.Login); | ||
}); | ||
|
||
it('should not redirect to "/" with empty action', () => { | ||
const emptyAction = { type: '', payload: routesEnum.MAIN }; | ||
store.dispatch(emptyAction); | ||
expect(browserHistory.location.pathname).not.toBe(routesEnum.MAIN); | ||
}); | ||
}); |
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,14 @@ | ||
import { PayloadAction } from '@reduxjs/toolkit'; | ||
import { Middleware } from 'redux'; | ||
|
||
import browserHistory from './config'; | ||
import { ReducerType } from './types/types'; | ||
|
||
export const redirect: Middleware<unknown, ReducerType> = | ||
() => (next) => (action: PayloadAction<string>) => { | ||
if (action.type === 'user/redirectToRoute') { | ||
browserHistory.push(action.payload); | ||
} | ||
|
||
return next(action); | ||
}; |
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 @@ | ||
export { Cities, $api } from './typicode'; |
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,47 @@ | ||
import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios'; | ||
import { API_URL, REQUEST_TIMEOUT } from '../../config'; | ||
import { StatusCodes } from 'http-status-codes'; | ||
import { toast } from 'react-toastify'; | ||
import { getToken } from './token'; | ||
import { ErrorMessageType } from '../../types'; | ||
|
||
const BadStatusCodesArray: (StatusCodes | string)[] = [ | ||
StatusCodes.BAD_REQUEST, | ||
StatusCodes.UNAUTHORIZED, | ||
StatusCodes.BAD_GATEWAY, | ||
StatusCodes.INTERNAL_SERVER_ERROR, | ||
'ERR_NETWORK', | ||
'ERR_BAD_REQUEST', | ||
'ECONNABORTED', | ||
]; | ||
|
||
export const $api = axios.create({ | ||
timeout: REQUEST_TIMEOUT, | ||
baseURL: API_URL, | ||
}); | ||
|
||
$api.interceptors.request.use( | ||
(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => { | ||
const token = getToken(); | ||
|
||
if (token && config.headers) { | ||
config.headers['x-token'] = token; | ||
} | ||
|
||
return config; | ||
} | ||
); | ||
|
||
$api.interceptors.response.use( | ||
(response) => response, | ||
(error: AxiosError<ErrorMessageType>) => { | ||
if (error.code && BadStatusCodesArray.includes(error.code)) { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call | ||
toast.warn('ERRORS WITH SERVER', { | ||
position: 'top-left', | ||
autoClose: 3000, | ||
}); | ||
} | ||
throw error; | ||
} | ||
); |
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,8 @@ | ||
export enum Cities { | ||
Paris = 'Paris', | ||
Cologne ='Cologne', | ||
Brussels ='Brussels', | ||
Amsterdam ='Amsterdam', | ||
Hamburg = 'Hamburg', | ||
Dusseldorf = 'Dusseldorf' | ||
} |
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,2 @@ | ||
export {Cities} from './cities'; | ||
export {$api} from './base'; |
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,16 @@ | ||
import { Token } from '../../types'; | ||
|
||
const AUTH_TOKEN_KEY_NAME = 'guess-six-cities-token'; | ||
|
||
export const getToken = (): Token => { | ||
const token = localStorage.getItem(AUTH_TOKEN_KEY_NAME); | ||
return token ?? ''; | ||
}; | ||
|
||
export const saveToken = (token: Token | undefined): void => { | ||
localStorage.setItem(AUTH_TOKEN_KEY_NAME, token ?? ''); | ||
}; | ||
|
||
export const dropToken = (): void => { | ||
localStorage.removeItem(AUTH_TOKEN_KEY_NAME); | ||
}; |
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,10 @@ | ||
export const API_URL = 'https://14.design.htmlacademy.pro/six-cities'; | ||
export const REQUEST_TIMEOUT = 3000; | ||
|
||
export enum routesEnum { | ||
LOGIN = '/login', | ||
MAIN = '/', | ||
FAVORITES = '/favorites', | ||
OFFER = '/offer/:id', | ||
NOT_FOUND = '/notFound' | ||
} |
Oops, something went wrong.