feat:Make product import v1 compatible (#8362)
This commit is contained in:
+6
@@ -0,0 +1,6 @@
|
||||
Product Id;Product Title;Product Subtitle;Product Status;Product External Id;Product Description;Product Handle;Product Is Giftcard;Product Discountable;Product Thumbnail;Product Collection Id;Product Type Id;Product Weight;Product Length;Product Height;Product Width;Product Hs Code;Product Origin Country;Product Mid Code;Product Material;Product Created At;Product Updated At;Product Deleted At;Product Image 1;Product Image 2;Product Tag 1;Product Tag 2;Variant Id;Variant Title;Variant Sku;Variant Barcode;Variant Ean;Variant Upc;Variant Allow Backorder;Variant Manage Inventory;Variant Hs Code;Variant Origin Country;Variant Mid Code;Variant Material;Variant Weight;Variant Length;Variant Height;Variant Width;Variant Metadata;Variant Variant Rank;Variant Product Id;Variant Created At;Variant Updated At;Variant Deleted At;Variant Price USD;Variant Price EUR;Variant Price DKK;Variant Option 1 Name;Variant Option 1 Value;Variant Option 2 Name;Variant Option 2 Value
|
||||
prod_01J3CRPNVGRZ01A8GH8FQYK10Z;Base product;;draft;;"test-product-description
|
||||
test line 2";base-product;false;true;test-image.png;pcol_01J3CRPNT6A0G5GG34MWHWE7QD;ptyp_01J3CRPNV39E51BGGWSKT674C5;;;;;;;;;2024-07-22T08:25:06.158Z;2024-07-22T08:25:06.158Z;;test-image.png;test-image-2.png;123;456;variant_01J3CRPNW5J6EBVVQP1TN33A58;Test variant;;;;;false;true;;;;;;;;;;0;prod_01J3CRPNVGRZ01A8GH8FQYK10Z;2024-07-22T08:25:06.182Z;2024-07-22T08:25:06.182Z;;100;45;30;size;large;color;green
|
||||
prod_01J3CRPNVGRZ01A8GH8FQYK10Z;Base product;;draft;;"test-product-description
|
||||
test line 2";base-product;false;true;test-image.png;pcol_01J3CRPNT6A0G5GG34MWHWE7QD;ptyp_01J3CRPNV39E51BGGWSKT674C5;;;;;;;;;2024-07-22T08:25:06.158Z;2024-07-22T08:25:06.158Z;;test-image.png;test-image-2.png;123;456;variant_01J3CRPNW6NES6EN14X93F6YYB;Test variant 2;;;;;false;true;;;;;;;;;;0;prod_01J3CRPNVGRZ01A8GH8FQYK10Z;2024-07-22T08:25:06.182Z;2024-07-22T08:25:06.182Z;;200;65;50;size;small;color;green
|
||||
prod_01J3CRPNYJTCAV1QKRF6H0BY3M;Proposed product;;proposed;;test-product-description;proposed-product;false;true;test-image.png;;ptyp_01J3CRPNV39E51BGGWSKT674C5;;;;;;;;;2024-07-22T08:25:06.256Z;2024-07-22T08:25:06.256Z;;test-image.png;test-image-2.png;new-tag;;variant_01J3CRPNYZ6VZ5FVJ7WHJABV54;Test variant;;;;;false;true;;;;;;;;;;0;prod_01J3CRPNYJTCAV1QKRF6H0BY3M;2024-07-22T08:25:06.271Z;2024-07-22T08:25:06.271Z;;100;45;30;size;large;color;green
|
||||
|
@@ -1,2 +1,2 @@
|
||||
Product Id,Product Title,Product Subtitle,Product Status,Product External Id,Product Description,Product Handle,Product Is Giftcard,Product Discountable,Product Thumbnail,Product Collection Id,Product Type Id,Product Weight,Product Length,Product Height,Product Width,Product Hs Code,Product Origin Country,Product Mid Code,Product Material,Product Created At,Product Updated At,Product Deleted At,Product Image 1,Product Image 2,Product Tag 1,Variant Id,Variant Title,Variant Sku,Variant Barcode,Variant Ean,Variant Upc,Variant Allow Backorder,Variant Manage Inventory,Variant Hs Code,Variant Origin Country,Variant Mid Code,Variant Material,Variant Weight,Variant Length,Variant Height,Variant Width,Variant Metadata,Variant Variant Rank,Variant Product Id,Variant Created At,Variant Updated At,Variant Deleted At,Variant Price USD,Variant Price EUR,Variant Price reg_nonexistent,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value
|
||||
Product Id,Product Title,Product Subtitle,Product Status,Product External Id,Product Description,Product Handle,Product Is Giftcard,Product Discountable,Product Thumbnail,Product Collection Id,Product Type Id,Product Weight,Product Length,Product Height,Product Width,Product Hs Code,Product Origin Country,Product Mid Code,Product Material,Product Created At,Product Updated At,Product Deleted At,Product Image 1,Product Image 2,Product Tag 1,Variant Id,Variant Title,Variant Sku,Variant Barcode,Variant Ean,Variant Upc,Variant Allow Backorder,Variant Manage Inventory,Variant Hs Code,Variant Origin Country,Variant Mid Code,Variant Material,Variant Weight,Variant Length,Variant Height,Variant Width,Variant Metadata,Variant Variant Rank,Variant Product Id,Variant Created At,Variant Updated At,Variant Deleted At,Variant Price USD,Variant Price EUR,Variant Price nonexistent [EUR],Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value
|
||||
prod_01J3CSN791SN1RN7X155Z8S9CN,Proposed product,,proposed,,test-product-description,proposed-product,false,true,test-image.png,,ptyp_01J3CSN76GCRSCDV9V489B5FWQ,,,,,,,,,2024-07-22T08:41:47.040Z,2024-07-22T08:41:47.040Z,,test-image.png,test-image-2.png,new-tag,variant_01J3CSN79CQ2ND94SRJSXMEMNH,Test variant,,,,,false,true,,,,,,,,,,0,prod_01J3CSN791SN1RN7X155Z8S9CN,2024-07-22T08:41:47.053Z,2024-07-22T08:41:47.053Z,,100,45,30,size,large,color,green
|
||||
|
@@ -0,0 +1,6 @@
|
||||
Product Id,Product Handle,Product Title,Product Subtitle,Product Description,Product Status,Product Thumbnail,Product Weight,Product Length,Product Width,Product Height,Product HS Code,Product Origin Country,Product MID Code,Product Material,Product Collection Title,Product Collection Handle,Product Type,Product Tags,Product Discountable,Product External Id,Variant Id,Variant Title,Variant SKU,Variant Barcode,Variant Inventory Quantity,Variant Allow Backorder,Variant Manage Inventory,Variant Weight,Variant Length,Variant Width,Variant Height,Variant HS Code,Variant Origin Country,Variant MID Code,Variant Material,Price Test region [USD],Price USD,Option 1 Name,Option 1 Value,Option 2 Name,Option 2 Value,Image 1 Url
|
||||
,test-product-product-1,Test product,,"Hopper Stripes Bedding, available as duvet cover, pillow sham and sheet.\n100% organic cotton, soft and crisp to the touch. Made in Portugal.",draft,,,,,,,,,,Test collection 1,test-collection1,,123_1,TRUE,,,Test variant,test-sku-1,test-barcode-1,10,FALSE,TRUE,,,,,,,,,1.00,1.10,test-option-1,option 1 value red,test-option-2,option 2 value 1,test-image.png
|
||||
,test-product-product-1-1,Test product,,"Hopper Stripes Bedding, available as duvet cover, pillow sham and sheet.\n100% organic cotton, soft and crisp to the touch. Made in Portugal.",draft,,,,,,,,,,Test collection 1,test-collection1,,,TRUE,,,Test variant,test-sku-1-1,test-barcode-1-1,10,FALSE,TRUE,,,,,,,,,1.00,1.10,test-option-1,option 1 value red,test-option-2,option 2 value 1,test-image.png
|
||||
existing-product-id,test-product-product-2,Test product,,test-product-description,draft,test-image.png,,,,,,,,,Test collection,test-collection2,test-type,123,TRUE,,,Test variant,test-sku-2,test-barcode-2,10,FALSE,TRUE,,,,,,,,,,1.10,Size,Small,,,test-image.png
|
||||
existing-product-id,test-product-product-2,Test product,,test-product-description,draft,,,,,,,,,,Test collection,test-collection2,test-type,123,TRUE,,,Test variant,test-sku-3,test-barcode-3,10,FALSE,TRUE,,,,,,,,,,1.20,Size,Medium,,,test-image.png
|
||||
existing-product-id,test-product-product-2,Test product,,test-product-description,draft,,,,,,,,,,Test collection,test-collection2,test-type,123,TRUE,,existing-variant-id,Test variant changed,test-sku-4,test-barcode-4,10,FALSE,TRUE,,,,,,,,,,,Size,Large,,,test-image.png
|
||||
|
@@ -194,7 +194,7 @@ medusaIntegrationTestRunner({
|
||||
|
||||
await compareCSVs(
|
||||
notifications[0].data.file.url,
|
||||
path.join(__dirname, "__fixtures__", "exported-products.csv")
|
||||
path.join(__dirname, "__fixtures__", "exported-products-comma.csv")
|
||||
)
|
||||
await fs.rm(path.dirname(notifications[0].data.file.url), {
|
||||
force: true,
|
||||
|
||||
@@ -31,6 +31,7 @@ medusaIntegrationTestRunner({
|
||||
let baseCollection
|
||||
let baseType
|
||||
let baseProduct
|
||||
let baseRegion
|
||||
|
||||
let eventBus: IEventBusModuleService
|
||||
beforeAll(async () => {
|
||||
@@ -64,6 +65,17 @@ medusaIntegrationTestRunner({
|
||||
adminHeaders
|
||||
)
|
||||
).data.product
|
||||
|
||||
baseRegion = (
|
||||
await api.post(
|
||||
"/admin/regions",
|
||||
{
|
||||
name: "Test region",
|
||||
currency_code: "USD",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
).data.region
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -71,255 +83,268 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
|
||||
describe("POST /admin/products/export", () => {
|
||||
it("should import a previously exported products CSV file", async () => {
|
||||
const subscriberExecution = TestEventUtils.waitSubscribersExecution(
|
||||
"notification.notification.created",
|
||||
eventBus
|
||||
)
|
||||
// We want to ensure files with different delimiters are supported
|
||||
;[
|
||||
{ file: "exported-products-comma.csv", name: "delimited with comma" },
|
||||
{
|
||||
file: "exported-products-semicolon.csv",
|
||||
name: "delimited with semicolon",
|
||||
},
|
||||
].forEach((testcase) => {
|
||||
it(`should import a previously exported products CSV file ${testcase.name}`, async () => {
|
||||
const subscriberExecution = TestEventUtils.waitSubscribersExecution(
|
||||
"notification.notification.created",
|
||||
eventBus
|
||||
)
|
||||
|
||||
let fileContent = await fs.readFile(
|
||||
path.join(__dirname, "__fixtures__", "exported-products.csv"),
|
||||
{ encoding: "utf-8" }
|
||||
)
|
||||
let fileContent = await fs.readFile(
|
||||
path.join(__dirname, "__fixtures__", testcase.file),
|
||||
{ encoding: "utf-8" }
|
||||
)
|
||||
|
||||
fileContent = fileContent.replace(
|
||||
/prod_01J3CRPNVGRZ01A8GH8FQYK10Z/g,
|
||||
baseProduct.id
|
||||
)
|
||||
fileContent = fileContent.replace(
|
||||
/variant_01J3CRPNW5J6EBVVQP1TN33A58/g,
|
||||
baseProduct.variants[0].id
|
||||
)
|
||||
fileContent = fileContent.replace(/pcol_\w*\d*/g, baseCollection.id)
|
||||
fileContent = fileContent.replace(/ptyp_\w*\d*/g, baseType.id)
|
||||
fileContent = fileContent.replace(
|
||||
/prod_01J3CRPNVGRZ01A8GH8FQYK10Z/g,
|
||||
baseProduct.id
|
||||
)
|
||||
fileContent = fileContent.replace(
|
||||
/variant_01J3CRPNW5J6EBVVQP1TN33A58/g,
|
||||
baseProduct.variants[0].id
|
||||
)
|
||||
fileContent = fileContent.replace(/pcol_\w*\d*/g, baseCollection.id)
|
||||
fileContent = fileContent.replace(/ptyp_\w*\d*/g, baseType.id)
|
||||
|
||||
const { form, meta } = getUploadReq({
|
||||
name: "test.csv",
|
||||
content: fileContent,
|
||||
})
|
||||
|
||||
// BREAKING: The batch endpoints moved to the domain routes (admin/batch-jobs -> /admin/products/import). The payload and response changed as well.
|
||||
const batchJobRes = await api.post("/admin/products/import", form, meta)
|
||||
|
||||
const transactionId = batchJobRes.data.transaction_id
|
||||
expect(transactionId).toBeTruthy()
|
||||
expect(batchJobRes.data.summary).toEqual({
|
||||
toCreate: 1,
|
||||
toUpdate: 1,
|
||||
})
|
||||
|
||||
await api.post(
|
||||
`/admin/products/import/${transactionId}/confirm`,
|
||||
{},
|
||||
meta
|
||||
)
|
||||
|
||||
await subscriberExecution
|
||||
const notifications = (
|
||||
await api.get("/admin/notifications", adminHeaders)
|
||||
).data.notifications
|
||||
|
||||
expect(notifications.length).toBe(1)
|
||||
expect(notifications[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
title: "Product import",
|
||||
description: `Product import of file test.csv completed successfully!`,
|
||||
}),
|
||||
const { form, meta } = getUploadReq({
|
||||
name: "test.csv",
|
||||
content: fileContent,
|
||||
})
|
||||
)
|
||||
|
||||
const dbProducts = (await api.get("/admin/products", adminHeaders)).data
|
||||
.products
|
||||
// BREAKING: The batch endpoints moved to the domain routes (admin/batch-jobs -> /admin/products/import). The payload and response changed as well.
|
||||
const batchJobRes = await api.post(
|
||||
"/admin/products/import",
|
||||
form,
|
||||
meta
|
||||
)
|
||||
|
||||
expect(dbProducts).toHaveLength(2)
|
||||
expect(dbProducts).toEqual(
|
||||
expect.arrayContaining([
|
||||
const transactionId = batchJobRes.data.transaction_id
|
||||
expect(transactionId).toBeTruthy()
|
||||
expect(batchJobRes.data.summary).toEqual({
|
||||
toCreate: 1,
|
||||
toUpdate: 1,
|
||||
})
|
||||
|
||||
await api.post(
|
||||
`/admin/products/import/${transactionId}/confirm`,
|
||||
{},
|
||||
meta
|
||||
)
|
||||
|
||||
await subscriberExecution
|
||||
const notifications = (
|
||||
await api.get("/admin/notifications", adminHeaders)
|
||||
).data.notifications
|
||||
|
||||
expect(notifications.length).toBe(1)
|
||||
expect(notifications[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: baseProduct.id,
|
||||
handle: "base-product",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-image.png",
|
||||
status: "draft",
|
||||
description: "test-product-description\ntest line 2",
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
title: "size",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "color",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
url: "test-image-2.png",
|
||||
}),
|
||||
]),
|
||||
tags: [
|
||||
expect.objectContaining({
|
||||
value: "123",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "456",
|
||||
}),
|
||||
],
|
||||
type: expect.objectContaining({
|
||||
id: baseType.id,
|
||||
data: expect.objectContaining({
|
||||
title: "Product import",
|
||||
description: `Product import of file test.csv completed successfully!`,
|
||||
}),
|
||||
collection: expect.objectContaining({
|
||||
id: baseCollection.id,
|
||||
})
|
||||
)
|
||||
|
||||
const dbProducts = (await api.get("/admin/products", adminHeaders))
|
||||
.data.products
|
||||
|
||||
expect(dbProducts).toHaveLength(2)
|
||||
expect(dbProducts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: baseProduct.id,
|
||||
handle: "base-product",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-image.png",
|
||||
status: "draft",
|
||||
description: "test-product-description\ntest line 2",
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
title: "size",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "color",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
url: "test-image-2.png",
|
||||
}),
|
||||
]),
|
||||
tags: [
|
||||
expect.objectContaining({
|
||||
value: "123",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "456",
|
||||
}),
|
||||
],
|
||||
type: expect.objectContaining({
|
||||
id: baseType.id,
|
||||
}),
|
||||
collection: expect.objectContaining({
|
||||
id: baseCollection.id,
|
||||
}),
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Test variant 2",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 200,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 65,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 50,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
handle: "proposed-product",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-image.png",
|
||||
status: "proposed",
|
||||
description: "test-product-description",
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
title: "size",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "color",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
url: "test-image-2.png",
|
||||
}),
|
||||
]),
|
||||
tags: [
|
||||
expect.objectContaining({
|
||||
value: "new-tag",
|
||||
}),
|
||||
],
|
||||
type: expect.objectContaining({
|
||||
id: baseType.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Test variant 2",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 200,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 65,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 50,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
handle: "proposed-product",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-image.png",
|
||||
status: "proposed",
|
||||
description: "test-product-description",
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
title: "size",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "color",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
url: "test-image-2.png",
|
||||
}),
|
||||
]),
|
||||
tags: [
|
||||
expect.objectContaining({
|
||||
value: "new-tag",
|
||||
}),
|
||||
],
|
||||
type: expect.objectContaining({
|
||||
id: baseType.id,
|
||||
collection: null,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
collection: null,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
])
|
||||
)
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("should fail on invalid region in prices being present in the CSV", async () => {
|
||||
@@ -337,7 +362,7 @@ medusaIntegrationTestRunner({
|
||||
.post("/admin/products/import", form, meta)
|
||||
.catch((e) => e)
|
||||
expect(err.response.data.message).toEqual(
|
||||
"Region with ID reg_nonexistent not found"
|
||||
"Region with name nonexistent not found"
|
||||
)
|
||||
})
|
||||
|
||||
@@ -439,6 +464,251 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("supports importing the v1 template", async () => {
|
||||
const subscriberExecution = TestEventUtils.waitSubscribersExecution(
|
||||
"notification.notification.created",
|
||||
eventBus
|
||||
)
|
||||
|
||||
let fileContent = await fs.readFile(
|
||||
path.join(__dirname, "__fixtures__", "v1-template.csv"),
|
||||
{ encoding: "utf-8" }
|
||||
)
|
||||
|
||||
fileContent = fileContent.replace(
|
||||
/existing-product-id/g,
|
||||
baseProduct.id
|
||||
)
|
||||
fileContent = fileContent.replace(
|
||||
/existing-variant-id/g,
|
||||
baseProduct.variants[0].id
|
||||
)
|
||||
fileContent = fileContent.replace(/test-type/g, baseType.value)
|
||||
fileContent = fileContent.replace(
|
||||
/test-collection1/g,
|
||||
baseCollection.handle
|
||||
)
|
||||
fileContent = fileContent.replace(
|
||||
/test-collection2/g,
|
||||
baseCollection.handle
|
||||
)
|
||||
|
||||
const { form, meta } = getUploadReq({
|
||||
name: "test.csv",
|
||||
content: fileContent,
|
||||
})
|
||||
|
||||
const batchJobRes = await api.post("/admin/products/import", form, meta)
|
||||
const transactionId = batchJobRes.data.transaction_id
|
||||
expect(transactionId).toBeTruthy()
|
||||
expect(batchJobRes.data.summary).toEqual({
|
||||
toCreate: 2,
|
||||
toUpdate: 1,
|
||||
})
|
||||
|
||||
await api.post(
|
||||
`/admin/products/import/${transactionId}/confirm`,
|
||||
{},
|
||||
meta
|
||||
)
|
||||
|
||||
await subscriberExecution
|
||||
const dbProducts = (await api.get("/admin/products", adminHeaders)).data
|
||||
.products
|
||||
|
||||
expect(dbProducts).toHaveLength(3)
|
||||
expect(dbProducts).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: baseProduct.id,
|
||||
handle: "test-product-product-2",
|
||||
title: "Test product",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-image.png",
|
||||
status: "draft",
|
||||
description: "test-product-description",
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
title: "Size",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "Small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "Medium",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "Large",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
}),
|
||||
]),
|
||||
tags: [
|
||||
expect.objectContaining({
|
||||
value: "123",
|
||||
}),
|
||||
],
|
||||
type: expect.objectContaining({
|
||||
id: baseType.id,
|
||||
}),
|
||||
collection: expect.objectContaining({
|
||||
id: baseCollection.id,
|
||||
}),
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
sku: "test-sku-2",
|
||||
barcode: "test-barcode-2",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 1.1,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "Small",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
sku: "test-sku-3",
|
||||
barcode: "test-barcode-3",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 1.2,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "Medium",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: baseProduct.variants[0].id,
|
||||
title: "Test variant changed",
|
||||
sku: "test-sku-4",
|
||||
barcode: "test-barcode-4",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "Large",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: "Test product",
|
||||
handle: "test-product-product-1-1",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-image.png",
|
||||
status: "draft",
|
||||
description:
|
||||
"Hopper Stripes Bedding, available as duvet cover, pillow sham and sheet.\\n100% organic cotton, soft and crisp to the touch. Made in Portugal.",
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
title: "test-option-1",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "option 1 value red",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "test-option-2",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "option 2 value 1",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
}),
|
||||
]),
|
||||
tags: [],
|
||||
type: null,
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
sku: "test-sku-1-1",
|
||||
barcode: "test-barcode-1-1",
|
||||
allow_backorder: true,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
rules: {
|
||||
region_id: baseRegion.id,
|
||||
},
|
||||
amount: 1,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 1.1,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "option 1 value red",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "option 2 value 1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: "Test product",
|
||||
handle: "test-product-product-1",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail when a v1 import has non existent collection", async () => {
|
||||
let fileContent = await fs.readFile(
|
||||
path.join(__dirname, "__fixtures__", "v1-template.csv"),
|
||||
{ encoding: "utf-8" }
|
||||
)
|
||||
|
||||
const { form, meta } = getUploadReq({
|
||||
name: "test.csv",
|
||||
content: fileContent,
|
||||
})
|
||||
|
||||
const err = await api
|
||||
.post("/admin/products/import", form, meta)
|
||||
.catch((e) => e)
|
||||
expect(err.response.data.message).toEqual(
|
||||
"Product collection with handle 'test-collection1' does not exist"
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user