From 874b2b1d49ef10679cd56bac677c5f46a07537bf Mon Sep 17 00:00:00 2001 From: egemen-balli Date: Mon, 2 Jun 2025 18:43:45 +0300 Subject: [PATCH] bug/FRESHDEAL-138 --- .babelrc | 3 + __mocks__/fileMock.js | 1 + __mocks__/styleMock.js | 1 + __tests__/components/AddressBar.test.jsx | 77 + __tests__/components/CategoryFilter.test.jsx | 46 + .../FavoriteRestaurantList.test.jsx | 76 + .../FlashDealsFloatingBadge.test.jsx | 22 + __tests__/components/FlashDealsModal.test.jsx | 94 + __tests__/components/Footer.test.jsx | 20 + .../components/RecentRestaurants.test.jsx | 82 + __tests__/components/Recommendations.test.jsx | 97 + __tests__/components/RestaurantList.test.jsx | 39 + __tests__/components/RestaurantMap.test.jsx | 27 + __tests__/pages/Account.test.jsx | 80 + __tests__/pages/Achievements.test.jsx | 60 + __tests__/pages/AddressSelection.test.jsx | 51 + __tests__/pages/Cart.test.jsx | 100 + __tests__/pages/Checkout.test.jsx | 77 + __tests__/pages/ErrorPage.test.jsx | 30 + __tests__/pages/FavoritesPage.test.jsx | 79 + jest.config.js | 12 + jest.setup.js | 17 + package-lock.json | 14229 ++++++++++++---- package.json | 14 +- src/components/FlashDealsModal.jsx | 1 + src/components/RestaurantList.jsx | 6 +- src/components/RestaurantMap.jsx | 2 +- src/pages/AddressSelection.jsx | 2 +- src/pages/Checkout.jsx | 2 +- src/testUtils/createMockStore.js | 14 + 30 files changed, 12490 insertions(+), 2871 deletions(-) create mode 100644 .babelrc create mode 100644 __mocks__/fileMock.js create mode 100644 __mocks__/styleMock.js create mode 100644 __tests__/components/AddressBar.test.jsx create mode 100644 __tests__/components/CategoryFilter.test.jsx create mode 100644 __tests__/components/FavoriteRestaurantList.test.jsx create mode 100644 __tests__/components/FlashDealsFloatingBadge.test.jsx create mode 100644 __tests__/components/FlashDealsModal.test.jsx create mode 100644 __tests__/components/Footer.test.jsx create mode 100644 __tests__/components/RecentRestaurants.test.jsx create mode 100644 __tests__/components/Recommendations.test.jsx create mode 100644 __tests__/components/RestaurantList.test.jsx create mode 100644 __tests__/components/RestaurantMap.test.jsx create mode 100644 __tests__/pages/Account.test.jsx create mode 100644 __tests__/pages/Achievements.test.jsx create mode 100644 __tests__/pages/AddressSelection.test.jsx create mode 100644 __tests__/pages/Cart.test.jsx create mode 100644 __tests__/pages/Checkout.test.jsx create mode 100644 __tests__/pages/ErrorPage.test.jsx create mode 100644 __tests__/pages/FavoritesPage.test.jsx create mode 100644 jest.config.js create mode 100644 jest.setup.js create mode 100644 src/testUtils/createMockStore.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..2b7bafa --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-react"] +} diff --git a/__mocks__/fileMock.js b/__mocks__/fileMock.js new file mode 100644 index 0000000..84c1da6 --- /dev/null +++ b/__mocks__/fileMock.js @@ -0,0 +1 @@ +module.exports = 'test-file-stub'; \ No newline at end of file diff --git a/__mocks__/styleMock.js b/__mocks__/styleMock.js new file mode 100644 index 0000000..a099545 --- /dev/null +++ b/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {}; \ No newline at end of file diff --git a/__tests__/components/AddressBar.test.jsx b/__tests__/components/AddressBar.test.jsx new file mode 100644 index 0000000..d976ce7 --- /dev/null +++ b/__tests__/components/AddressBar.test.jsx @@ -0,0 +1,77 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { Provider } from "react-redux"; +import { MemoryRouter } from "react-router-dom"; +import configureStore from "redux-mock-store"; +import AddressBar from "../../src/components/AddressBar"; + +const mockStore = configureStore([]); + +const renderWithProviders = (ui, { initialState }) => { + const store = mockStore(initialState); + return render( + + {ui} + + ); +}; + +describe("AddressBar", () => { + test("displays the selected address if it exists", () => { + const initialState = { + address: { + selectedAddressId: 2, + addresses: [ + { id: 1, street: "1st Street", neighborhood: "Old Town", district: "East District", is_primary: false }, + { id: 2, street: "2nd Avenue", neighborhood: "Downtown", district: "Central District", is_primary: true }, + ], + }, + }; + + renderWithProviders(, { initialState }); + + expect(screen.getByText(/2nd Avenue, Downtown, Central District/i)).toBeInTheDocument(); + }); + + test("shows fallback message when no addresses exist", () => { + const initialState = { + address: { + selectedAddressId: null, + addresses: [], + }, + }; + + renderWithProviders(, { initialState }); + + expect(screen.getByText(/Add your delivery address/i)).toBeInTheDocument(); + }); + + test("displays primary address if no selected address is set", () => { + const initialState = { + address: { + selectedAddressId: null, + addresses: [ + { id: 3, street: "Main Street", neighborhood: "Greenwood", district: "Northside", is_primary: true }, + ], + }, + }; + + renderWithProviders(, { initialState }); + + expect(screen.getByText(/Main Street, Greenwood, Northside/i)).toBeInTheDocument(); + }); + + test("link navigates to /Address", () => { + const initialState = { + address: { + selectedAddressId: null, + addresses: [], + }, + }; + + renderWithProviders(, { initialState }); + + const link = screen.getByRole("link"); + expect(link).toHaveAttribute("href", "/Address"); + }); +}); diff --git a/__tests__/components/CategoryFilter.test.jsx b/__tests__/components/CategoryFilter.test.jsx new file mode 100644 index 0000000..7e41bab --- /dev/null +++ b/__tests__/components/CategoryFilter.test.jsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import CategoryFilter from '../../src/components/CategoryFilter'; + +describe('CategoryFilter', () => { + const mockOnSelect = jest.fn(); + + beforeEach(() => { + mockOnSelect.mockClear(); + }); + + test('renders all category buttons', () => { + render( + + ); + + const buttons = screen.getAllByRole('button'); + expect(buttons.length).toBeGreaterThanOrEqual(10); + + expect(screen.getByText('All Categories')).toBeInTheDocument(); + expect(screen.getByText('Baked Goods')).toBeInTheDocument(); + expect(screen.getByText('Organic Products')).toBeInTheDocument(); + }); + + test('highlights the selected category', () => { + render( + + ); + + const selectedButton = screen.getByText('Fruits & Vegetables'); + expect(selectedButton).toHaveStyle('background-color: #50703C'); + expect(selectedButton).toHaveStyle('color: #fff'); + }); + + test('calls onSelectCategory when a category is clicked', () => { + render( + + ); + + const button = screen.getByText('Meat & Seafood'); + fireEvent.click(button); + + expect(mockOnSelect).toHaveBeenCalledTimes(1); + expect(mockOnSelect).toHaveBeenCalledWith('Meat & Seafood'); + }); +}); diff --git a/__tests__/components/FavoriteRestaurantList.test.jsx b/__tests__/components/FavoriteRestaurantList.test.jsx new file mode 100644 index 0000000..ccb0b36 --- /dev/null +++ b/__tests__/components/FavoriteRestaurantList.test.jsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import configureStore from 'redux-mock-store'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; +import FavoriteRestaurantList from '../../src/components/FavoriteRestaurantList'; + +jest.mock("react-slick", () => { + return ({ children }) =>
{children}
; +}); + +const mockStore = configureStore([]); + +const renderWithProviders = (ui, { initialState }) => { + const store = mockStore(initialState); + return render( + + {ui} + + ); +}; + +describe('FavoriteRestaurantList', () => { + const restaurantSample = { + id: 1, + restaurantName: 'Testaurant', + image_url: 'https://example.com/image.jpg', + rating: 4.5, + listings: 3, + workingDays: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], + workingHoursStart: '08:00', + workingHoursEnd: '22:00' + }; + + test('renders favorite restaurants when available', () => { + const initialState = { + restaurant: { + favoriteRestaurantsIDs: [1], + restaurantsProximity: [restaurantSample], + }, + }; + + renderWithProviders(, { initialState }); + + expect(screen.getByText('Favorites')).toBeInTheDocument(); + expect(screen.getByText('Testaurant')).toBeInTheDocument(); + expect(screen.getByTestId('mock-slider')).toBeInTheDocument(); + }); + + test('returns null when no favorite restaurants', () => { + const initialState = { + restaurant: { + favoriteRestaurantsIDs: [], + restaurantsProximity: [], + }, + }; + + const { container } = renderWithProviders(, { initialState }); + expect(container.firstChild).toBeNull(); + }); + + test('renders action buttons', () => { + const initialState = { + restaurant: { + favoriteRestaurantsIDs: [1], + restaurantsProximity: [restaurantSample], + }, + }; + + renderWithProviders(, { initialState }); + + expect(screen.getByText('State')).toBeInTheDocument(); + expect(screen.getByText('API')).toBeInTheDocument(); + expect(screen.getByRole('link', { name: '' })).toHaveAttribute('href', '/favorites'); + }); +}); diff --git a/__tests__/components/FlashDealsFloatingBadge.test.jsx b/__tests__/components/FlashDealsFloatingBadge.test.jsx new file mode 100644 index 0000000..37e735b --- /dev/null +++ b/__tests__/components/FlashDealsFloatingBadge.test.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import FlashDealsFloatingBadge from '../../src/components/FlashDealsFloatingBadge'; + +describe('FlashDealsFloatingBadge', () => { + test('renders with correct text and icon', () => { + render( {}} />); + + expect(screen.getByText('Flash Deals')).toBeInTheDocument(); + expect(document.querySelector('.bi-lightning-fill')).toBeInTheDocument(); + }); + + test('calls onClick when clicked', () => { + const handleClick = jest.fn(); + render(); + + const badge = screen.getByText('Flash Deals').parentElement; + fireEvent.click(badge); + + expect(handleClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/__tests__/components/FlashDealsModal.test.jsx b/__tests__/components/FlashDealsModal.test.jsx new file mode 100644 index 0000000..0e0b242 --- /dev/null +++ b/__tests__/components/FlashDealsModal.test.jsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import FlashDealsModal from '../../src/components/FlashDealsModal'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import { MemoryRouter } from 'react-router-dom'; +import thunk from 'redux-thunk'; + +jest.mock('@src/redux/thunks/restaurantThunks.js', () => ({ + getFlashDealsThunk: () => ({ type: 'MOCK_GET_FLASH_DEALS' }), +})); + +const mockStore = configureStore({ + middlewares: [thunk], +}); + + +const renderWithProviders = (ui, { initialState }) => { + const store = mockStore(initialState); + return { + ...render( + + {ui} + + ), + store, + }; +}; + +describe('FlashDealsModal', () => { + const mockRestaurants = [ + { + id: 1, + restaurantName: 'Pizza Place', + flash_deals_available: true, + image_url: '', + rating: 4.5, + distance_km: 2.3, + }, + ]; + + const initialStateWithData = { + restaurant: { + flashDealsRestaurants: mockRestaurants, + flashDealsLoading: false, + }, + }; + + const initialStateLoading = { + restaurant: { + flashDealsRestaurants: [], + flashDealsLoading: true, + }, + }; + + test('renders modal content with available discounts and restaurants', () => { + renderWithProviders(, { + initialState: initialStateWithData, + }); + + expect(screen.getByText('Available Discounts:')).toBeInTheDocument(); + expect(screen.getByText('Pizza Place')).toBeInTheDocument(); + expect(screen.getByText('Flash Deal Available')).toBeInTheDocument(); + }); + + test('renders loading spinner when loading is true', () => { + renderWithProviders(, { + initialState: initialStateLoading, + }); + + expect(screen.getAllByText('Loading Flash Deals...')).toHaveLength(2); + expect(screen.getByRole('status')).toBeInTheDocument(); // spinner + }); + + test('calls onHide when Close button is clicked', () => { + const mockOnHide = jest.fn(); + + renderWithProviders(, { + initialState: initialStateWithData, + }); + + fireEvent.click(screen.getByText('Close')); + expect(mockOnHide).toHaveBeenCalledTimes(1); + }); + + test('dispatches getFlashDealsThunk when modal is shown', () => { + const { store } = renderWithProviders( {}} />, { + initialState: initialStateWithData, + }); + + const actions = store.getActions(); + expect(actions).toEqual(expect.arrayContaining([{ type: 'MOCK_GET_FLASH_DEALS' }])); + }); +}); diff --git a/__tests__/components/Footer.test.jsx b/__tests__/components/Footer.test.jsx new file mode 100644 index 0000000..8494ce5 --- /dev/null +++ b/__tests__/components/Footer.test.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import Footer from '../../src/components/Footer'; +import { BrowserRouter } from 'react-router-dom'; + +const renderWithRouter = (ui) => render({ui}); + +describe('Footer component', () => { + test('renders FreshDeal logo', () => { + renderWithRouter(