diff --git a/integration-tests/api/__tests__/admin/product.js b/integration-tests/api/__tests__/admin/product.js index 7b0696e02c..e179af156d 100644 --- a/integration-tests/api/__tests__/admin/product.js +++ b/integration-tests/api/__tests__/admin/product.js @@ -1,49 +1,49 @@ -const path = require("path"); +const path = require("path") -const setupServer = require("../../../helpers/setup-server"); -const { useApi } = require("../../../helpers/use-api"); -const { initDb, useDb } = require("../../../helpers/use-db"); +const setupServer = require("../../../helpers/setup-server") +const { useApi } = require("../../../helpers/use-api") +const { initDb, useDb } = require("../../../helpers/use-db") -const adminSeeder = require("../../helpers/admin-seeder"); -const productSeeder = require("../../helpers/product-seeder"); +const adminSeeder = require("../../helpers/admin-seeder") +const productSeeder = require("../../helpers/product-seeder") -jest.setTimeout(30000); +jest.setTimeout(30000) describe("/admin/products", () => { - let medusaProcess; - let dbConnection; + let medusaProcess + let dbConnection beforeAll(async () => { - const cwd = path.resolve(path.join(__dirname, "..", "..")); - dbConnection = await initDb({ cwd }); - medusaProcess = await setupServer({ cwd }); - }); + const cwd = path.resolve(path.join(__dirname, "..", "..")) + dbConnection = await initDb({ cwd }) + medusaProcess = await setupServer({ cwd }) + }) afterAll(async () => { - const db = useDb(); - await db.shutdown(); + const db = useDb() + await db.shutdown() - medusaProcess.kill(); - }); + medusaProcess.kill() + }) describe("POST /admin/products", () => { beforeEach(async () => { try { - await productSeeder(dbConnection); - await adminSeeder(dbConnection); + await productSeeder(dbConnection) + await adminSeeder(dbConnection) } catch (err) { - console.log(err); - throw err; + console.log(err) + throw err } - }); + }) afterEach(async () => { - const db = useDb(); - await db.teardown(); - }); + const db = useDb() + await db.teardown() + }) it("creates a product", async () => { - const api = useApi(); + const api = useApi() const payload = { title: "Test product", @@ -61,7 +61,7 @@ describe("/admin/products", () => { options: [{ value: "large" }, { value: "green" }], }, ], - }; + } const response = await api .post("/admin/products", payload, { @@ -70,10 +70,10 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data.product).toEqual( expect.objectContaining({ @@ -133,11 +133,67 @@ describe("/admin/products", () => { }), ], }) - ); - }); + ) + }) + + it("Sets variant ranks when creating a product", async () => { + const api = useApi() + + const payload = { + title: "Test product - 1", + description: "test-product-description 1", + type: { value: "test-type 1" }, + images: ["test-image.png", "test-image-2.png"], + collection_id: "test-collection", + tags: [{ value: "123" }, { value: "456" }], + options: [{ title: "size" }, { title: "color" }], + variants: [ + { + title: "Test variant 1", + inventory_quantity: 10, + prices: [{ currency_code: "usd", amount: 100 }], + options: [{ value: "large" }, { value: "green" }], + }, + { + title: "Test variant 2", + inventory_quantity: 10, + prices: [{ currency_code: "usd", amount: 100 }], + options: [{ value: "large" }, { value: "green" }], + }, + ], + } + + const response = await api + .post("/admin/products", payload, { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + + expect(response.status).toEqual(200) + + expect(response.data.product).toEqual( + expect.objectContaining({ + title: "Test product - 1", + variants: [ + expect.objectContaining({ + title: "Test variant 1", + rank: 0, + }), + expect.objectContaining({ + title: "Test variant 2", + rank: 1, + }), + ], + }) + ) + }) it("creates a giftcard", async () => { - const api = useApi(); + const api = useApi() const payload = { title: "Test Giftcard", @@ -151,7 +207,7 @@ describe("/admin/products", () => { options: [{ value: "100" }], }, ], - }; + } const response = await api .post("/admin/products", payload, { @@ -160,21 +216,21 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data.product).toEqual( expect.objectContaining({ title: "Test Giftcard", discountable: false, }) - ); - }); + ) + }) it("updates a product (update prices, tags, delete collection, delete type, replaces images)", async () => { - const api = useApi(); + const api = useApi() const payload = { collection_id: null, @@ -182,13 +238,19 @@ describe("/admin/products", () => { variants: [ { id: "test-variant", - prices: [{ currency_code: "usd", amount: 100, sale_amount: 75 }], + prices: [ + { + currency_code: "usd", + amount: 100, + sale_amount: 75, + }, + ], }, ], tags: [{ value: "123" }], images: ["test-image-2.png"], type: { value: "test-type-2" }, - }; + } const response = await api .post("/admin/products/test-product", payload, { @@ -197,10 +259,10 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data.product).toEqual( expect.objectContaining({ @@ -231,15 +293,72 @@ describe("/admin/products", () => { value: "test-type-2", }), }) - ); - }); + ) + }) + + it("updates a product (variant ordering)", async () => { + const api = useApi() + + const payload = { + collection_id: null, + type: null, + variants: [ + { + id: "test-variant", + }, + { + id: "test-variant_1", + }, + { + id: "test-variant_2", + }, + ], + } + + const response = await api + .post("/admin/products/test-product", payload, { + headers: { + Authorization: "Bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + + expect(response.status).toEqual(200) + + expect(response.data.product).toEqual( + expect.objectContaining({ + title: "Test product", + variants: [ + expect.objectContaining({ + id: "test-variant", + rank: 0, + title: "Test variant", + }), + expect.objectContaining({ + id: "test-variant_1", + rank: 1, + title: "Test variant rank (1)", + }), + expect.objectContaining({ + id: "test-variant_2", + rank: 2, + title: "Test variant rank (2)", + }), + ], + type: null, + collection: null, + }) + ) + }) it("add option", async () => { - const api = useApi(); + const api = useApi() const payload = { title: "should_add", - }; + } const response = await api .post("/admin/products/test-product/options", payload, { @@ -248,41 +367,41 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data.product).toEqual( expect.objectContaining({ - options: [ + options: expect.arrayContaining([ expect.objectContaining({ title: "should_add", product_id: "test-product", }), - ], + ]), }) - ); - }); - }); + ) + }) + }) describe("testing for soft-deletion + uniqueness on handles, collection and variant properties", () => { beforeEach(async () => { try { - await productSeeder(dbConnection); - await adminSeeder(dbConnection); + await productSeeder(dbConnection) + await adminSeeder(dbConnection) } catch (err) { - console.log(err); - throw err; + console.log(err) + throw err } - }); + }) afterEach(async () => { - const db = useDb(); - await db.teardown(); - }); + const db = useDb() + await db.teardown() + }) it("successfully deletes a product", async () => { - const api = useApi(); + const api = useApi() const response = await api .delete("/admin/products/test-product", { @@ -291,21 +410,21 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); + expect(response.status).toEqual(200) expect(response.data).toEqual( expect.objectContaining({ id: "test-product", deleted: true, }) - ); - }); + ) + }) it("successfully creates product with soft-deleted product handle", async () => { - const api = useApi(); + const api = useApi() // First we soft-delete the product const response = await api @@ -315,11 +434,11 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); - expect(response.data.id).toEqual("test-product"); + expect(response.status).toEqual(200) + expect(response.data.id).toEqual("test-product") // Lets try to create a product with same handle as deleted one const payload = { @@ -339,20 +458,20 @@ describe("/admin/products", () => { options: [{ value: "large" }, { value: "green" }], }, ], - }; + } const res = await api.post("/admin/products", payload, { headers: { Authorization: "Bearer test_token", }, - }); + }) - expect(res.status).toEqual(200); - expect(res.data.product.handle).toEqual("test-product"); - }); + expect(res.status).toEqual(200) + expect(res.data.product.handle).toEqual("test-product") + }) it("successfully deletes product collection", async () => { - const api = useApi(); + const api = useApi() // First we soft-delete the product collection const response = await api @@ -362,15 +481,15 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); - expect(response.data.id).toEqual("test-collection"); - }); + expect(response.status).toEqual(200) + expect(response.data.id).toEqual("test-collection") + }) it("successfully creates soft-deleted product collection", async () => { - const api = useApi(); + const api = useApi() const response = await api .delete("/admin/collections/test-collection", { @@ -379,30 +498,43 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); - expect(response.data.id).toEqual("test-collection"); + expect(response.status).toEqual(200) + expect(response.data.id).toEqual("test-collection") // Lets try to create a product collection with same handle as deleted one const payload = { title: "Another test collection", handle: "test-collection", - }; + } const res = await api.post("/admin/collections", payload, { headers: { Authorization: "Bearer test_token", }, - }); + }) - expect(res.status).toEqual(200); - expect(res.data.collection.handle).toEqual("test-collection"); - }); + expect(res.status).toEqual(200) + expect(res.data.collection.handle).toEqual("test-collection") + }) it("successfully creates soft-deleted product variant", async () => { - const api = useApi(); + const api = useApi() + + console.log("test") + + const product = await api + .get("/admin/products/test-product", { + headers: { + Authorization: "bearer test_token", + }, + }) + .catch((err) => { + console.log(err) + }) + console.log(product.data.product) const response = await api .delete("/admin/products/test-product/variants/test-variant", { @@ -411,11 +543,11 @@ describe("/admin/products", () => { }, }) .catch((err) => { - console.log(err); - }); + console.log(err) + }) - expect(response.status).toEqual(200); - expect(response.data.variant_id).toEqual("test-variant"); + expect(response.status).toEqual(200) + expect(response.data.variant_id).toEqual("test-variant") // Lets try to create a product collection with same handle as deleted one const payload = { @@ -430,19 +562,18 @@ describe("/admin/products", () => { amount: 100, }, ], - }; + options: [{ option_id: "test-option", value: "inserted value" }], + } - const res = await api.post( - "/admin/products/test-product/variants", - payload, - { + const res = await api + .post("/admin/products/test-product/variants", payload, { headers: { Authorization: "Bearer test_token", }, - } - ); + }) + .catch((err) => console.log(err)) - expect(res.status).toEqual(200); + expect(res.status).toEqual(200) expect(res.data.product.variants).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -453,7 +584,7 @@ describe("/admin/products", () => { barcode: "test-barcode", }), ]) - ); - }); - }); -}); + ) + }) + }) +}) diff --git a/integration-tests/api/helpers/product-seeder.js b/integration-tests/api/helpers/product-seeder.js index 77fd31a034..ae7109b4e6 100644 --- a/integration-tests/api/helpers/product-seeder.js +++ b/integration-tests/api/helpers/product-seeder.js @@ -2,55 +2,58 @@ const { ProductCollection, ProductTag, ProductType, + ProductOption, + ProductOptionValue, Region, Product, ShippingProfile, ProductVariant, Image, -} = require("@medusajs/medusa"); + MoneyAmount, +} = require("@medusajs/medusa") module.exports = async (connection, data = {}) => { - const manager = connection.manager; + const manager = connection.manager const defaultProfile = await manager.findOne(ShippingProfile, { type: "default", - }); + }) const coll = manager.create(ProductCollection, { id: "test-collection", handle: "test-collection", title: "Test collection", - }); + }) - await manager.save(coll); + await manager.save(coll) const tag = manager.create(ProductTag, { id: "tag1", value: "123", - }); + }) - await manager.save(tag); + await manager.save(tag) const type = manager.create(ProductType, { id: "test-type", value: "test-type", - }); + }) - await manager.save(type); + await manager.save(type) const image = manager.create(Image, { id: "test-image", url: "test-image.png", - }); + }) - await manager.save(image); + await manager.save(image) await manager.insert(Region, { id: "test-region", name: "Test Region", currency_code: "usd", tax_rate: 0, - }); + }) const p = manager.create(Product, { id: "test-product", @@ -64,23 +67,107 @@ module.exports = async (connection, data = {}) => { { id: "tag1", value: "123" }, { tag: "tag2", value: "456" }, ], - options: [{ id: "test-option", title: "Default value" }], - }); + }) - p.images = [image]; + const productOption = manager.create(ProductOption, { + id: "test-option", + title: "test-option", + }) - await manager.save(p); + await manager.save(productOption) - await manager.insert(ProductVariant, { + p.options = [productOption] + + p.images = [image] + + await manager.save(p) + + const variant1 = await manager.create(ProductVariant, { id: "test-variant", inventory_quantity: 10, title: "Test variant", + rank: 0, sku: "test-sku", ean: "test-ean", upc: "test-upc", barcode: "test-barcode", product_id: "test-product", prices: [{ id: "test-price", currency_code: "usd", amount: 100 }], - options: [{ id: "test-variant-option", value: "Default variant" }], - }); -}; + // options: [{ id: "test-variant-option", value: "Default variant" }], + }) + + const variantOption_1 = manager.create(ProductOptionValue, { + id: "test-option-variant1", + option_id: "test-option", + value: "test-option1", + variant_id: "test-variant", + // product_id: "test-product", + }) + + // variant1.options = [variantOption_1] + + await manager.save(variant1) + await manager.save(variantOption_1) + + const variant2 = await manager.create(ProductVariant, { + id: "test-variant_1", + inventory_quantity: 10, + title: "Test variant rank (1)", + rank: 2, + sku: "test-sku1", + ean: "test-ean1", + upc: "test-upc1", + barcode: "test-barcode 1", + product_id: "test-product", + prices: [{ id: "test-price1", currency_code: "usd", amount: 100 }], + // options: [{ id: "test-variant-option-1", value: "Default variant 1" }], + }) + + const variantOption_2 = manager.create(ProductOptionValue, { + id: "test-option-variant2", + option_id: "test-option", + value: "test-option2", + variant_id: "test-variant_1", + // product_id: "test-product", + }) + await manager.save(variant2) + + await manager.save(variantOption_2) + + // variant2.options = [variantOption_2] + + const variant3 = await manager.create(ProductVariant, { + id: "test-variant_2", + inventory_quantity: 10, + title: "Test variant rank (2)", + rank: 1, + sku: "test-sku2", + ean: "test-ean2", + upc: "test-upc2", + product_id: "test-product", + prices: [{ id: "test-price2", currency_code: "usd", amount: 100 }], + // options: [{ id: "test-variant-option-2", value: "Default variant 2" }], + }) + + const variantOption_3 = manager.create(ProductOptionValue, { + id: "test-option-variant3", + option_id: "test-option", + value: "test-option3", + variant_id: "test-variant_2", + // product_id: "test-product", + }) + + await manager.save(variant3) + await manager.save(variantOption_3) + + // variant3.options = [variantOption_3] + + const moneyAmount = await manager.create(MoneyAmount, { + id: "money_amount", + amount: 100, + currency_code: "usd", + variant_id: "test-variant", + }) + + await manager.save(moneyAmount) +}