import type { Product, ShoppingList } from "@commercetools/platform-sdk"; import { afterEach, beforeEach, describe, expect, test } from "vitest"; import { shoppingListDraftFactory } from "#src/testing/shopping-list.ts"; import { CommercetoolsMock } from "../ctMock.ts"; const shoppingList: ShoppingList = { id: "f15b4a80-7def-4381-bf6a-c66cab258a2b", version: 1, lineItems: [ { addedAt: "2021-08-03T14:19:29.496Z", productType: { typeId: "product-type", id: "product-type-id" }, id: "42ea3c57-aced-49ea-ae70-7005a47c7463", productId: "303bf5d8-1201-4fb9-8157-ff6efb8c04b4", name: {}, quantity: 1, productSlug: {}, variantId: 2, published: true, }, ], textLineItems: [], createdAt: "2021-07-22T12:23:33.472Z", lastModifiedAt: "2021-08-03T14:19:29.496Z", name: {}, }; export const product: Product = { id: "303bf5d8-1201-4fb9-8157-ff6efb8c04b4", createdAt: "2022-05-30T13:21:26.777Z", lastModifiedAt: "2022-05-30T13:21:26.777Z", version: 1, productType: { typeId: "product-type", id: "303bf5d8-1201-4fb9-8157-ff6efb8c04b4", }, masterData: { staged: { name: {}, attributes: [], categories: [], slug: {}, masterVariant: { id: 1, sku: "1", }, variants: [], searchKeywords: {}, }, current: { name: {}, slug: {}, attributes: [], categories: [], masterVariant: { id: 1, availability: {}, }, variants: [ { id: 2, sku: "22241940260", }, ], searchKeywords: {}, }, hasStagedChanges: true, published: true, }, }; describe("Shopping List", () => { const ctMock = new CommercetoolsMock({ defaultProjectKey: "dummy", }); beforeEach(async () => { await ctMock.project().unsafeAdd("product", product); await ctMock.project().unsafeAdd("shopping-list", shoppingList); }); test("Adds variant ID on lineItems when creating", async () => { const draft = shoppingListDraftFactory(ctMock).build({ name: {}, lineItems: [{ sku: "22241940260" }], }); const response = await ctMock.app.inject({ method: "POST", url: "/dummy/shopping-lists", payload: draft, }); expect(response.statusCode).toBe(201); expect(response.json().lineItems[0].variantId).toBe(2); }); test("Expands variant on lineItems when getting", async () => { const response = await ctMock.app.inject({ method: "GET", url: `/dummy/shopping-lists/${shoppingList.id}?expand=lineItems[*].variant`, }); expect(response.statusCode).toBe(200); expect(response.json().lineItems[0].variant).toEqual({ id: 2, sku: "22241940260", }); }); test("Expands variant on lineItems when creating", async () => { const draft = shoppingListDraftFactory(ctMock).build({ name: {}, lineItems: [{ sku: "22241940260" }], }); const response = await ctMock.app.inject({ method: "POST", url: "/dummy/shopping-lists?expand=lineItems[*].variant", payload: draft, }); expect(response.statusCode).toBe(201); expect(response.json().lineItems[0].variant).toEqual({ id: 2, sku: "22241940260", }); }); }); describe("Shopping List Update Actions", () => { const ctMock = new CommercetoolsMock({ defaultProjectKey: "dummy", }); beforeEach(async () => { await ctMock.project().unsafeAdd("product", product); await ctMock.project().unsafeAdd("shopping-list", shoppingList); }); afterEach(async () => { await ctMock.clear(); }); test("addLineItem by productID & variantID", async () => { await ctMock.clear(); await ctMock.project().unsafeAdd("product", product); await ctMock .project() .unsafeAdd("shopping-list", { ...shoppingList, lineItems: [] }); const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "addLineItem", productId: product.id, variantId: product.masterData.current.variants[0].id, }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.version).toBe(2); expect(body.lineItems).toHaveLength(1); expect(body.lineItems[0].variantId).toEqual(2); }); test("addLineItem by productID", async () => { await ctMock.clear(); await ctMock.project().unsafeAdd("product", product); await ctMock .project() .unsafeAdd("shopping-list", { ...shoppingList, lineItems: [] }); const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "addLineItem", productId: product.id, }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.version).toBe(2); expect(body.lineItems).toHaveLength(1); expect(body.lineItems[0].variantId).toEqual(1); }); test("addLineItem by sku", async () => { await ctMock.clear(); await ctMock.project().unsafeAdd("product", product); await ctMock .project() .unsafeAdd("shopping-list", { ...shoppingList, lineItems: [] }); const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "addLineItem", sku: "22241940260", }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.version).toBe(2); expect(body.lineItems).toHaveLength(1); expect(body.lineItems[0].variantId).toEqual(2); }); test("addLineItem increases quantity", async () => { const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "addLineItem", sku: "22241940260", }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.version).toBe(2); expect(body.lineItems).toHaveLength(1); expect(body.lineItems[0].variantId).toEqual(2); expect(body.lineItems[0].quantity).toEqual(2); }); test("addLineItem unknown product", async () => { const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [{ action: "addLineItem", productId: "123", variantId: 1 }], }, }); expect(response.statusCode).toBe(400); expect(response.json().message).toBe("A product with ID '123' not found."); }); test("addLineItem by productID", async () => { await ctMock.clear(); await ctMock.project().unsafeAdd("product", product); await ctMock .project() .unsafeAdd("shopping-list", { ...shoppingList, lineItems: [] }); const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "addLineItem", productId: product.id, key: "my-key", }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.version).toBe(2); expect(body.lineItems).toHaveLength(1); expect(body.lineItems[0].key).toBeDefined(); expect(body.lineItems[0].key).toEqual("my-key"); }); test("removeLineItem", async () => { const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "removeLineItem", lineItemId: shoppingList.lineItems[0].id, }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.version).toBe(2); expect(body.lineItems).toHaveLength(0); }); test("removeLineItem decreases quantity", async () => { await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "addLineItem", sku: "22241940260", }, ], }, }); const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 2, actions: [ { action: "removeLineItem", lineItemId: shoppingList.lineItems[0].id, quantity: 1, }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.version).toBe(3); expect(body.lineItems).toHaveLength(1); expect(body.lineItems[0].quantity).toBe(1); }); test("changeLineItemQuantity sets quantity", async () => { const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "changeLineItemQuantity", lineItemId: shoppingList.lineItems[0].id, quantity: 2, }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.version).toBe(2); expect(body.lineItems.length).toBe(1); expect(body.lineItems[0].quantity).toBe(2); }); test("changeLineItemQuantity removes line item if quantity is 0", async () => { const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "changeLineItemQuantity", lineItemId: shoppingList.lineItems[0].id, quantity: 0, }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.version).toBe(2); expect(body.lineItems.length).toBe(0); }); test("various setters", async () => { const response = await ctMock.app.inject({ method: "POST", url: `/dummy/shopping-lists/${shoppingList.id}`, payload: { version: 1, actions: [ { action: "setKey", key: "new-key", }, { action: "setSlug", slug: "new-slug", }, { action: "changeName", name: { en: "new name" }, }, { action: "setDescription", description: { en: "new description" }, }, { action: "setCustomer", customer: { typeId: "customer", id: "customer-id" }, }, { action: "setStore", store: { typeId: "store", key: "store-key" } }, { action: "setAnonymousId", anonymousId: "new-anonymous-id" }, { action: "setDeleteDaysAfterLastModification", deleteDaysAfterLastModification: 1, }, ], }, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.key).toBe("new-key"); expect(body.slug).toBe("new-slug"); expect(body.name).toEqual({ en: "new name" }); expect(body.description).toEqual({ en: "new description" }); expect(body.customer).toEqual({ typeId: "customer", id: "customer-id", }); expect(body.store).toEqual({ typeId: "store", key: "store-key" }); expect(body.anonymousId).toEqual("new-anonymous-id"); expect(body.deleteDaysAfterLastModification).toEqual(1); }); });