feat(core-flows,product,types): scoped variant images (#13623)
* wip(product): variant images * fix: return type * wip: repo and list approach * fix: redo repo method, make test pass * fix: change getVariantImages impl * feat: update test * feat: API and core flows layer * wip: integration spec * fix: deterministic test * chore: refactor and simplify, cleanup, remove repo method * wip: batch add all images to all vairants * fix: remove, expand testing * refactor: pass variants instead of refetch * chore: expand integration test * feat: test multi assign route * fix: remove `/admin/products/:id/variants/images` route * feat: batch images to variant endpoint * fix: length assertion * feat: variant thumbnail * fix: send variant thumbnail by default * fix: product export test assertion * fix: test * feat: variant thumbnail on line item * fix: add missing list and count method, update types * feat: optimise variant images lookups * feat: thumbnail management in core flows * fix: typos, type, build * feat: cascade delete to pivot table, rm unused unused fields * feat(dashboard): variant images management UI (#13670) * wip(dashboard): setup variant media form * wip: cleanup table and images, wip check handler * feat: proper sidebar functionallity * fefat: add js-sdk and hooks * feat: allow only one selection * wip: lazy load variants in the table * feat: new variants management for images on product details * chore: refactor * wip: variant details page work * fix: cleanup media section, fix issues and types * feat: correct scoped images, cleanup in edit modal * feat: js sdk and hooks, filter out product images on variant details, labels, add API call and wrap UI * chore: cleanup * refacto: rename route * feat: thumbnail functionallity * fix: refresh checked after revalidation load * fix: rm unused, refactor type * Create thirty-clocks-refuse.md * feat: new add remove variant media layout * feat: new image add UX --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> * fix: table name in migration * chore: update changesets --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
1 parent
bafd006094
commit
4757281677
57 files changed
+3323
-80
No files matched your search
@@ -298,6 +298,7 @@ medusaIntegrationTestRunner({
|
||||
"Variant Deleted At": expect.any(String),
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Thumbnail": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Id": expect.any(String),
|
||||
"Variant Length": "",
|
||||
@@ -357,6 +358,7 @@ medusaIntegrationTestRunner({
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Thumbnail": "",
|
||||
"Variant Id": expect.any(String),
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
@@ -415,12 +417,14 @@ medusaIntegrationTestRunner({
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Thumbnail": "",
|
||||
"Variant Id": expect.any(String),
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Thumbnail": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "large",
|
||||
"Variant Option 2 Name": "color",
|
||||
@@ -505,6 +509,7 @@ medusaIntegrationTestRunner({
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Thumbnail": "",
|
||||
"Variant Id": expect.any(String),
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
@@ -557,6 +562,7 @@ medusaIntegrationTestRunner({
|
||||
"Product Updated At": expect.any(String),
|
||||
"Product Weight": "",
|
||||
"Product Width": "",
|
||||
"Variant Thumbnail": "",
|
||||
"Variant Allow Backorder": false,
|
||||
"Variant Barcode": "",
|
||||
"Variant Created At": expect.any(String),
|
||||
@@ -692,6 +698,7 @@ medusaIntegrationTestRunner({
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Thumbnail": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "large",
|
||||
"Variant Option 2 Name": "color",
|
||||
@@ -782,6 +789,7 @@ medusaIntegrationTestRunner({
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Thumbnail": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "large",
|
||||
"Variant Option 2 Name": "color",
|
||||
|
||||
@@ -3902,6 +3902,308 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/products/:id/images/:image_id/variants/batch", () => {
|
||||
it("should batch assign and remove variants from images", async () => {
|
||||
// Create a product with multiple images
|
||||
const productWithMultipleImages = await api.post(
|
||||
"/admin/products",
|
||||
{
|
||||
title: "product with multiple images",
|
||||
status: "published",
|
||||
options: [
|
||||
{
|
||||
title: "size",
|
||||
values: ["large", "small"],
|
||||
},
|
||||
{
|
||||
title: "color",
|
||||
values: ["red", "blue"],
|
||||
},
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: "https://via.placeholder.com/100",
|
||||
},
|
||||
{
|
||||
url: "https://via.placeholder.com/200",
|
||||
},
|
||||
{
|
||||
url: "https://via.placeholder.com/300",
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const product = productWithMultipleImages.data.product
|
||||
|
||||
const variant1Response = await api.post(
|
||||
`/admin/products/${product.id}/variants`,
|
||||
{
|
||||
title: "variant 1",
|
||||
options: { size: "large", color: "red" },
|
||||
prices: [{ currency_code: "usd", amount: 100 }],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const variant2Response = await api.post(
|
||||
`/admin/products/${product.id}/variants`,
|
||||
{
|
||||
title: "variant 2",
|
||||
options: { size: "small", color: "blue" },
|
||||
prices: [{ currency_code: "usd", amount: 200 }],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const variant1 = variant1Response.data.product.variants.find(
|
||||
(v) => v.title === "variant 1"
|
||||
)
|
||||
const variant2 = variant2Response.data.product.variants.find(
|
||||
(v) => v.title === "variant 2"
|
||||
)
|
||||
|
||||
const addResponse = await api.post(
|
||||
`/admin/products/${product.id}/images/${product.images[0].id}/variants/batch`,
|
||||
{
|
||||
add: [variant1.id, variant2.id],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(addResponse.status).toBe(200)
|
||||
expect(addResponse.data.added).toHaveLength(2)
|
||||
expect(addResponse.data.added).toContain(variant1.id)
|
||||
expect(addResponse.data.added).toContain(variant2.id)
|
||||
|
||||
const addResponse2 = await api.post(
|
||||
`/admin/products/${product.id}/images/${product.images[1].id}/variants/batch`,
|
||||
{
|
||||
add: [variant1.id],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(addResponse2.status).toBe(200)
|
||||
expect(addResponse2.data.added).toHaveLength(1)
|
||||
expect(addResponse2.data.added).toContain(variant1.id)
|
||||
|
||||
const variant1WithImages = await api.get(
|
||||
`/admin/products/${product.id}/variants/${variant1.id}?fields=*images`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const variant2WithImages = await api.get(
|
||||
`/admin/products/${product.id}/variants/${variant2.id}?fields=*images`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(variant1WithImages.data.variant.images).toHaveLength(3)
|
||||
|
||||
// Variant 1 should have both images (first and second)
|
||||
expect(variant1WithImages.data.variant.images).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: product.images[0].id, // Variant image
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: product.images[1].id, // Variant image
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: product.images[2].id, // General product image
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
expect(variant2WithImages.data.variant.images).toHaveLength(2)
|
||||
|
||||
// Variant 2 should have the first image
|
||||
expect(variant2WithImages.data.variant.images).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: product.images[0].id, // Variant image
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: product.images[2].id, // General product image
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
const removeResponse = await api.post(
|
||||
`/admin/products/${product.id}/images/${product.images[0].id}/variants/batch`,
|
||||
{
|
||||
remove: [variant1.id],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(removeResponse.status).toBe(200)
|
||||
expect(removeResponse.data.removed).toHaveLength(1)
|
||||
expect(removeResponse.data.removed).toContain(variant1.id)
|
||||
|
||||
const variant1WithImagesAfterRemove = await api.get(
|
||||
`/admin/products/${product.id}/variants/${variant1.id}?fields=*images`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(
|
||||
variant1WithImagesAfterRemove.data.variant.images
|
||||
).toHaveLength(2)
|
||||
expect(variant1WithImagesAfterRemove.data.variant.images).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: product.images[1].id, // Variant image
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: product.images[2].id, // General product image
|
||||
}),
|
||||
])
|
||||
)
|
||||
|
||||
const variant2WithImagesAfterRemove = await api.get(
|
||||
`/admin/products/${product.id}/variants/${variant2.id}?fields=*images`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(
|
||||
variant2WithImagesAfterRemove.data.variant.images
|
||||
).toHaveLength(2)
|
||||
expect(variant2WithImagesAfterRemove.data.variant.images).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
// Removed from the first variant but still on the second
|
||||
id: product.images[0].id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: product.images[2].id,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/products/:id/variants/:variant_id/images/batch", () => {
|
||||
it("should batch manage images for a specific variant", async () => {
|
||||
// Create a product with multiple images and variants
|
||||
const productWithMultipleImages = await api.post(
|
||||
"/admin/products",
|
||||
{
|
||||
title: "product for variant image batch management",
|
||||
status: "published",
|
||||
options: [
|
||||
{
|
||||
title: "size",
|
||||
values: ["large", "small"],
|
||||
},
|
||||
{
|
||||
title: "color",
|
||||
values: ["red", "blue"],
|
||||
},
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: "https://via.placeholder.com/100",
|
||||
},
|
||||
{
|
||||
url: "https://via.placeholder.com/200",
|
||||
},
|
||||
{
|
||||
url: "https://via.placeholder.com/300",
|
||||
},
|
||||
{
|
||||
url: "https://via.placeholder.com/400",
|
||||
},
|
||||
],
|
||||
variants: [
|
||||
{
|
||||
title: "variant 1",
|
||||
options: { size: "large", color: "red" },
|
||||
prices: [{ currency_code: "usd", amount: 100 }],
|
||||
},
|
||||
{
|
||||
title: "variant 2",
|
||||
options: { size: "small", color: "blue" },
|
||||
prices: [{ currency_code: "usd", amount: 200 }],
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const product = productWithMultipleImages.data.product
|
||||
const variant1 = product.variants.find((v) => v.title === "variant 1")
|
||||
const variant2 = product.variants.find((v) => v.title === "variant 2")
|
||||
|
||||
// First, assign some images to variant1
|
||||
const initialAssignResponse = await api.post(
|
||||
`/admin/products/${product.id}/variants/${variant1.id}/images/batch`,
|
||||
{
|
||||
add: [product.images[0].id, product.images[1].id],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(initialAssignResponse.status).toBe(200)
|
||||
expect(initialAssignResponse.data.added).toHaveLength(2)
|
||||
expect(initialAssignResponse.data.added).toEqual(
|
||||
expect.arrayContaining([product.images[0].id, product.images[1].id])
|
||||
)
|
||||
|
||||
// Now batch manage images for variant1: add one more, remove one
|
||||
const batchResponse = await api.post(
|
||||
`/admin/products/${product.id}/variants/${variant1.id}/images/batch`,
|
||||
{
|
||||
add: [product.images[2].id],
|
||||
remove: [product.images[0].id],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(batchResponse.status).toBe(200)
|
||||
expect(batchResponse.data.added).toHaveLength(1)
|
||||
expect(batchResponse.data.added).toEqual(
|
||||
expect.arrayContaining([product.images[2].id])
|
||||
)
|
||||
expect(batchResponse.data.removed).toHaveLength(1)
|
||||
expect(batchResponse.data.removed).toEqual(
|
||||
expect.arrayContaining([product.images[0].id])
|
||||
)
|
||||
|
||||
// Verify the final state by checking variant1 images
|
||||
const variant1WithImages = await api.get(
|
||||
`/admin/products/${product.id}/variants/${variant1.id}?fields=*images`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
// Should have 3 images: images[0] and images[3] (general product image), images[1] and images[2] variant scoped
|
||||
expect(variant1WithImages.data.variant.images).toHaveLength(4)
|
||||
expect(variant1WithImages.data.variant.images).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: product.images[0].id }),
|
||||
expect.objectContaining({ id: product.images[1].id }),
|
||||
expect.objectContaining({ id: product.images[2].id }),
|
||||
expect.objectContaining({ id: product.images[3].id }),
|
||||
])
|
||||
)
|
||||
|
||||
// Verify variant2
|
||||
const variant2WithImages = await api.get(
|
||||
`/admin/products/${product.id}/variants/${variant2.id}?fields=*images`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
// Should only have the general product image
|
||||
expect(variant2WithImages.data.variant.images).toHaveLength(2)
|
||||
expect(variant2WithImages.data.variant.images).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: product.images[0].id }),
|
||||
expect.objectContaining({ id: product.images[3].id }),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -1565,6 +1565,146 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
})
|
||||
|
||||
it("should add one item with variant thumbnail and one item with product thumbnail", async () => {
|
||||
const salesChannel = await scModuleService.createSalesChannels({
|
||||
name: "Webshop",
|
||||
})
|
||||
|
||||
const location = await stockLocationModule.createStockLocations({
|
||||
name: "Warehouse",
|
||||
})
|
||||
|
||||
let cart = await cartModuleService.createCarts({
|
||||
currency_code: "usd",
|
||||
sales_channel_id: salesChannel.id,
|
||||
})
|
||||
|
||||
await remoteLink.create([
|
||||
{
|
||||
[Modules.SALES_CHANNEL]: {
|
||||
sales_channel_id: salesChannel.id,
|
||||
},
|
||||
[Modules.STOCK_LOCATION]: {
|
||||
stock_location_id: location.id,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const [product1, product2] = await productModule.createProducts([
|
||||
{
|
||||
title: "Test product 1",
|
||||
status: ProductStatus.PUBLISHED,
|
||||
thumbnail: "product-thumbnail-1",
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant 1",
|
||||
manage_inventory: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Test product 2",
|
||||
status: ProductStatus.PUBLISHED,
|
||||
thumbnail: "product-thumbnail-2",
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant 2",
|
||||
manage_inventory: false,
|
||||
thumbnail: "variant-thumbnail-2",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const priceSet1 = await pricingModule.createPriceSets({
|
||||
prices: [
|
||||
{
|
||||
amount: 30,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const priceSet2 = await pricingModule.createPriceSets({
|
||||
prices: [
|
||||
{
|
||||
amount: 30,
|
||||
currency_code: "usd",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await pricingModule.createPricePreferences({
|
||||
attribute: "currency_code",
|
||||
value: "usd",
|
||||
is_tax_inclusive: true,
|
||||
})
|
||||
|
||||
await remoteLink.create([
|
||||
{
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product1.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: priceSet1.id,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await remoteLink.create([
|
||||
{
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product2.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: priceSet2.id,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
cart = await cartModuleService.retrieveCart(cart.id, {
|
||||
select: ["id", "region_id", "currency_code", "sales_channel_id"],
|
||||
})
|
||||
|
||||
await addToCartWorkflow(appContainer).run({
|
||||
input: {
|
||||
items: [
|
||||
{
|
||||
variant_id: product1.variants[0].id,
|
||||
quantity: 1,
|
||||
},
|
||||
{
|
||||
variant_id: product2.variants[0].id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
cart_id: cart.id,
|
||||
},
|
||||
})
|
||||
|
||||
cart = await cartModuleService.retrieveCart(cart.id, {
|
||||
relations: ["items"],
|
||||
})
|
||||
|
||||
expect(cart.items).toHaveLength(2)
|
||||
expect(cart.items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
variant_id: product1.variants.find(
|
||||
(v) => v.title === "Test variant 1"
|
||||
)!.id,
|
||||
thumbnail: "product-thumbnail-1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
variant_id: product2.variants.find(
|
||||
(v) => v.title === "Test variant 2"
|
||||
)!.id,
|
||||
thumbnail: "variant-thumbnail-2",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should add custom item to cart", async () => {
|
||||
const salesChannel = await scModuleService.createSalesChannels({
|
||||
name: "Webshop",
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
import {
|
||||
batchImageVariantsWorkflow,
|
||||
batchVariantImagesWorkflow,
|
||||
} from "@medusajs/core-flows"
|
||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const env = {}
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
env,
|
||||
testSuite: ({ getContainer }) => {
|
||||
describe("Workflows: Batch variant image management", () => {
|
||||
let appContainer
|
||||
let productModule: IProductModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
appContainer = getContainer()
|
||||
productModule = appContainer.resolve(Modules.PRODUCT)
|
||||
})
|
||||
|
||||
const createVariantWithImage = async (
|
||||
imageUrl: string,
|
||||
suffix: string
|
||||
) => {
|
||||
const [createdProduct] = await productModule.createProducts([
|
||||
{
|
||||
title: `test-product-${suffix}`,
|
||||
images: [{ url: imageUrl }],
|
||||
variants: [
|
||||
{
|
||||
title: `test-variant-${suffix}`,
|
||||
sku: `test-variant-sku-${suffix}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const [product] = await productModule.listProducts(
|
||||
{ id: createdProduct.id },
|
||||
{
|
||||
relations: ["variants", "images"],
|
||||
}
|
||||
)
|
||||
|
||||
const variant = product.variants[0]!
|
||||
const image = product.images[0]!
|
||||
|
||||
await productModule.updateProductVariants(variant.id, {
|
||||
thumbnail: imageUrl,
|
||||
})
|
||||
|
||||
await productModule.addImageToVariant([
|
||||
{
|
||||
variant_id: variant.id,
|
||||
image_id: image.id,
|
||||
},
|
||||
])
|
||||
|
||||
const [variantWithThumbnail] = await productModule.listProductVariants(
|
||||
{ id: variant.id },
|
||||
{
|
||||
select: ["id", "thumbnail"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(variantWithThumbnail.thumbnail).toEqual(imageUrl)
|
||||
|
||||
return {
|
||||
variantId: variant.id,
|
||||
imageId: image.id,
|
||||
imageUrl,
|
||||
}
|
||||
}
|
||||
|
||||
it("clears the variant thumbnail when removing images via batchVariantImagesWorkflow", async () => {
|
||||
const imageUrl = "https://test-image-url.com/image-1.png"
|
||||
const { variantId, imageId } = await createVariantWithImage(
|
||||
imageUrl,
|
||||
"variant-workflow"
|
||||
)
|
||||
|
||||
const workflow = batchVariantImagesWorkflow(appContainer)
|
||||
const { result } = await workflow.run({
|
||||
input: {
|
||||
variant_id: variantId,
|
||||
remove: [imageId],
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.removed).toEqual([imageId])
|
||||
|
||||
const [updatedVariant] = await productModule.listProductVariants(
|
||||
{ id: variantId },
|
||||
{
|
||||
select: ["id", "thumbnail"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(updatedVariant.thumbnail).toBeNull()
|
||||
})
|
||||
|
||||
it("clears the variant thumbnail when removing variants via batchImageVariantsWorkflow", async () => {
|
||||
const imageUrl = "https://test-image-url.com/image-2.png"
|
||||
const { variantId, imageId } = await createVariantWithImage(
|
||||
imageUrl,
|
||||
"image-workflow"
|
||||
)
|
||||
|
||||
const workflow = batchImageVariantsWorkflow(appContainer)
|
||||
const { result } = await workflow.run({
|
||||
input: {
|
||||
image_id: imageId,
|
||||
remove: [variantId],
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.removed).toEqual([variantId])
|
||||
|
||||
const [updatedVariant] = await productModule.listProductVariants(
|
||||
{ id: variantId },
|
||||
{
|
||||
select: ["id", "thumbnail"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(updatedVariant.thumbnail).toBeNull()
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
Reference in new issue
Block a user