Skip to content

Commit

Permalink
Merge pull request #15 from ktvtk/module9-task2
Browse files Browse the repository at this point in the history
  • Loading branch information
keksobot authored Dec 20, 2024
2 parents 4c5e3f8 + 5019508 commit 489b3c2
Show file tree
Hide file tree
Showing 16 changed files with 558 additions and 11 deletions.
80 changes: 80 additions & 0 deletions src/components/cities-list/cities-list.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {BRUSSELS, CITIES, PARIS} from '../../const.ts';
import {configureMockStore} from '@jedmao/redux-mock-store';
import {State} from '../../types/state.ts';
import {render, screen} from '@testing-library/react';
import {Provider} from 'react-redux';
import {MemoryRouter} from 'react-router-dom';
import MemoizedCitiesList from './cities-list.tsx';
import userEvent from '@testing-library/user-event';

const mockStore = configureMockStore<State>();

describe('Component: CitiesList', () => {
const initialState = {
APP: {
city: PARIS,
},
};

const store = mockStore(initialState);

it('should render all cities correctly', () => {
render(
<Provider store={store}>
<MemoryRouter>
<MemoizedCitiesList activeCity={PARIS.name} />
</MemoryRouter>
</Provider>
);

CITIES.forEach((city) => {
expect(screen.getByText(city.name)).toBeInTheDocument();
});
});

it('should have active state for the currently selected city', () => {
render(
<Provider store={store}>
<MemoryRouter>
<MemoizedCitiesList activeCity={PARIS.name} />
</MemoryRouter>
</Provider>
);

const parisLink = screen.getByText('Paris');
expect(parisLink.closest('div')).toHaveClass('tabs__item--active');
});

it('should dispatch active city change on city click', async () => {
const user = userEvent.setup();

render(
<Provider store={store}>
<MemoryRouter>
<MemoizedCitiesList activeCity={PARIS.name} />
</MemoryRouter>
</Provider>
);

const brusselsLink = screen.getByText('Brussels');
await user.click(brusselsLink);

const actions = store.getActions();
expect(actions).toHaveLength(1);
expect(actions[0].type).toBe('APP/changeActiveCity');
expect(actions[0].payload).toBe(BRUSSELS);
});

it('should have correct number of city items', () => {
render(
<Provider store={store}>
<MemoryRouter>
<MemoizedCitiesList activeCity={PARIS.name} />
</MemoryRouter>
</Provider>
);

const cityItems = screen.getAllByRole('listitem');
expect(cityItems).toHaveLength(CITIES.length);
});
});
8 changes: 5 additions & 3 deletions src/components/header/header.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import MemoizedHeader from './header.tsx';
import userEvent from '@testing-library/user-event';
import {logout} from '../../store/api-actions.ts';
import {MemoryRouter} from 'react-router-dom';
import {internet} from 'faker';

describe('Component: Header', () => {
const middlewares = [thunk];
Expand All @@ -22,10 +23,11 @@ describe('Component: Header', () => {
},
};

const testUserEmail = internet.email()

Check failure on line 26 in src/components/header/header.test.tsx

View workflow job for this annotation

GitHub Actions / Check

Missing semicolon
const initialStateAuth = {
USER: {
authorizationStatus: AuthorizationStatus.Authorized,
userEmail: '[email protected]',
userEmail: testUserEmail,
},
OFFERS: {
favoritesCount: 2,
Expand Down Expand Up @@ -59,7 +61,7 @@ describe('Component: Header', () => {
</Provider>
);

const userEmail = screen.getByText('[email protected]');
const userEmail = screen.getByText(testUserEmail);
expect(userEmail).toBeInTheDocument();

const favoritesCount = screen.getByText('2');
Expand Down Expand Up @@ -99,7 +101,7 @@ describe('Component: Header', () => {
</Provider>
);

const favoritesLink = screen.getByText('[email protected]').closest('a');
const favoritesLink = screen.getByText(testUserEmail).closest('a');
expect(favoritesLink).toHaveAttribute('href', AppRoute.Favorites);
});
});
48 changes: 48 additions & 0 deletions src/components/map/map.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {Offer} from '../../types/offer.ts';
import {makeFakeOffer} from '../../utils/mocks.ts';
import {render, screen} from '@testing-library/react';
import {PARIS} from '../../const.ts';
import {Map} from './map.tsx';

vi.mock('../../hooks/useMap', () => ({
__esModule: true,
default: vi.fn(),
}));


describe('Component: Map', () => {
let mockOffers: Offer[];
let mockActiveOffer: Offer;

beforeEach(() => {
mockOffers = [makeFakeOffer(), makeFakeOffer()];
mockActiveOffer = mockOffers[0];
});

it('should render map correctly', () => {
render(
<Map
city={PARIS}
offers={mockOffers}
selectedOffer= {mockActiveOffer}
/>
);

const mapContainer = screen.getByTestId('map');
expect(mapContainer).toBeInTheDocument();
expect(mapContainer).toHaveClass('cities__map map')

Check failure on line 33 in src/components/map/map.test.tsx

View workflow job for this annotation

GitHub Actions / Check

Missing semicolon
});

it('should highlight active marker when activeOffer is provided', () => {
render(
<Map
city={PARIS}
offers={mockOffers}
selectedOffer= {mockActiveOffer}
/>
);
const activeMarkerIconUrl = 'img/pin-active.svg';
expect(mockActiveOffer.id).toBe(mockOffers[0].id);
expect(activeMarkerIconUrl).toBe('img/pin-active.svg');
});
});
2 changes: 1 addition & 1 deletion src/components/map/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ export function Map({ city, offers, selectedOffer }: MapProps): JSX.Element {
}
}, [map, offers, selectedOffer]);

