/ packages / frontend / src / components / Navigation_test.tsx
Navigation_test.tsx
  1  import "../happyDomSetup.ts";
  2  import {
  3    cleanup,
  4    render,
  5    screen,
  6    waitFor,
  7    within,
  8  } from "@testing-library/react";
  9  import { expect } from "@std/expect";
 10  import { userEvent } from "@testing-library/user-event";
 11  
 12  import { allListings } from "@massmarket/schema/testFixtures";
 13  import type { CodecValue } from "@massmarket/utils/codec";
 14  import { random256BigInt, randUint64 } from "@massmarket/utils";
 15  import { Order, OrderedItem } from "@massmarket/schema";
 16  
 17  import Navigation from "./Navigation.tsx";
 18  import { createRouterWrapper, testClient } from "../testutils/mod.tsx";
 19  import { OrderState } from "../types.ts";
 20  Deno.test("Check that we can render the navigation bar", {
 21    sanitizeResources: false,
 22    sanitizeOps: false,
 23  }, async (t) => {
 24    const shopId = random256BigInt();
 25  
 26    const {
 27      stateManager: merchantStateManager,
 28      relayClient: merchantRelayClient,
 29    } = await createRouterWrapper({
 30      shopId,
 31      createShop: true,
 32      enrollMerchant: true,
 33    });
 34  
 35    merchantStateManager.addConnection(merchantRelayClient);
 36  
 37    // populate state manager with listings
 38    for (const [listingID, listing] of allListings.entries()) {
 39      await merchantStateManager.set(["Listings", listingID], listing);
 40      await merchantStateManager.set(["Inventory", listingID], 100);
 41    }
 42  
 43    // remove merchant's keycard to free up for customer
 44    localStorage.removeItem(`keycard${shopId}`);
 45  
 46    const { wrapper, stateManager, relayClient, testAccount } =
 47      await createRouterWrapper({
 48        shopId,
 49      });
 50    await relayClient.enrollKeycard(testClient, testAccount, true);
 51    stateManager.addConnection(relayClient);
 52  
 53    await waitFor(async () => {
 54      const storedListings = await stateManager.get(["Listings"]) as Map<
 55        bigint,
 56        unknown
 57      >;
 58      expect(storedListings.size).toBe(allListings.size);
 59    });
 60  
 61    // Create order and add items to it
 62    const orderId = randUint64();
 63    const item1ID = 23;
 64    const item2ID = 42;
 65    const order = new Order(
 66      orderId,
 67      [
 68        new OrderedItem(item1ID, 32),
 69        new OrderedItem(item2ID, 24),
 70      ],
 71      OrderState.Open,
 72    );
 73    await stateManager.set(["Orders", orderId], order);
 74  
 75    const { unmount } = render(<Navigation />, { wrapper });
 76    await screen.findByTestId("navigation");
 77  
 78    const user = userEvent.setup();
 79    await waitFor(async () => {
 80      const cartToggle = screen.getByTestId("cart-toggle");
 81      expect(cartToggle).toBeDefined();
 82      await user.click(cartToggle);
 83    });
 84  
 85    await t.step("Remove item from cart", async () => {
 86      // Check that the cart items are rendered
 87      const desktopCart = screen.getByTestId("desktop-cart");
 88  
 89      await waitFor(() => {
 90        expect(desktopCart).toBeDefined();
 91        const cartItems = within(desktopCart).getAllByTestId("cart-item");
 92        expect(cartItems.length).toBe(2);
 93        //check that the added quantity is displayed correctly
 94        expect(cartItems[0].textContent).toEqual(
 95          "test32Qty: 320.00000000000736ETH",
 96        );
 97        expect(cartItems[1].textContent).toEqual(
 98          "test4224Qty: 240.00000000001008ETH",
 99        );
100      });
101  
102      await waitFor(async () => {
103        const removeButton = within(desktopCart).getByTestId(
104          `remove-item-${item1ID}`,
105        );
106        expect(removeButton).toBeDefined();
107        await user.click(removeButton);
108      });
109  
110      await waitFor(() => {
111        const cartItems = within(desktopCart).getAllByTestId("cart-item");
112        expect(cartItems.length).toBe(1);
113        expect(cartItems[0].textContent).toContain("test4224Qty");
114      });
115    });
116  
117    await t.step("Add quantity to item", async () => {
118      const desktopCart = screen.getByTestId("desktop-cart");
119  
120      await waitFor(async () => {
121        const addButton = within(desktopCart).getByTestId(
122          `add-quantity-${item2ID}`,
123        );
124        expect(addButton).toBeDefined();
125        await user.click(addButton);
126      });
127      await waitFor(() => {
128        const quantity = within(desktopCart).getByTestId(`quantity-${item2ID}`);
129        expect(quantity.textContent).toContain("25");
130      });
131      // Check statemanager updated correctly.
132      const updatedOrder = await stateManager.get(["Orders", orderId]);
133      expect(updatedOrder).toBeDefined();
134      const updatedOrderItems = Order.fromCBOR(updatedOrder!).Items;
135      expect(updatedOrderItems[0].ListingID).toBe(item2ID);
136      expect(updatedOrderItems[0].Quantity).toBe(25);
137    });
138  
139    await t.step("Remove quantity from item", async () => {
140      const desktopCart = screen.getByTestId("desktop-cart");
141  
142      await waitFor(async () => {
143        const minusQty = within(desktopCart).getByTestId(
144          `remove-quantity-${item2ID}`,
145        );
146        expect(minusQty).toBeDefined();
147        await user.click(minusQty);
148      });
149      await waitFor(() => {
150        const quantity = within(desktopCart).getByTestId(`quantity-${item2ID}`);
151        expect(quantity.textContent).toContain("24");
152      });
153      // Check statemanager updated correctly.
154      const updatedOrder = await stateManager.get(["Orders", orderId]);
155      expect(updatedOrder).toBeDefined();
156      const updatedOrderItems = Order.fromCBOR(updatedOrder!).Items;
157      expect(updatedOrderItems[0].ListingID).toBe(item2ID);
158      expect(updatedOrderItems[0].Quantity).toBe(24);
159    });
160  
161    await t.step("Clear cart", async () => {
162      const desktopCart = screen.getByTestId("desktop-cart");
163  
164      await waitFor(async () => {
165        const clearCart = within(desktopCart).getByTestId("clear-cart");
166        await user.click(clearCart);
167      });
168      await waitFor(() => {
169        const cartItems = within(desktopCart).queryAllByTestId("cart-item");
170        expect(cartItems.length).toBe(0);
171      });
172      const updatedOrder = await stateManager.get(["Orders", orderId]);
173      expect(updatedOrder).toBeDefined();
174      const updatedOrderItems = Order.fromCBOR(updatedOrder!).Items;
175      expect(updatedOrderItems.length).toBe(0);
176    });
177  
178    await t.step("Checkout button", async () => {
179      await stateManager.set(["Orders", orderId, "Items"], [
180        new OrderedItem(item1ID, 32).asCBORMap(),
181        new OrderedItem(item2ID, 24).asCBORMap(),
182      ]);
183  
184      await waitFor(async () => {
185        const cartToggle = screen.getByTestId("cart-toggle");
186        expect(cartToggle).toBeTruthy();
187        await user.click(cartToggle);
188      });
189      const desktopCart = screen.getByTestId("desktop-cart");
190  
191      await waitFor(async () => {
192        const checkoutButton = within(desktopCart).getByTestId(
193          "checkout-button",
194        );
195        expect(checkoutButton).toBeDefined();
196        await user.click(checkoutButton);
197      });
198      //Check that the order was committed after clicking checkout button.
199      const updatedOrder = await stateManager.get(["Orders", orderId]);
200      expect(updatedOrder).toBeDefined();
201      const state = Order.fromCBOR(updatedOrder!).State;
202      expect(state).toBe(OrderState.Committed);
203    });
204  
205    await t.step("clear cart when order is committed", async () => {
206      await waitFor(async () => {
207        const cartToggle = screen.getByTestId("cart-toggle");
208        expect(cartToggle).toBeTruthy();
209        await user.click(cartToggle);
210      });
211      const desktopCart = screen.getByTestId("desktop-cart");
212  
213      await waitFor(async () => {
214        // Verify the cart header is displayed
215        const cartHeader = within(desktopCart).getByText("Cart");
216        expect(cartHeader).toBeTruthy();
217        // Try to clear cart that's already committed
218        const clearCart = within(desktopCart).getByTestId("clear-cart");
219        expect(clearCart).toBeDefined();
220        await user.click(clearCart);
221      });
222      await waitFor(async () => {
223        const cartItems = within(desktopCart).queryAllByTestId("cart-item");
224        expect(cartItems.length).toBe(0);
225        const orders = await stateManager.get(["Orders"]) as Map<
226          number,
227          unknown
228        >;
229        //Clearing cart of an already committed order should cancel the order and recreate a new order with no items.
230        expect(orders.size).toBe(2);
231        const o = orders.get(orderId) as CodecValue;
232        const order = Order.fromCBOR(o!);
233        expect(order.CanceledAt).toBeDefined();
234        expect(order.State).toBe(OrderState.Canceled);
235      });
236    });
237  
238    unmount();
239    cleanup();
240  });