Skip to content

Commit

Permalink
simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
electricmonk committed Sep 6, 2024
1 parent 25805e8 commit 38ed25f
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 44 deletions.
7 changes: 3 additions & 4 deletions packages/client/src/adapters/harness.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ export async function makeMonolithicApp({
};
}

export async function makeMicroservicesApp({
export async function runBackendAndRender({
products = [],
}: AppContext) {

const { catalogApp, ordersApp, cartApp, orderRepo, productRepo } = await runMicroservices(products)
const { catalogApp, ordersApp, cartApp, orderRepo } = await runMicroservices(products)

const queryClient = new QueryClient();

Expand All @@ -85,9 +85,8 @@ export async function makeMicroservicesApp({
const driver = createDriver(app);

return {
productRepo,
orderRepo,
driver,
app: driver,
[Symbol.dispose]: async () => {
await cartServer.close();
await catalogServer.close();
Expand Down
18 changes: 13 additions & 5 deletions packages/client/src/components/Shop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import React, {useState} from "react";
import {useProducts} from "../hooks/products";
import {useCartWidget} from "../hooks/cart";

interface ShopProps {
type ShopProps = {
cartId: string;
}

export const Shop: React.FC<ShopProps> = ({ cartId }) => {

const [ freeTextSearch, setFreeTextSearch ] = useState('');
const { products, productsLoading, productsError } = useProducts({freeTextSearch});
const products = useProducts(freeTextSearch);
const { viewCart, addItem, itemCount, fetched } = useCartWidget(cartId);

return <section>
Expand All @@ -20,11 +20,19 @@ export const Shop: React.FC<ShopProps> = ({ cartId }) => {
<input type="text" aria-label="free-text-search" placeholder="Search products" value={freeTextSearch} onChange={(e) => setFreeTextSearch(e.target.value)}/>
<button aria-label="Search">Search</button>
</section>
<Products addItem={addItem} products={products} isLoading={productsLoading} error={productsError} />
<Products addItem={addItem} products={products}/>
</section>
};

const Products: React.FC<{ products: Product[] | undefined, isLoading: boolean, error: unknown | null, addItem: (id: string) => void }> = ({ products, isLoading, error, addItem }) => {
type ProductsProps = {
products: {
data: Product[] | undefined;
isLoading: boolean;
error: unknown | null;
}
addItem: (id: string) => void;
}
const Products: React.FC<ProductsProps> = ( {products: {data, isLoading, error}, addItem}) => {

if (isLoading) {
return <section>Loading...</section>
Expand All @@ -34,7 +42,7 @@ const Products: React.FC<{ products: Product[] | undefined, isLoading: boolean,
return <section><>Error: {error}</></section>
}

return <>{products!.map(({ title, id }) =>
return <>{data!.map(({ title, id }) =>
<div key={id} aria-label={title}>
<h3>{title}</h3>
<button onClick={() => addItem(id)} aria-label="Add to cart" role="button">
Expand Down
1 change: 1 addition & 0 deletions packages/client/src/hooks/cart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const useCartSummary = (id: string) => {

return {isLoading, error, summary, checkout};
}

export const useCartWidget = (cartId: string) => {
const {cart} = useContext(IOContext);

Expand Down
15 changes: 3 additions & 12 deletions packages/client/src/hooks/products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,12 @@ import {IOContext} from "../adapters/context";
import {useQuery} from "react-query";
import {Product} from "@ts-react-tdd/server/src/types";

type ProductQuery = {
freeTextSearch: string;
}
export const useProducts = ({freeTextSearch}: ProductQuery) => {
export const useProducts = (query: string) => {
const {productCatalog} = useContext(IOContext);
const url = query?.length > 0 ? `/products/search?query=${query}` : `/products`;

const {data, isLoading, error} = useQuery(["products", freeTextSearch], async () => {
const url = freeTextSearch?.length > 0 ? `/products/search?query=${freeTextSearch}` : `/products`;
return useQuery(["products", query], async () => {
const res = await productCatalog.get<unknown[]>(url);
return res.data.map(p => Product.parse(p));
});

return {
products: data,
productsLoading: isLoading,
productsError: error,
}
}
30 changes: 15 additions & 15 deletions packages/client/test/purchase.flow.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import {aProduct} from "@ts-react-tdd/server/src/builders";
import {makeMicroservicesApp} from "../src/adapters/harness";
import {test, expect} from 'vitest'
import {runBackendAndRender} from "../src/adapters/harness";
import {expect, test} from 'vitest'

test("a user can purchase a product, see the confirmation page and see their order summary, after which the cart is reset", async () => {

const moogOne = aProduct({title: "Moog One"});
using harness = await makeMicroservicesApp({
using harness = await runBackendAndRender({
products: [moogOne],
});
const {driver, orderRepo} = harness;
const {app, orderRepo} = harness;

await driver.findByRole('paragraph', { name: /0 items in cart/i });
await app.findByRole('paragraph', { name: /0 items in cart/i });

await driver.addProductToCart(moogOne.title);
await driver.findByRole('paragraph', { name: /1 items in cart/i });
await app.addProductToCart(moogOne.title);
await app.findByRole('paragraph', { name: /1 items in cart/i });

await driver.viewCart();
expect(await driver.findByRole('listitem', { name: moogOne.title })).toBeTruthy();
await app.viewCart();
expect(await app.findByRole('listitem', { name: moogOne.title })).toBeTruthy();

await driver.checkout();
expect(await driver.findByRole('heading', { name: /thank you/i })).toBeTruthy();
expect(await driver.findByRole('listitem', {name: moogOne.title})).toBeTruthy();
await app.checkout();
expect(await app.findByRole('heading', { name: /thank you/i })).toBeTruthy();
expect(await app.findByRole('listitem', {name: moogOne.title})).toBeTruthy();

expect(orderRepo.orders).toContainEqual(expect.objectContaining({
items: expect.arrayContaining([
Expand All @@ -30,6 +30,6 @@ test("a user can purchase a product, see the confirmation page and see their ord
])
}));

await driver.home();
await driver.findByRole('paragraph', { name: /0 items in cart/i });
})
await app.home();
await app.findByRole('paragraph', { name: /0 items in cart/i });
})
16 changes: 8 additions & 8 deletions packages/client/test/search.flow.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {aProduct} from "@ts-react-tdd/server/src/builders";
import {makeMicroservicesApp} from "../src/adapters/harness";
import {runBackendAndRender} from "../src/adapters/harness";
import userEvent from "@testing-library/user-event";
import {test, expect} from 'vitest'

Expand All @@ -8,16 +8,16 @@ test("Product search is case-insensitive", async () => {
const moogOne = aProduct({title: "Moog One"});
const minimoog = aProduct({title: "Minimoog"});
const ob8x = aProduct({title: "OB 8x"});
using harness = await makeMicroservicesApp({
using harness = await runBackendAndRender({
products: [moogOne, minimoog, ob8x],
});
const {driver} = harness;
const {app} = harness;

await userEvent.type(driver.getByRole('textbox', {name: 'free-text-search'}), 'moog');
await userEvent.click(driver.getByRole('button', { name: /search/i }));
await userEvent.type(app.getByRole('textbox', {name: 'free-text-search'}), 'moog');
await userEvent.click(app.getByRole('button', { name: /search/i }));

expect(driver.queryByRole('heading', { name: moogOne.title })).toBeInTheDocument();
expect(driver.queryByRole('heading', { name: minimoog.title })).toBeInTheDocument();
expect(driver.queryByRole('heading', { name: ob8x.title })).not.toBeInTheDocument();
expect(app.queryByRole('heading', { name: moogOne.title })).toBeInTheDocument();
expect(app.queryByRole('heading', { name: minimoog.title })).toBeInTheDocument();
expect(app.queryByRole('heading', { name: ob8x.title })).not.toBeInTheDocument();

})

0 comments on commit 38ed25f

Please sign in to comment.