return <div className="cities__map map" style={{ height: '500px' }} ref={mapRef}></div>;
return <div className="cities__map map" style={{ height: '500px' }} ref={mapRef} data-testid='map'></div>;
}
43 changes: 43 additions & 0 deletions src/components/offers-list/offers-list.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {render, screen} from '@testing-library/react';
import {Provider} from 'react-redux';
import {store} from '../../store';
import {MemoryRouter} from 'react-router-dom';
import {makeFakeOffer} from '../../utils/mocks.ts';
import {OffersList} from './offers-list.tsx';
import userEvent from '@testing-library/user-event';

describe('Component: OffersList', () => {
const renderWithProvider = (elem: React.ReactElement) =>
render(
<Provider store={store}>
<MemoryRouter>{elem}</MemoryRouter>
</Provider>
);

it('should render offers correctly', () => {
const mockOffers = [makeFakeOffer(), makeFakeOffer(), makeFakeOffer()];

renderWithProvider(<OffersList offers={mockOffers} onChange={() => {}} />);

mockOffers.forEach((offer) => {
expect(screen.getByText(offer.title)).toBeInTheDocument();
});
});

it('should call onOfferHover when an offer is hovered', async () => {
const mockOffers = [makeFakeOffer(), makeFakeOffer()];
const handleOfferHover = vi.fn();
const user = userEvent.setup();

renderWithProvider(<OffersList offers={mockOffers} onChange={handleOfferHover} />);

const firstOffer = screen.getByText(mockOffers[0].title);
await user.hover(firstOffer);

expect(handleOfferHover).toHaveBeenCalledWith(mockOffers[0].id);

await user.unhover(firstOffer);

expect(handleOfferHover).toHaveBeenCalledWith(null);
});
});
61 changes: 61 additions & 0 deletions src/pages/favorites-screen/favorite-screen.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {FavoriteScreen} from './favorite-screen.tsx';
import {withStore} from '../../utils/mocks-components.tsx';
import {makeFakeOffer, makeFakeStore} from '../../utils/mocks.ts';
import {AuthorizationStatus} from '../../const.ts';
import {render, screen} from '@testing-library/react';
import {HelmetProvider} from 'react-helmet-async';
import {MemoryRouter} from 'react-router-dom';
import {internet} from 'faker';

describe('Component: FavoriteScreen', () => {
const component =
<MemoryRouter>

Check failure on line 12 in src/pages/favorites-screen/favorite-screen.test.tsx

View workflow job for this annotation

GitHub Actions / Check

Missing parentheses around multilines JSX
<HelmetProvider>
<FavoriteScreen />
</HelmetProvider>
</MemoryRouter>

Check failure on line 16 in src/pages/favorites-screen/favorite-screen.test.tsx

View workflow job for this annotation

GitHub Actions / Check

Missing semicolon
it('should render "Nothing yet saved" when there are no favorite offers', () => {
const { withStoreComponent } = withStore(
component,
makeFakeStore({
USER: {
authorizationStatus: AuthorizationStatus.Authorized,
userEmail: internet.email(),
},
OFFERS: {
offers: [],
favoritesOffers: [],
favoritesCount: 0,
},
})
);

render(withStoreComponent);

expect(screen.getByText(/Nothing yet saved/i)).toBeInTheDocument();
});

it('should render favorite offers when there are favorite offers', () => {
const favoriteOffer = makeFakeOffer();

const { withStoreComponent } = withStore(
component,
makeFakeStore({
USER: {
authorizationStatus: AuthorizationStatus.Authorized,
userEmail: internet.email(),
},
OFFERS: {
offers: [],
favoritesOffers: [favoriteOffer],
favoritesCount: 0,
},
})
);

render(withStoreComponent);

expect(screen.getByText(/Saved listing/i)).toBeInTheDocument();
expect(screen.getByText(favoriteOffer.title)).toBeInTheDocument();
});
});
65 changes: 65 additions & 0 deletions src/pages/login-screen/login-screen.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {withStore} from '../../utils/mocks-components.tsx';
import {makeFakeStore} from '../../utils/mocks.ts';
import {AuthorizationStatus, LoadingStatus, PARIS} from '../../const.ts';
import {MemoryRouter} from 'react-router-dom';
import {HelmetProvider} from 'react-helmet-async';
import {LoginScreen} from './login-screen.tsx';
import {fireEvent, render, screen} from '@testing-library/react';
import {internet} from 'faker';

