Skip to content

9k Solution #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 9k
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,33 @@ export function Header({ cart }) {
<div className="left-section">
<Link to="/" className="header-link">
<img className="logo"
data-testid="header-logo"
src="images/logo-white.png" />
<img className="mobile-logo"
data-testid="header-mobile-logo"
src="images/mobile-logo-white.png" />
</Link>
</div>

<div className="middle-section">
<input className="search-bar" type="text" placeholder="Search" />
<input className="search-bar" type="text" placeholder="Search"
data-testid="header-search-bar" />

<button className="search-button">
<button className="search-button"
data-testid="header-search-button">
<img className="search-icon" src="images/icons/search-icon.png" />
</button>
</div>

<div className="right-section">
<Link className="orders-link header-link" to="/orders">
<Link className="orders-link header-link" to="/orders"
data-testid="header-orders-link">

<span className="orders-text">Orders</span>
</Link>

<Link className="cart-link header-link" to="/checkout">
<Link className="cart-link header-link" to="/checkout"
data-testid="header-cart-link">
<img className="cart-icon" src="images/icons/cart-icon.png" />
<div className="cart-quantity">{totalQuantity}</div>
<div className="cart-text">Cart</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { it, expect, describe, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import { Header } from './Header';

describe('Header component', () => {
let cart;

beforeEach(() => {
cart = [{
productId: 'e43638ce-6aa0-4b85-b27f-e1d07eb678c6',
quantity: 2,
deliveryOptionId: '1'
}, {
productId: '15b6fc6f-327a-4ec4-896f-486349e85a3d',
quantity: 3,
deliveryOptionId: '2'
}];
});

it('displays the header correctly', () => {
render(
<MemoryRouter>
<Header cart={cart} />
</MemoryRouter>
);

const logo = screen.getByTestId('header-logo');
expect(logo).toHaveAttribute('src', 'images/logo-white.png');

const mobileLogo = screen.getByTestId('header-mobile-logo');
expect(mobileLogo).toHaveAttribute('src', 'images/mobile-logo-white.png');

expect(screen.getByTestId('header-search-bar')).toBeInTheDocument();
expect(screen.getByTestId('header-search-button')).toBeInTheDocument();

const ordersLink = screen.getByTestId('header-orders-link');
expect(ordersLink).toHaveTextContent('Orders');
expect(ordersLink).toHaveAttribute('href', '/orders');

const cartLink = screen.getByTestId('header-cart-link');
expect(cartLink).toHaveTextContent('Cart');
expect(cartLink).toHaveTextContent('5');
expect(cartLink).toHaveAttribute('href', '/checkout');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export function CheckoutPage({ cart, loadCart }) {
<>
<title>Checkout</title>

<div className="checkout-header">
<div className="checkout-header"
data-testid="checkout-header">
<div className="header-content">
<div className="checkout-header-left-section">
<a href="/">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { it, expect, describe, vi, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router';
import axios from 'axios';
import { CheckoutPage } from './CheckoutPage';

vi.mock('axios');

describe('CheckoutPage component', () => {
let loadCart;
let cart;
let deliveryOptions;
let paymentSummary;

beforeEach(() => {
loadCart = vi.fn();

cart = [{
productId: 'e43638ce-6aa0-4b85-b27f-e1d07eb678c6',
quantity: 2,
deliveryOptionId: '1',
product: {
id: "e43638ce-6aa0-4b85-b27f-e1d07eb678c6",
image: "images/products/athletic-cotton-socks-6-pairs.jpg",
name: "Black and Gray Athletic Cotton Socks - 6 Pairs",
rating: {
stars: 4.5,
count: 87
},
priceCents: 1090,
keywords: ["socks", "sports", "apparel"]
}
}, {
productId: '15b6fc6f-327a-4ec4-896f-486349e85a3d',
quantity: 1,
deliveryOptionId: '2',
product: {
id: "15b6fc6f-327a-4ec4-896f-486349e85a3d",
image: "images/products/intermediate-composite-basketball.jpg",
name: "Intermediate Size Basketball",
rating: {
stars: 4,
count: 127
},
priceCents: 2095,
keywords: ["sports", "basketballs"]
}
}];

deliveryOptions = [{
id: '1',
deliveryDays: 7,
priceCents: 0,
estimatedDeliveryTimeMs: 1747597994451,
}, {
id: '2',
deliveryDays: 3,
priceCents: 499,
estimatedDeliveryTimeMs: 1747252394451,
}, {
id: '3',
deliveryDays: 1,
priceCents: 999,
estimatedDeliveryTimeMs: 1747079594451,
}];

paymentSummary = {
totalItems: 3,
productCostCents: 4275,
shippingCostCents: 499,
totalCostBeforeTaxCents: 4774,
taxCents: 477,
totalCostCents: 5251
};

axios.get.mockImplementation(async (url) => {
if (url === '/api/delivery-options?expand=estimatedDeliveryTime') {
return { data: deliveryOptions };
}
if (url === '/api/payment-summary') {
return { data: paymentSummary };
}
});
});

it('displays the page correctly', async () => {
render(
<MemoryRouter>
<CheckoutPage cart={cart} loadCart={loadCart} />
</MemoryRouter>
);

// findByTestId() = waits until it finds a single element.
const paymentSummary = await screen.findByTestId('payment-summary-product-cost');

expect(axios.get).toHaveBeenNthCalledWith(
1,
'/api/delivery-options?expand=estimatedDeliveryTime'
);
expect(axios.get).toHaveBeenNthCalledWith(
2,
'/api/payment-summary'
);

// Check the order summary is overall correct (since OrderSummary
// is tested in more detail in OrderSummary.test.jsx)
expect(screen.getByText('Review your order')).toBeInTheDocument();
expect(
screen.getByText('Black and Gray Athletic Cotton Socks - 6 Pairs')
).toBeInTheDocument();
expect(
screen.getByText('Intermediate Size Basketball')
).toBeInTheDocument();

// Check the payment summary is overall correct (since PaymentSummary
// is tested in more detail in PaymentSummary.test.jsx)
expect(paymentSummary).toBeInTheDocument();
expect(screen.getByText('Payment Summary')).toBeInTheDocument();
expect(screen.getByTestId('payment-summary-product-cost'))
.toHaveTextContent('Items (3):');
expect(screen.getByTestId('payment-summary-shipping-cost'))
.toHaveTextContent('$4.99');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@ export function DeliveryOptions({ cartItem, deliveryOptions, loadCart }) {

return (
<div key={deliveryOption.id} className="delivery-option"
onClick={updateDeliveryOption}>
onClick={updateDeliveryOption}
data-testid="delivery-option">
<input type="radio"
checked={deliveryOption.id === cartItem.deliveryOptionId}
onChange={() => {}}
className="delivery-option-input"
name={`delivery-option-${cartItem.productId}`} />
name={`delivery-option-${cartItem.productId}`}
data-testid="delivery-option-input"
/>
<div>
<div className="delivery-option-date">
{dayjs(deliveryOption.estimatedDeliveryTimeMs).format('dddd, MMMM D')}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { it, expect, describe, vi, beforeEach } from 'vitest';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import axios from 'axios';
import { DeliveryOptions } from './DeliveryOptions';

vi.mock('axios');

describe('DeliveryOptions component', () => {
let cartItem;
let deliveryOptions;
let loadCart;
let user;

beforeEach(() => {
cartItem = {
productId: 'e43638ce-6aa0-4b85-b27f-e1d07eb678c6',
quantity: 2,
deliveryOptionId: '2',
};

deliveryOptions = [{
id: '1',
deliveryDays: 7,
priceCents: 0,
estimatedDeliveryTimeMs: 1747597994451,
}, {
id: '2',
deliveryDays: 3,
priceCents: 499,
estimatedDeliveryTimeMs: 1747252394451,
}, {
id: '3',
deliveryDays: 1,
priceCents: 999,
estimatedDeliveryTimeMs: 1747079594451,
}];

loadCart = vi.fn();
user = userEvent.setup();
});

it('renders delivery options correctly', () => {
render(
<DeliveryOptions
cartItem={cartItem}
deliveryOptions={deliveryOptions}
loadCart={loadCart}
/>
);

expect(screen.getByText('Choose a delivery option:')).toBeInTheDocument();

const deliveryOptionElems = screen.getAllByTestId('delivery-option');
expect(deliveryOptionElems.length).toBe(3);

expect(deliveryOptionElems[0]).toHaveTextContent('Sunday, May 18');
expect(deliveryOptionElems[0]).toHaveTextContent('FREE Shipping');
expect(
within(deliveryOptionElems[0]).getByTestId('delivery-option-input').checked
).toBe(false);

expect(deliveryOptionElems[1]).toHaveTextContent('Wednesday, May 14');
expect(deliveryOptionElems[1]).toHaveTextContent('$4.99 - Shipping');
expect(
within(deliveryOptionElems[1]).getByTestId('delivery-option-input').checked
).toBe(true);

expect(deliveryOptionElems[2]).toHaveTextContent('Monday, May 12');
expect(deliveryOptionElems[2]).toHaveTextContent('$9.99 - Shipping');
expect(
within(deliveryOptionElems[2]).getByTestId('delivery-option-input').checked
).toBe(false);
});

it('updates the delivery option', async () => {
render(
<DeliveryOptions
cartItem={cartItem}
deliveryOptions={deliveryOptions}
loadCart={loadCart}
/>
);

const deliveryOptionElems = screen.getAllByTestId('delivery-option');

await user.click(deliveryOptionElems[2]);
expect(axios.put).toHaveBeenCalledWith(
'/api/cart-items/e43638ce-6aa0-4b85-b27f-e1d07eb678c6',
{ deliveryOptionId: '3' }
);
expect(loadCart).toHaveBeenCalledTimes(1);

await user.click(deliveryOptionElems[0]);
expect(axios.put).toHaveBeenCalledWith(
'/api/cart-items/e43638ce-6aa0-4b85-b27f-e1d07eb678c6',
{ deliveryOptionId: '1' }
);
expect(loadCart).toHaveBeenCalledTimes(2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,35 @@ export function OrderSummary({ cart, deliveryOptions, loadCart }) {
};

return (
<div key={cartItem.productId} className="cart-item-container">
<div key={cartItem.productId} className="cart-item-container"
data-testid="cart-item-container">
<div className="delivery-date">
Delivery date: {dayjs(selectedDeliveryOption.estimatedDeliveryTimeMs).format('dddd, MMMM D')}
</div>

<div className="cart-item-details-grid">
<img className="product-image"
src={cartItem.product.image} />
src={cartItem.product.image}
data-testid="cart-item-image" />

<div className="cart-item-details">
<div className="product-name">
<div className="product-name"
data-testid="cart-item-name">
{cartItem.product.name}
</div>
<div className="product-price">
<div className="product-price"
data-testid="cart-item-price">
{formatMoney(cartItem.product.priceCents)}
</div>
<div className="product-quantity">
<span>
<span data-testid="cart-item-quantity">
Quantity: <span className="quantity-label">{cartItem.quantity}</span>
</span>
<span className="update-quantity-link link-primary">
Update
</span>
<span className="delete-quantity-link link-primary"
data-testid="cart-item-delete-quantity-link"
onClick={deleteCartItem}>
Delete
</span>
Expand Down
Loading