Skip to content

Commit

Permalink
Created Checkout page
Browse files Browse the repository at this point in the history
  • Loading branch information
niyobern committed Jul 23, 2024
1 parent 904bc2c commit 162cbda
Show file tree
Hide file tree
Showing 26 changed files with 1,327 additions and 14 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"axios": "^1.7.2",
"axios-mock-adapter": "^1.22.0",
"chart.js": "^4.4.3",
"chart.js": "^4.4.3",
"date-fns": "^3.6.0",
"cloudinary": "^2.2.0",
"cloudinary-core": "^2.13.1",
"date-fns": "^3.6.0",
Expand Down
1 change: 1 addition & 0 deletions public/icons/address.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/icons/card.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/icons/ccv.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/icons/date.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/icons/location.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/icons/user.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
125 changes: 125 additions & 0 deletions public/momo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/unionpay.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions src/__test__/Checkout/cardInput.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import CardInput from '@/components/Checkout/CardInput';

describe('CardInput Component', () => {
it('renders CardInput component and types into card number input', () => {
const mockSaveCard = vi.fn();
render(<CardInput saveCard={mockSaveCard} />);
const cardNumberInput = screen.getByPlaceholderText('Card Number');
fireEvent.change(cardNumberInput, {
target: { value: '4111111111111111' },
});
expect(cardNumberInput).toHaveValue('4111111111111111');
});

it('detects Visa card type correctly', () => {
const mockSaveCard = vi.fn();
render(<CardInput saveCard={mockSaveCard} />);
const cardNumberInput = screen.getByPlaceholderText('Card Number');
fireEvent.change(cardNumberInput, {
target: { value: '4111111111111111' },
});
expect(screen.getByAltText('visa')).toBeInTheDocument();
});

test('detects Mastercard card type correctly', () => {
const mockSaveCard = vi.fn();
render(<CardInput saveCard={mockSaveCard} />);
const cardNumberInput = screen.getByPlaceholderText('Card Number');
fireEvent.change(cardNumberInput, {
target: { value: '5105105105105100' },
});
expect(screen.getByAltText('mastercard')).toBeInTheDocument();
});

test('shows error for invalid card type', () => {
const mockSaveCard = vi.fn();
render(<CardInput saveCard={mockSaveCard} />);
const cardNumberInput = screen.getByPlaceholderText('Card Number');
fireEvent.change(cardNumberInput, {
target: { value: '1234567890123456' },
});
expect(screen.getByText('Invalid Card')).toBeInTheDocument();
});
test('handles expiry date change correctly', () => {
const mockSaveCard = vi.fn();
render(<CardInput saveCard={mockSaveCard} />);
const expiryDateInput = screen.getByPlaceholderText('Expiry MM/YY');
fireEvent.change(expiryDateInput, { target: { value: '12/34' } });
expect(expiryDateInput).toHaveValue('12/34');
});

test('shows error for invalid expiry date', () => {
const mockSaveCard = vi.fn();
render(<CardInput saveCard={mockSaveCard} />);
const expiryDateInput = screen.getByPlaceholderText('Expiry MM/YY');
fireEvent.change(expiryDateInput, { target: { value: '19/11' } });
expect(screen.getByText('Invalid Expiry Date')).toBeInTheDocument();
});
});
143 changes: 143 additions & 0 deletions src/__test__/Checkout/checkout.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { configureStore } from '@reduxjs/toolkit';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import checkoutReducer, {
placeOrder,
getOrders,
makePayment,
updateDeliveryInfo,
updateCouponCode,
} from '@/features/Checkout/checkoutSlice';
import Order from '@/interfaces/order';

// Create mock for axios
const mock = new MockAdapter(axios);

// Configure mock store with the checkout reducer
const store = configureStore({
reducer: {
checkout: checkoutReducer,
},
});