describe('Component: LoginScreen', () => {
const component =
<MemoryRouter>

Check failure on line 12 in src/pages/login-screen/login-screen.test.tsx

View workflow job for this annotation

GitHub Actions / Check

Missing parentheses around multilines JSX
<HelmetProvider>
<LoginScreen />
</HelmetProvider>
</MemoryRouter>

Check failure on line 16 in src/pages/login-screen/login-screen.test.tsx

View workflow job for this annotation

GitHub Actions / Check

Missing semicolon
it('should render correctly', () => {
const { withStoreComponent } = withStore(
component,
makeFakeStore({
USER: {
authorizationStatus: AuthorizationStatus.Unauthorized,
userEmail: null,
},
APP: {
city: PARIS,
loadingStatus: LoadingStatus.Succeed
},
})
);

render(withStoreComponent);

expect(screen.getByRole('heading', { name: /Sign in/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Sign in/i })).toBeInTheDocument();
expect(screen.getByPlaceholderText(/Email/i)).toBeInTheDocument();
expect(screen.getByPlaceholderText(/Password/i)).toBeInTheDocument();
});


it('should handle form submission', () => {
const { withStoreComponent, mockStore } = withStore(
component,
makeFakeStore({
USER: {
authorizationStatus: AuthorizationStatus.Unauthorized,
userEmail: null,
},
APP: {
city: PARIS,
loadingStatus: LoadingStatus.Succeed
},
})
);

render(withStoreComponent);

fireEvent.change(screen.getByPlaceholderText(/Email/i), { target: { value: internet.email() } });
fireEvent.change(screen.getByPlaceholderText(/Password/i), { target: { value: 'password123' } });
fireEvent.click(screen.getByRole('button', { name: /Sign in/i }));

const actions = mockStore.getActions();
expect(actions[0].type).toBe('auth/login/pending');
});
});
42 changes: 42 additions & 0 deletions src/pages/main-empty-screen/main-empty-screen.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {MemoryRouter} from 'react-router-dom';
import {HelmetProvider} from 'react-helmet-async';
import {MainEmptyScreen} from './main-empty-screen.tsx';
import {withStore} from '../../utils/mocks-components.tsx';
import {makeFakeStore} from '../../utils/mocks.ts';
import {AuthorizationStatus, LoadingStatus, PARIS} from '../../const.ts';
import {internet} from 'faker';
import {render, screen} from '@testing-library/react';

describe('Component: MainEmptyScreen', () => {
const component =
<MemoryRouter>

Check failure on line 12 in src/pages/main-empty-screen/main-empty-screen.test.tsx

View workflow job for this annotation

GitHub Actions / Check

Missing parentheses around multilines JSX
<HelmetProvider>
<MainEmptyScreen />
</HelmetProvider>
</MemoryRouter>

Check failure on line 16 in src/pages/main-empty-screen/main-empty-screen.test.tsx

View workflow job for this annotation

GitHub Actions / Check

Missing semicolon
it('should render "No places to stay available" when there are no offers', () => {
const { withStoreComponent } = withStore(
component,
makeFakeStore({
USER: {
authorizationStatus: AuthorizationStatus.Authorized,
userEmail: internet.email(),
},
OFFERS: {
offers: [],
favoritesOffers: [],
favoritesCount: 0,
},
APP: {
city: PARIS,
loadingStatus: LoadingStatus.Succeed
},
})
);

render(withStoreComponent);

expect(screen.getByText(/No places to stay available/i)).toBeInTheDocument();
expect(screen.getByText(/We could not find any property available at the moment in Paris/i)).toBeInTheDocument();
});
})

Check failure on line 42 in src/pages/main-empty-screen/main-empty-screen.test.tsx

View workflow job for this annotation

GitHub Actions / Check

Missing semicolon
Loading

0 comments on commit 489b3c2

Please sign in to comment.