/ packages / frontend / src / components / ListingDetail_test.tsx
ListingDetail_test.tsx
  1  import "../happyDomSetup.ts";
  2  import { cleanup, render, screen, waitFor } from "@testing-library/react";
  3  import { expect } from "@std/expect";
  4  import { userEvent } from "@testing-library/user-event";
  5  
  6  import { random256BigInt } from "@massmarket/utils";
  7  import { Listing, Order } from "@massmarket/schema";
  8  import { allListings } from "@massmarket/schema/testFixtures";
  9  import type { CodecKey, CodecValue } from "@massmarket/utils/codec";
 10  
 11  import ListingDetail from "./ListingDetail.tsx";
 12  import { createRouterWrapper, testClient } from "../testutils/mod.tsx";
 13  import { formatUnits } from "viem";
 14  import { OrderState } from "../types.ts";
 15  
 16  Deno.test("Check that we can render the listing details screen", {
 17    sanitizeResources: false,
 18    sanitizeOps: false,
 19  }, async () => {
 20    const shopId = random256BigInt();
 21  
 22    const {
 23      stateManager: merchantStateManager,
 24      relayClient: merchantRelayClient,
 25    } = await createRouterWrapper({
 26      shopId,
 27      createShop: true,
 28      enrollMerchant: true,
 29    });
 30    merchantStateManager.addConnection(merchantRelayClient);
 31    let listing: Listing;
 32    const listingId = 23;
 33    if (!allListings.has(listingId)) {
 34      throw new Error(`Listing ${listingId} not found`);
 35    }
 36    for (const [key, entry] of allListings.entries()) {
 37      await merchantStateManager.set(["Listings", key], entry);
 38      if (key === listingId) {
 39        listing = entry as Listing;
 40      }
 41    }
 42  
 43    await merchantStateManager.set(["Inventory", listingId], 30);
 44  
 45    // Remove merchant's keycard to free up for customer
 46    localStorage.removeItem(`keycard${shopId}`);
 47  
 48    // Set up customer
 49    const { wrapper, stateManager, relayClient, testAccount } =
 50      await createRouterWrapper({
 51        shopId,
 52        path: `/?itemId=${listingId}`,
 53      });
 54    await relayClient.enrollKeycard(testClient, testAccount, true);
 55    await relayClient.connect();
 56    await relayClient.authenticate();
 57    stateManager.addConnection(relayClient);
 58  
 59    await waitFor(async () => {
 60      const storedListings = await stateManager.get(["Listings"]) as Map<
 61        number,
 62        unknown
 63      >;
 64      expect(storedListings.size).toBe(allListings.size);
 65    });
 66    const { unmount } = render(<ListingDetail />, { wrapper });
 67  
 68    await screen.findByTestId("listing-detail-page");
 69    await waitFor(() => {
 70      const price = screen.getByTestId("price");
 71      expect(price.textContent).toBe(formatUnits(listing.Price, 18));
 72      const description = screen.getByTestId("description");
 73      expect(description.textContent).toBe(listing.Metadata.Description);
 74      const title = screen.getByTestId("title");
 75      expect(title.textContent).toBe(listing.Metadata.Title);
 76    });
 77  
 78    let allOrders = await stateManager.get(["Orders"]) as Map<string, unknown>;
 79    expect(allOrders.size).toBe(0);
 80  
 81    // Test adding to cart
 82    const user = userEvent.setup();
 83  
 84    // initial quantity chosen for item
 85    const initialQty = 2;
 86    // how much the quantity is increased with on the user's second pass
 87    const qtyIncreasedBy = 7;
 88    const qtyIncreasedBy2 = 3;
 89  
 90    let successToast;
 91    let successToastText;
 92    const purchaseQty = await screen.findByTestId("purchaseQty");
 93    expect(purchaseQty).toBeTruthy();
 94    await user.clear(purchaseQty);
 95    await user.type(purchaseQty, `${initialQty}`);
 96    const addToCart = screen.getByTestId("addToCart");
 97    await user.click(addToCart);
 98    // wait for the success toast to appear
 99    successToast = await screen.findByTestId("success-toast");
100    expect(successToast).toBeTruthy();
101    // the success toast text should have its message begin with `${initialQty}`
102    // first off: the toast text should begin with `${initialQty}` since initialQty > 1)
103    successToastText = await screen.findByText(
104      `${initialQty} items added`,
105    );
106    expect(successToastText.textContent?.startsWith(`${initialQty}`))
107      .toBeTruthy();
108  
109    allOrders = await stateManager.get(["Orders"]) as Map<string, unknown>;
110    expect(allOrders.size).toBe(1);
111  
112    const orderId = Array.from(allOrders.keys())[0];
113    const orderData = await stateManager.get(["Orders", orderId]);
114    expect(orderData).toBeDefined();
115  
116    const order = Order.fromCBOR(orderData!);
117    expect(order.Items[0].ListingID).toBe(listingId);
118    expect(order.Items[0].Quantity).toBe(initialQty);
119  
120    // Update quantity
121  
122    const purchaseQty2 = await screen.findByTestId("purchaseQty");
123    expect(purchaseQty2).toBeTruthy();
124    await user.clear(purchaseQty2);
125    await user.type(purchaseQty2, `${qtyIncreasedBy}`);
126    const addToCart2 = screen.getByTestId("addToCart");
127    await user.click(addToCart2);
128    // wait for the success toast to appear
129    successToast = await screen.findByTestId("success-toast");
130    expect(successToast).toBeTruthy();
131    // now: its text should begin with `${qtyIncreasedBy}`
132    successToastText = await screen.findByText(
133      `${qtyIncreasedBy} items added`,
134    );
135    expect(successToastText.textContent?.startsWith(`${qtyIncreasedBy}`))
136      .toBeTruthy();
137  
138    const d = await stateManager.get(["Orders", orderId]);
139    expect(d).toBeDefined();
140    const items = Order.fromCBOR(d!).Items;
141    expect(items[0].ListingID).toBe(listingId);
142    expect(items[0].Quantity).toBe(initialQty + qtyIncreasedBy);
143  
144    // Commit order and try to update quantity. Tests cancelAndRecreateOrder fn
145    await stateManager.set(
146      ["Orders", orderId, "State"],
147      OrderState.Committed,
148    );
149    const purchaseQty3 = await screen.findByTestId("purchaseQty");
150    expect(purchaseQty3).toBeTruthy();
151    await user.clear(purchaseQty3);
152  
153    // Third quantity update
154    await user.type(purchaseQty3, `${qtyIncreasedBy2}`);
155    const addToCart3 = screen.getByTestId("addToCart");
156    await user.click(addToCart3);
157    // wait for the success toast to appear
158    successToast = await screen.findByTestId("success-toast");
159    expect(successToast).toBeTruthy();
160    // finally: its text should begin with `${qtyIncreasedBy2}`
161    successToastText = await screen.findByText(
162      `${qtyIncreasedBy2} items added`,
163    );
164    expect(successToastText.textContent?.startsWith(`${qtyIncreasedBy2}`))
165      .toBeTruthy();
166  
167    let updatedOrders;
168    await waitFor(async () => {
169      updatedOrders = await stateManager.get(["Orders"]) as Map<
170        CodecKey,
171        CodecValue
172      >;
173      expect(updatedOrders.size).toBe(2);
174    });
175    const orderIds = Array.from(updatedOrders!.keys()).filter((id) =>
176      id !== orderId
177    );
178    expect(orderIds.length).toBe(1);
179    const newOrderId = orderIds[0] as number;
180    await waitFor(async () => {
181      const newOrderData = await stateManager.get(["Orders", newOrderId]) as Map<
182        CodecKey,
183        CodecValue
184      >;
185      const newOrder = Order.fromCBOR(newOrderData);
186      // Since quantity was updated 3 times, it should be the addition of the 3 quantities tested.
187      const totalQuantity = initialQty + qtyIncreasedBy + qtyIncreasedBy2;
188      expect(newOrder.Items[0].Quantity).toBe(totalQuantity);
189    });
190  
191    unmount();
192  
193    cleanup();
194  });