describe('checkoutSlice', () => {
beforeEach(() => {
mock.reset();
});

it('should handle initial state', () => {
expect(store.getState().checkout).toEqual({
checkout: {
id: 31,
totalAmount: 160,
status: 'Pending',
couponCode: '',
deliveryInfo: {
address: '123 Main St',
city: 'Anytown',
zip: '12345',
},
country: 'US',
paymentInfo: null,
trackingNumber: 'Tr280585',
createdAt: '2024-07-22T01:48:05.301Z',
updatedAt: '2024-07-22T11:01:20.291Z',
paid: true,
orderDetails: [
{
id: 41,
quantity: 2,
price: 160,
},
],
},
loading: false,
paying: false,
error: null,
});
});

it('placeOrder updates state on fulfilled', async () => {
const order = {
deliveryInfo: {
address: '123 Main St',
city: 'Anytown',
zip: '12345',
},
couponCode: 'string',
email: 'string',
firstName: 'string',
lastName: 'string',
};

mock.onPost('/checkout').reply(200, { order });

await store.dispatch(placeOrder(order));

expect(store.getState().checkout.checkout.deliveryInfo).toEqual(
expect.objectContaining(order.deliveryInfo)
);
expect(store.getState().checkout.checkout.status).toEqual('Pending');
});

it('getOrders updates state on fulfilled', async () => {
const orders: Order[] = [
{
country: 'US',
couponCode: '',
createdAt: '2024-07-22T01:48:05.301Z',
deliveryInfo: {
address: '123 Main St',
city: 'Anytown',
zip: '12345',
},
id: 31,
orderDetails: [
{
id: 41,
price: 160,
quantity: 2,
},
],
paid: true,
paymentInfo: null,
status: 'Pending',
totalAmount: 160,
trackingNumber: 'Tr280585',
updatedAt: '2024-07-22T11:01:20.291Z',
},
];

mock.onGet('/checkout/getall-order').reply(200, orders);

await store.dispatch(getOrders());

expect(store.getState().checkout.checkout).toEqual(
expect.objectContaining(orders[0])
);
});

it('makePayment updates state on fulfilled', async () => {
mock.onPost('/buyer/payment').reply(200, { success: true });

await store.dispatch(makePayment(31));

expect(store.getState().checkout.paying).toEqual(true);
});

it('updateDeliveryInfo updates delivery info', () => {
const deliveryInfo = { address: '456 Main St' };
store.dispatch(updateDeliveryInfo(deliveryInfo));

expect(store.getState().checkout.checkout.deliveryInfo).toEqual(
expect.objectContaining(deliveryInfo)
);
});

it('updateCouponCode updates coupon code', () => {
const couponCode = 'NEWYEAR';
store.dispatch(updateCouponCode(couponCode));

expect(store.getState().checkout.checkout.couponCode).toEqual(couponCode);
});
});
10 changes: 5 additions & 5 deletions src/__test__/Orders/Orders.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ const mockOrders: Order[] = [
updatedAt: '2023-07-17T00:00:00Z',
status: 'Pending',
totalAmount: 100,
deliveryInfo:
'{"address": "123 Main St", "city": "Anytown", "country": "USA"}',
country: 'USA',
deliveryInfo: { address: '123 Main St', city: 'Anytown', zip: '12345' },
paymentInfo: null,
createdAt: '',
paid: false,
Expand All @@ -26,8 +26,8 @@ const mockOrders: Order[] = [
updatedAt: '2023-07-16T00:00:00Z',
status: 'Completed',
totalAmount: 200,
deliveryInfo:
'{"address": "456 Elm St", "city": "Othertown", "country": "USA"}',
country: 'USA',
deliveryInfo: { address: '123 Main St', city: 'Anytown', zip: '12345' },
paymentInfo: null,
createdAt: '',
paid: false,
Expand Down Expand Up @@ -168,6 +168,6 @@ describe('Orders Component', () => {
</Provider>
);
const rows = screen.getAllByRole('row');
expect(rows.length).toBe(11); //
expect(rows.length).toBe(6); //
});
});
2 changes: 2 additions & 0 deletions src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import buyerSlice from '@/app/Dashboard/buyerSlice';
import orderSlice from './Dashboard/orderSlice';
import cartReducer from '@/features/Cart/cartSlice';
import checkoutSlice from '@/features/Checkout/checkoutSlice';

import ordersSliceReducer from '@/features/Orders/ordersSlice';
import contactReducer from '@/features/contact/contactSlice';
Expand All @@ -41,6 +42,7 @@ export const store = configureStore({
cartItems: cartReducer,
allProducts: allProductSlice,
contact: contactReducer,
checkout: checkoutSlice,
},
});

Expand Down
2 changes: 1 addition & 1 deletion src/components/Cart/Cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export default function Cart() {
<h2 className="text-2xl font-bold text-gray-900">Total:</h2>
<span className="text-xl font-medium text-primary">${total}</span>
</div>
<HSButton title="CHECKOUT" />
<HSButton title="CHECKOUT" path="/checkout" />
</div>
</div>
<div className="flex flex-col gap-12">
Expand Down
Loading

0 comments on commit 162cbda

Please sign in to comment.