feat: define validators and use normalize-products step (#12473)
Fixes: FRMW-2965 In this PR we replace/remove the existing step to normalize a CSV file with the newly written CSV normalizer and also we validate the file contents further using a Zod schema. I have duplicated the schema for now. But it is makes sense to re-use the schema for CSV validating and `/admin/products/batch`, then I can keep one source of truth under utils and re-export it. WDYT? **Screenshots of some errors after validating the file strictly**  
This commit is contained in:
+2
-2
@@ -1,5 +1,5 @@
|
||||
Product Id,Product Handle,Product Title,Product Status,Product Description,Product Subtitle,Product External Id,Product Thumbnail,Product Collection Id,Product Type Id,Product Category 1,Product Created At,Product Deleted At,Product Discountable,Product Height,Product Hs Code,Product Image 1,Product Image 2,Product Is Giftcard,Product Length,Product Material,Product Mid Code,Product Origin Country,Product Tag 1,Product Tag 2,Product Updated At,Product Weight,Product Width,Variant Id,Variant Title,Variant Sku,Variant Upc,Variant Ean,Variant Hs Code,Variant Mid Code,Variant Manage Inventory,Variant Allow Backorder,Variant Barcode,Variant Created At,Variant Deleted At,Variant Height,Variant Length,Variant Material,Variant Metadata,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value,Variant Origin Country,Variant Price DKK,Variant Price EUR,Variant Price USD,Variant Product Id,Variant Updated At,Variant Variant Rank,Variant Weight,Variant Width,Shipping Profile Id
|
||||
prod_01J44RRKH4HH2SANJ0S05YM853,base-product,Base product,draft,"test-product-description
|
||||
test line 2",,,test-image.png,pcol_01J44RRKG4P1RZ3CKAMBS760TD,ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1,pcat_01J44RRKGS2N86A8V37ZJD877K,2024-07-31T16:07:55.681Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.681Z,,,variant_01J44RRKHRQ7WJ902X4936GEK7,Test variant,,,,,,true,false,,2024-07-31T16:07:55.704Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRKH4HH2SANJ0S05YM853,2024-07-31T16:07:55.704Z,0,,,import-shipping-profile
|
||||
test line 2",,,test-image.png,pcol_01J44RRKG4P1RZ3CKAMBS760TD,ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1,pcat_01J44RRKGS2N86A8V37ZJD877K,2024-07-31T16:07:55.681Z,,true,,,test-image.png,test-image-2.png,false,,,,,tag-123,tag-456,2024-07-31T16:07:55.681Z,,,variant_01J44RRKHRQ7WJ902X4936GEK7,Test variant,,,,,,true,false,,2024-07-31T16:07:55.704Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRKH4HH2SANJ0S05YM853,2024-07-31T16:07:55.704Z,0,,,import-shipping-profile
|
||||
prod_01J44RRKH4HH2SANJ0S05YM853,base-product,Base product,draft,"test-product-description
|
||||
test line 2",,,test-image.png,pcol_01J44RRKG4P1RZ3CKAMBS760TD,ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1,pcat_01J44RRKGS2N86A8V37ZJD877K,2024-07-31T16:07:55.681Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.681Z,,,variant_01J44RRKHRAYY7Q7NTXNNMDA1S,Test variant 2,,,,,,true,false,,2024-07-31T16:07:55.704Z,,,,,,size,small,color,green,,50,65,200,prod_01J44RRKH4HH2SANJ0S05YM853,2024-07-31T16:07:55.704Z,0,,,import-shipping-profile
|
||||
test line 2",,,test-image.png,pcol_01J44RRKG4P1RZ3CKAMBS760TD,ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1,pcat_01J44RRKGS2N86A8V37ZJD877K,2024-07-31T16:07:55.681Z,,true,,,test-image.png,test-image-2.png,false,,,,,tag-123,tag-456,2024-07-31T16:07:55.681Z,,,variant_01J44RRKHRAYY7Q7NTXNNMDA1S,Test variant 2,,,,,,true,false,,2024-07-31T16:07:55.704Z,,,,,,size,small,color,green,,50,65,200,prod_01J44RRKH4HH2SANJ0S05YM853,2024-07-31T16:07:55.704Z,0,,,import-shipping-profile
|
||||
|
+3
-3
@@ -1,6 +1,6 @@
|
||||
Product Id,Product Handle,Product Title,Product Status,Product Description,Product Subtitle,Product External Id,Product Thumbnail,Product Collection Id,Product Type Id,Product Created At,Product Deleted At,Product Discountable,Product Height,Product Hs Code,Product Image 1,Product Image 2,Product Is Giftcard,Product Length,Product Material,Product Mid Code,Product Origin Country,Product Tag 1,Product Tag 2,Product Updated At,Product Weight,Product Width,Variant Id,Variant Title,Variant Sku,Variant Upc,Variant Ean,Variant Hs Code,Variant Mid Code,Variant Manage Inventory,Variant Allow Backorder,Variant Barcode,Variant Created At,Variant Deleted At,Variant Height,Variant Length,Variant Material,Variant Metadata,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value,Variant Origin Country,Variant Price DKK,Variant Price EUR,Variant Price USD,Variant Product Id,Variant Updated At,Variant Variant Rank,Variant Weight,Variant Width,Shipping Profile Id
|
||||
prod_01J44RRJZ3M5F63NY82434RNM5,base-product,Base product,draft,"test-product-description
|
||||
test line 2",,,test-image.png,pcol_01J44RRJXM6AM3YS5PMJDMH3YF,ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD,2024-07-31T16:07:55.102Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.102Z,,,variant_01J44RRJZW1T9KQB6XG7Q6K61F,Test variant,,,,,,true,false,,2024-07-31T16:07:55.133Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRJZ3M5F63NY82434RNM5,2024-07-31T16:07:55.133Z,0,,,import-shipping-profile
|
||||
test line 2",,,test-image.png,pcol_01J44RRJXM6AM3YS5PMJDMH3YF,ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD,2024-07-31T16:07:55.102Z,,true,,,test-image.png,test-image-2.png,false,,,,,tag-123,tag-456,2024-07-31T16:07:55.102Z,,,variant_01J44RRJZW1T9KQB6XG7Q6K61F,Test variant,,,,,,true,false,,2024-07-31T16:07:55.133Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRJZ3M5F63NY82434RNM5,2024-07-31T16:07:55.133Z,0,,,import-shipping-profile
|
||||
prod_01J44RRJZ3M5F63NY82434RNM5,base-product,Base product,draft,"test-product-description
|
||||
test line 2",,,test-image.png,pcol_01J44RRJXM6AM3YS5PMJDMH3YF,ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD,2024-07-31T16:07:55.102Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.102Z,,,variant_01J44RRJZW5GNQKT1FEDACEESW,Test variant 2,,,,,,true,false,,2024-07-31T16:07:55.133Z,,,,,,size,small,color,green,,50,65,200,prod_01J44RRJZ3M5F63NY82434RNM5,2024-07-31T16:07:55.133Z,0,,,import-shipping-profile
|
||||
prod_01J44RRK2GJJVMQQXT67TJCV08,proposed-product,Proposed product,proposed,test-product-description,,,test-image.png,,ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD,2024-07-31T16:07:55.213Z,,true,,,test-image.png,test-image-2.png,false,,,,,new-tag,,2024-07-31T16:07:55.213Z,,,variant_01J44RRK2WYHH0RDEK8BBGP7CY,Test variant,,,,,,true,false,,2024-07-31T16:07:55.228Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRK2GJJVMQQXT67TJCV08,2024-07-31T16:07:55.228Z,0,,,import-shipping-profile
|
||||
test line 2",,,test-image.png,pcol_01J44RRJXM6AM3YS5PMJDMH3YF,ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD,2024-07-31T16:07:55.102Z,,true,,,test-image.png,test-image-2.png,false,,,,,tag-123,tag-456,2024-07-31T16:07:55.102Z,,,variant_01J44RRJZW5GNQKT1FEDACEESW,Test variant 2,,,,,,true,false,,2024-07-31T16:07:55.133Z,,,,,,size,small,color,green,,50,65,200,prod_01J44RRJZ3M5F63NY82434RNM5,2024-07-31T16:07:55.133Z,0,,,import-shipping-profile
|
||||
,proposed-product,Proposed product,proposed,test-product-description,,,test-image.png,,ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD,2024-07-31T16:07:55.213Z,,true,,,test-image.png,test-image-2.png,false,,,,,new-tag,,2024-07-31T16:07:55.213Z,,,,Test variant,,,,,,true,false,,2024-07-31T16:07:55.228Z,,,,,,size,large,color,green,,30,45,100,,2024-07-31T16:07:55.228Z,0,,,import-shipping-profile
|
||||
|
+3
-3
@@ -1,6 +1,6 @@
|
||||
Product Id;Product Handle;Product Title;Product Status;Product Description;Product Subtitle;Product External Id;Product Thumbnail;Product Collection Id;Product Type Id;Product Created At;Product Deleted At;Product Discountable;Product Height;Product Hs Code;Product Image 1;Product Image 2;Product Is Giftcard;Product Length;Product Material;Product Mid Code;Product Origin Country;Product Tag 1;Product Tag 2;Product Updated At;Product Weight;Product Width;Variant Id;Variant Title;Variant Sku;Variant Upc;Variant Ean;Variant Hs Code;Variant Mid Code;Variant Manage Inventory;Variant Allow Backorder;Variant Barcode;Variant Created At;Variant Deleted At;Variant Height;Variant Length;Variant Material;Variant Metadata;Variant Option 1 Name;Variant Option 1 Value;Variant Option 2 Name;Variant Option 2 Value;Variant Origin Country;Variant Price DKK;Variant Price EUR;Variant Price USD;Variant Product Id;Variant Updated At;Variant Variant Rank;Variant Weight;Variant Width;Shipping Profile Id
|
||||
prod_01J44RRJZ3M5F63NY82434RNM5;base-product;Base product;draft;"test-product-description
|
||||
test line 2";;;test-image.png;pcol_01J44RRJXM6AM3YS5PMJDMH3YF;ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD;2024-07-31T16:07:55.102Z;;true;;;test-image.png;test-image-2.png;false;;;;;123;456;2024-07-31T16:07:55.102Z;;;variant_01J44RRJZW1T9KQB6XG7Q6K61F;Test variant;;;;;;true;false;;2024-07-31T16:07:55.133Z;;;;;;size;large;color;green;;30;45;100;prod_01J44RRJZ3M5F63NY82434RNM5;2024-07-31T16:07:55.133Z;0;;;import-shipping-profile
|
||||
test line 2";;;test-image.png;pcol_01J44RRJXM6AM3YS5PMJDMH3YF;ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD;2024-07-31T16:07:55.102Z;;true;;;test-image.png;test-image-2.png;false;;;;;tag-123;tag-456;2024-07-31T16:07:55.102Z;;;variant_01J44RRJZW1T9KQB6XG7Q6K61F;Test variant;;;;;;true;false;;2024-07-31T16:07:55.133Z;;;;;;size;large;color;green;;30;45;100;prod_01J44RRJZ3M5F63NY82434RNM5;2024-07-31T16:07:55.133Z;0;;;import-shipping-profile
|
||||
prod_01J44RRJZ3M5F63NY82434RNM5;base-product;Base product;draft;"test-product-description
|
||||
test line 2";;;test-image.png;pcol_01J44RRJXM6AM3YS5PMJDMH3YF;ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD;2024-07-31T16:07:55.102Z;;true;;;test-image.png;test-image-2.png;false;;;;;123;456;2024-07-31T16:07:55.102Z;;;variant_01J44RRJZW5GNQKT1FEDACEESW;Test variant 2;;;;;;true;false;;2024-07-31T16:07:55.133Z;;;;;;size;small;color;green;;50;65;200;prod_01J44RRJZ3M5F63NY82434RNM5;2024-07-31T16:07:55.133Z;0;;;import-shipping-profile
|
||||
prod_01J44RRK2GJJVMQQXT67TJCV08;proposed-product;Proposed product;proposed;test-product-description;;;test-image.png;;ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD;2024-07-31T16:07:55.213Z;;true;;;test-image.png;test-image-2.png;false;;;;;new-tag;;2024-07-31T16:07:55.213Z;;;variant_01J44RRK2WYHH0RDEK8BBGP7CY;Test variant;;;;;;true;false;;2024-07-31T16:07:55.228Z;;;;;;size;large;color;green;;30;45;100;prod_01J44RRK2GJJVMQQXT67TJCV08;2024-07-31T16:07:55.228Z;0;;;import-shipping-profile
|
||||
test line 2";;;test-image.png;pcol_01J44RRJXM6AM3YS5PMJDMH3YF;ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD;2024-07-31T16:07:55.102Z;;true;;;test-image.png;test-image-2.png;false;;;;;tag-123;tag-456;2024-07-31T16:07:55.102Z;;;variant_01J44RRJZW5GNQKT1FEDACEESW;Test variant 2;;;;;;true;false;;2024-07-31T16:07:55.133Z;;;;;;size;small;color;green;;50;65;200;prod_01J44RRJZ3M5F63NY82434RNM5;2024-07-31T16:07:55.133Z;0;;;import-shipping-profile
|
||||
;proposed-product;Proposed product;proposed;test-product-description;;;test-image.png;;ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD;2024-07-31T16:07:55.213Z;;true;;;test-image.png;test-image-2.png;false;;;;;new-tag;;2024-07-31T16:07:55.213Z;;;;Test variant;;;;;;true;false;;2024-07-31T16:07:55.228Z;;;;;;size;large;color;green;;30;45;100;;2024-07-31T16:07:55.228Z;0;;;import-shipping-profile
|
||||
|
+431
@@ -0,0 +1,431 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[` POST /admin/products/export should export a csv file containing the expected products 1`] = `
|
||||
[
|
||||
{
|
||||
"Product Collection Id": "<ID>",
|
||||
"Product Created At": "<DateTime>",
|
||||
"Product Deleted At": "<DateTime>",
|
||||
"Product Description": "test-product-description
|
||||
test line 2",
|
||||
"Product Discountable": true,
|
||||
"Product External Id": "<ID>",
|
||||
"Product Handle": "base-product",
|
||||
"Product Height": "",
|
||||
"Product Hs Code": "",
|
||||
"Product Id": "<ID>",
|
||||
"Product Image 1": "test-image.png",
|
||||
"Product Image 2": "test-image-2.png",
|
||||
"Product Is Giftcard": false,
|
||||
"Product Length": "",
|
||||
"Product Material": "",
|
||||
"Product Mid Code": "",
|
||||
"Product Origin Country": "",
|
||||
"Product Status": "draft",
|
||||
"Product Subtitle": "",
|
||||
"Product Tag 1": "tag-123",
|
||||
"Product Tag 2": "tag-456",
|
||||
"Product Thumbnail": "test-image.png",
|
||||
"Product Title": "Base product",
|
||||
"Product Type Id": "<ID>",
|
||||
"Product Updated At": "<DateTime>",
|
||||
"Product Weight": "",
|
||||
"Product Width": "",
|
||||
"Variant Allow Backorder": false,
|
||||
"Variant Barcode": "",
|
||||
"Variant Created At": "<DateTime>",
|
||||
"Variant Deleted At": "<DateTime>",
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Id": "<ID>",
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "large",
|
||||
"Variant Option 2 Name": "color",
|
||||
"Variant Option 2 Value": "green",
|
||||
"Variant Origin Country": "",
|
||||
"Variant Price DKK": 30,
|
||||
"Variant Price EUR": 45,
|
||||
"Variant Price USD": 100,
|
||||
"Variant Product Id": "<ID>",
|
||||
"Variant Sku": "",
|
||||
"Variant Title": "Test variant",
|
||||
"Variant Upc": "",
|
||||
"Variant Updated At": "<DateTime>",
|
||||
"Variant Variant Rank": 0,
|
||||
"Variant Weight": "",
|
||||
"Variant Width": "",
|
||||
},
|
||||
{
|
||||
"Product Collection Id": "<ID>",
|
||||
"Product Created At": "<DateTime>",
|
||||
"Product Deleted At": "<DateTime>",
|
||||
"Product Description": "test-product-description
|
||||
test line 2",
|
||||
"Product Discountable": true,
|
||||
"Product External Id": "<ID>",
|
||||
"Product Handle": "base-product",
|
||||
"Product Height": "",
|
||||
"Product Hs Code": "",
|
||||
"Product Id": "<ID>",
|
||||
"Product Image 1": "test-image.png",
|
||||
"Product Image 2": "test-image-2.png",
|
||||
"Product Is Giftcard": false,
|
||||
"Product Length": "",
|
||||
"Product Material": "",
|
||||
"Product Mid Code": "",
|
||||
"Product Origin Country": "",
|
||||
"Product Status": "draft",
|
||||
"Product Subtitle": "",
|
||||
"Product Tag 1": "tag-123",
|
||||
"Product Tag 2": "tag-456",
|
||||
"Product Thumbnail": "test-image.png",
|
||||
"Product Title": "Base product",
|
||||
"Product Type Id": "<ID>",
|
||||
"Product Updated At": "<DateTime>",
|
||||
"Product Weight": "",
|
||||
"Product Width": "",
|
||||
"Variant Allow Backorder": false,
|
||||
"Variant Barcode": "",
|
||||
"Variant Created At": "<DateTime>",
|
||||
"Variant Deleted At": "<DateTime>",
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Id": "<ID>",
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "small",
|
||||
"Variant Option 2 Name": "color",
|
||||
"Variant Option 2 Value": "green",
|
||||
"Variant Origin Country": "",
|
||||
"Variant Price DKK": 50,
|
||||
"Variant Price EUR": 65,
|
||||
"Variant Price USD": 200,
|
||||
"Variant Product Id": "<ID>",
|
||||
"Variant Sku": "",
|
||||
"Variant Title": "Test variant 2",
|
||||
"Variant Upc": "",
|
||||
"Variant Updated At": "<DateTime>",
|
||||
"Variant Variant Rank": 0,
|
||||
"Variant Weight": "",
|
||||
"Variant Width": "",
|
||||
},
|
||||
{
|
||||
"Product Collection Id": "<ID>",
|
||||
"Product Created At": "<DateTime>",
|
||||
"Product Deleted At": "<DateTime>",
|
||||
"Product Description": "test-product-description",
|
||||
"Product Discountable": true,
|
||||
"Product External Id": "<ID>",
|
||||
"Product Handle": "proposed-product",
|
||||
"Product Height": "",
|
||||
"Product Hs Code": "",
|
||||
"Product Id": "<ID>",
|
||||
"Product Image 1": "test-image.png",
|
||||
"Product Image 2": "test-image-2.png",
|
||||
"Product Is Giftcard": false,
|
||||
"Product Length": "",
|
||||
"Product Material": "",
|
||||
"Product Mid Code": "",
|
||||
"Product Origin Country": "",
|
||||
"Product Status": "proposed",
|
||||
"Product Subtitle": "",
|
||||
"Product Tag 1": "new-tag",
|
||||
"Product Tag 2": "",
|
||||
"Product Thumbnail": "test-image.png",
|
||||
"Product Title": "Proposed product",
|
||||
"Product Type Id": "<ID>",
|
||||
"Product Updated At": "<DateTime>",
|
||||
"Product Weight": "",
|
||||
"Product Width": "",
|
||||
"Variant Allow Backorder": false,
|
||||
"Variant Barcode": "",
|
||||
"Variant Created At": "<DateTime>",
|
||||
"Variant Deleted At": "<DateTime>",
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Id": "<ID>",
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "large",
|
||||
"Variant Option 2 Name": "color",
|
||||
"Variant Option 2 Value": "green",
|
||||
"Variant Origin Country": "",
|
||||
"Variant Price DKK": 30,
|
||||
"Variant Price EUR": 45,
|
||||
"Variant Price USD": 100,
|
||||
"Variant Product Id": "<ID>",
|
||||
"Variant Sku": "",
|
||||
"Variant Title": "Test variant",
|
||||
"Variant Upc": "",
|
||||
"Variant Updated At": "<DateTime>",
|
||||
"Variant Variant Rank": 0,
|
||||
"Variant Weight": "",
|
||||
"Variant Width": "",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[` POST /admin/products/export should export a csv file filtered by specific products 1`] = `
|
||||
[
|
||||
{
|
||||
"Product Collection Id": "<ID>",
|
||||
"Product Created At": "<DateTime>",
|
||||
"Product Deleted At": "<DateTime>",
|
||||
"Product Description": "test-product-description",
|
||||
"Product Discountable": true,
|
||||
"Product External Id": "<ID>",
|
||||
"Product Handle": "proposed-product",
|
||||
"Product Height": "",
|
||||
"Product Hs Code": "",
|
||||
"Product Id": "<ID>",
|
||||
"Product Image 1": "test-image.png",
|
||||
"Product Image 2": "test-image-2.png",
|
||||
"Product Is Giftcard": false,
|
||||
"Product Length": "",
|
||||
"Product Material": "",
|
||||
"Product Mid Code": "",
|
||||
"Product Origin Country": "",
|
||||
"Product Status": "proposed",
|
||||
"Product Subtitle": "",
|
||||
"Product Tag 1": "new-tag",
|
||||
"Product Thumbnail": "test-image.png",
|
||||
"Product Title": "Proposed product",
|
||||
"Product Type Id": "<ID>",
|
||||
"Product Updated At": "<DateTime>",
|
||||
"Product Weight": "",
|
||||
"Product Width": "",
|
||||
"Variant Allow Backorder": false,
|
||||
"Variant Barcode": "",
|
||||
"Variant Created At": "<DateTime>",
|
||||
"Variant Deleted At": "<DateTime>",
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Id": "<ID>",
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "large",
|
||||
"Variant Option 2 Name": "color",
|
||||
"Variant Option 2 Value": "green",
|
||||
"Variant Origin Country": "",
|
||||
"Variant Price DKK": 30,
|
||||
"Variant Price EUR": 45,
|
||||
"Variant Price USD": 100,
|
||||
"Variant Product Id": "<ID>",
|
||||
"Variant Sku": "",
|
||||
"Variant Title": "Test variant",
|
||||
"Variant Upc": "",
|
||||
"Variant Updated At": "<DateTime>",
|
||||
"Variant Variant Rank": 0,
|
||||
"Variant Weight": "",
|
||||
"Variant Width": "",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[` POST /admin/products/export should export a csv file with categories 1`] = `
|
||||
[
|
||||
{
|
||||
"Product Category 1": "<ID>",
|
||||
"Product Collection Id": "<ID>",
|
||||
"Product Created At": "<DateTime>",
|
||||
"Product Deleted At": "<DateTime>",
|
||||
"Product Description": "test-product-description
|
||||
test line 2",
|
||||
"Product Discountable": true,
|
||||
"Product External Id": "<ID>",
|
||||
"Product Handle": "base-product",
|
||||
"Product Height": "",
|
||||
"Product Hs Code": "",
|
||||
"Product Id": "<ID>",
|
||||
"Product Image 1": "test-image.png",
|
||||
"Product Image 2": "test-image-2.png",
|
||||
"Product Is Giftcard": false,
|
||||
"Product Length": "",
|
||||
"Product Material": "",
|
||||
"Product Mid Code": "",
|
||||
"Product Origin Country": "",
|
||||
"Product Status": "draft",
|
||||
"Product Subtitle": "",
|
||||
"Product Tag 1": "tag-123",
|
||||
"Product Tag 2": "tag-456",
|
||||
"Product Thumbnail": "test-image.png",
|
||||
"Product Title": "Base product",
|
||||
"Product Type Id": "<ID>",
|
||||
"Product Updated At": "<DateTime>",
|
||||
"Product Weight": "",
|
||||
"Product Width": "",
|
||||
"Variant Allow Backorder": false,
|
||||
"Variant Barcode": "",
|
||||
"Variant Created At": "<DateTime>",
|
||||
"Variant Deleted At": "<DateTime>",
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Id": "<ID>",
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "large",
|
||||
"Variant Option 2 Name": "color",
|
||||
"Variant Option 2 Value": "green",
|
||||
"Variant Origin Country": "",
|
||||
"Variant Price DKK": 30,
|
||||
"Variant Price EUR": 45,
|
||||
"Variant Price USD": 100,
|
||||
"Variant Product Id": "<ID>",
|
||||
"Variant Sku": "",
|
||||
"Variant Title": "Test variant",
|
||||
"Variant Upc": "",
|
||||
"Variant Updated At": "<DateTime>",
|
||||
"Variant Variant Rank": 0,
|
||||
"Variant Weight": "",
|
||||
"Variant Width": "",
|
||||
},
|
||||
{
|
||||
"Product Category 1": "<ID>",
|
||||
"Product Collection Id": "<ID>",
|
||||
"Product Created At": "<DateTime>",
|
||||
"Product Deleted At": "<DateTime>",
|
||||
"Product Description": "test-product-description
|
||||
test line 2",
|
||||
"Product Discountable": true,
|
||||
"Product External Id": "<ID>",
|
||||
"Product Handle": "base-product",
|
||||
"Product Height": "",
|
||||
"Product Hs Code": "",
|
||||
"Product Id": "<ID>",
|
||||
"Product Image 1": "test-image.png",
|
||||
"Product Image 2": "test-image-2.png",
|
||||
"Product Is Giftcard": false,
|
||||
"Product Length": "",
|
||||
"Product Material": "",
|
||||
"Product Mid Code": "",
|
||||
"Product Origin Country": "",
|
||||
"Product Status": "draft",
|
||||
"Product Subtitle": "",
|
||||
"Product Tag 1": "tag-123",
|
||||
"Product Tag 2": "tag-456",
|
||||
"Product Thumbnail": "test-image.png",
|
||||
"Product Title": "Base product",
|
||||
"Product Type Id": "<ID>",
|
||||
"Product Updated At": "<DateTime>",
|
||||
"Product Weight": "",
|
||||
"Product Width": "",
|
||||
"Variant Allow Backorder": false,
|
||||
"Variant Barcode": "",
|
||||
"Variant Created At": "<DateTime>",
|
||||
"Variant Deleted At": "<DateTime>",
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Id": "<ID>",
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "small",
|
||||
"Variant Option 2 Name": "color",
|
||||
"Variant Option 2 Value": "green",
|
||||
"Variant Origin Country": "",
|
||||
"Variant Price DKK": 50,
|
||||
"Variant Price EUR": 65,
|
||||
"Variant Price USD": 200,
|
||||
"Variant Product Id": "<ID>",
|
||||
"Variant Sku": "",
|
||||
"Variant Title": "Test variant 2",
|
||||
"Variant Upc": "",
|
||||
"Variant Updated At": "<DateTime>",
|
||||
"Variant Variant Rank": 0,
|
||||
"Variant Weight": "",
|
||||
"Variant Width": "",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[` POST /admin/products/export should export a csv file with region prices 1`] = `
|
||||
[
|
||||
{
|
||||
"Product Collection Id": "<ID>",
|
||||
"Product Created At": "<DateTime>",
|
||||
"Product Deleted At": "<DateTime>",
|
||||
"Product Description": "test-product-description",
|
||||
"Product Discountable": true,
|
||||
"Product External Id": "<ID>",
|
||||
"Product Handle": "product-with-prices",
|
||||
"Product Height": "",
|
||||
"Product Hs Code": "",
|
||||
"Product Id": "<ID>",
|
||||
"Product Image 1": "test-image.png",
|
||||
"Product Image 2": "test-image-2.png",
|
||||
"Product Is Giftcard": false,
|
||||
"Product Length": "",
|
||||
"Product Material": "",
|
||||
"Product Mid Code": "",
|
||||
"Product Origin Country": "",
|
||||
"Product Status": "draft",
|
||||
"Product Subtitle": "",
|
||||
"Product Tag 1": "tag-123",
|
||||
"Product Tag 2": "tag-456",
|
||||
"Product Thumbnail": "test-image.png",
|
||||
"Product Title": "Product with prices",
|
||||
"Product Type Id": "<ID>",
|
||||
"Product Updated At": "<DateTime>",
|
||||
"Product Weight": "",
|
||||
"Product Width": "",
|
||||
"Variant Allow Backorder": false,
|
||||
"Variant Barcode": "",
|
||||
"Variant Created At": "<DateTime>",
|
||||
"Variant Deleted At": "<DateTime>",
|
||||
"Variant Ean": "",
|
||||
"Variant Height": "",
|
||||
"Variant Hs Code": "",
|
||||
"Variant Id": "<ID>",
|
||||
"Variant Length": "",
|
||||
"Variant Manage Inventory": true,
|
||||
"Variant Material": "",
|
||||
"Variant Metadata": "",
|
||||
"Variant Mid Code": "",
|
||||
"Variant Option 1 Name": "size",
|
||||
"Variant Option 1 Value": "large",
|
||||
"Variant Option 2 Name": "color",
|
||||
"Variant Option 2 Value": "green",
|
||||
"Variant Origin Country": "",
|
||||
"Variant Price Test Region [USD]": 45,
|
||||
"Variant Price USD": 100,
|
||||
"Variant Product Id": "<ID>",
|
||||
"Variant Sku": "",
|
||||
"Variant Title": "Test variant",
|
||||
"Variant Upc": "",
|
||||
"Variant Updated At": "<DateTime>",
|
||||
"Variant Variant Rank": 0,
|
||||
"Variant Weight": "",
|
||||
"Variant Width": "",
|
||||
},
|
||||
]
|
||||
`;
|
||||
@@ -11,38 +11,103 @@ import {
|
||||
createAdminUser,
|
||||
} from "../../../../helpers/create-admin-user"
|
||||
import { getProductFixture } from "../../../../helpers/fixtures"
|
||||
import { csv2json } from "json-2-csv"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const compareCSVs = async (filePath, expectedFilePath) => {
|
||||
const EXPORTED_COLUMNS = [
|
||||
"Product Collection Id",
|
||||
"Product Created At",
|
||||
"Product Deleted At",
|
||||
"Product Description",
|
||||
"Product Discountable",
|
||||
"Product External Id",
|
||||
"Product Handle",
|
||||
"Product Height",
|
||||
"Product Hs Code",
|
||||
"Product Id",
|
||||
"Product Image *",
|
||||
"Product Is Giftcard",
|
||||
"Product Length",
|
||||
"Product Material",
|
||||
"Product Mid Code",
|
||||
"Product Origin Country",
|
||||
"Product Status",
|
||||
"Product Subtitle",
|
||||
"Product Tag *",
|
||||
"Product Thumbnail",
|
||||
"Product Title",
|
||||
"Product Type Id",
|
||||
"Product Updated At",
|
||||
"Product Weight",
|
||||
"Product Width",
|
||||
"Variant Allow Backorder",
|
||||
"Variant Barcode",
|
||||
"Variant Created At",
|
||||
"Variant Deleted At",
|
||||
"Variant Ean",
|
||||
"Variant Height",
|
||||
"Variant Hs Code",
|
||||
"Variant Id",
|
||||
"Variant Length",
|
||||
"Variant Manage Inventory",
|
||||
"Variant Material",
|
||||
"Variant Metadata",
|
||||
"Variant Mid Code",
|
||||
"Variant Option * Name",
|
||||
"Variant Option * Value",
|
||||
"Variant Origin Country",
|
||||
"Variant Price [ISO]",
|
||||
"Variant Product Id",
|
||||
"Variant Sku",
|
||||
"Variant Title",
|
||||
"Variant Upc",
|
||||
"Variant Updated At",
|
||||
"Variant Variant Rank",
|
||||
"Variant Weight",
|
||||
"Variant Width",
|
||||
]
|
||||
|
||||
const getCSVContents = async (filePath: string) => {
|
||||
const asLocalPath = filePath.replace("http://localhost:9000", process.cwd())
|
||||
let fileContent = await fs.readFile(asLocalPath, { encoding: "utf-8" })
|
||||
let fixturesContent = await fs.readFile(expectedFilePath, {
|
||||
encoding: "utf-8",
|
||||
})
|
||||
const fileContent = await fs.readFile(asLocalPath, { encoding: "utf-8" })
|
||||
await fs.rm(path.dirname(asLocalPath), { recursive: true, force: true })
|
||||
const csvRows = csv2json(fileContent)
|
||||
|
||||
// Normalize csv data to get rid of dynamic data
|
||||
const idsToReplace = ["prod_", "pcol_", "variant_", "ptyp_", "pcat_"]
|
||||
const dateRegex =
|
||||
/(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})\.(\d{3})Z/g
|
||||
idsToReplace.forEach((prefix) => {
|
||||
fileContent = fileContent.replace(
|
||||
new RegExp(`${prefix}\\w*\\d*`, "g"),
|
||||
"<ID>"
|
||||
)
|
||||
fixturesContent = fixturesContent.replace(
|
||||
new RegExp(`${prefix}\\w*\\d*`, "g"),
|
||||
"<ID>"
|
||||
)
|
||||
return csvRows.reduce<any[]>((result, row) => {
|
||||
const rowCopy = { ...row }
|
||||
Object.keys(rowCopy).forEach((col) => {
|
||||
if (
|
||||
col.includes("Updated At") ||
|
||||
col.includes("Created At") ||
|
||||
col.includes("Deleted At")
|
||||
) {
|
||||
rowCopy[col] = "<DateTime>"
|
||||
}
|
||||
if (col.includes("Id") || col.startsWith("Product Category ")) {
|
||||
rowCopy[col] = "<ID>"
|
||||
}
|
||||
})
|
||||
|
||||
result.push(rowCopy)
|
||||
return result
|
||||
}, [])
|
||||
}
|
||||
|
||||
const assertExportedColumns = (rows: any[]) => {
|
||||
rows.forEach((row) => {
|
||||
EXPORTED_COLUMNS.forEach((column) => {
|
||||
if (column.includes("[ISO]")) {
|
||||
expect(
|
||||
Object.keys(row).filter((rowCol) =>
|
||||
rowCol.startsWith("Variant Price ")
|
||||
).length
|
||||
).toBeGreaterThanOrEqual(1)
|
||||
} else {
|
||||
expect(row).toHaveProperty(column.replace("*", "1"))
|
||||
}
|
||||
})
|
||||
})
|
||||
fileContent = fileContent.replace(dateRegex, "<DATE>")
|
||||
fixturesContent = fixturesContent.replace(dateRegex, "<DATE>")
|
||||
|
||||
fixturesContent = fixturesContent.replace(/,Shipping Profile Id*/g, "")
|
||||
fixturesContent = fixturesContent.replace(/,import-shipping-profile*/g, "")
|
||||
|
||||
expect(fileContent).toEqual(fixturesContent)
|
||||
}
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
@@ -121,11 +186,19 @@ medusaIntegrationTestRunner({
|
||||
).data.product_category
|
||||
|
||||
baseTag1 = (
|
||||
await api.post("/admin/product-tags", { value: "123" }, adminHeaders)
|
||||
await api.post(
|
||||
"/admin/product-tags",
|
||||
{ value: "tag-123" },
|
||||
adminHeaders
|
||||
)
|
||||
).data.product_tag
|
||||
|
||||
baseTag2 = (
|
||||
await api.post("/admin/product-tags", { value: "456" }, adminHeaders)
|
||||
await api.post(
|
||||
"/admin/product-tags",
|
||||
{ value: "tag-456" },
|
||||
adminHeaders
|
||||
)
|
||||
).data.product_tag
|
||||
|
||||
newTag = (
|
||||
@@ -252,10 +325,12 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
)
|
||||
|
||||
await compareCSVs(
|
||||
notifications[0].data.file.url,
|
||||
path.join(__dirname, "__fixtures__", "exported-products-comma.csv")
|
||||
const exportedFileContents = await getCSVContents(
|
||||
notifications[0].data.file.url
|
||||
)
|
||||
|
||||
assertExportedColumns(exportedFileContents)
|
||||
expect(exportedFileContents).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("should export a csv file with categories", async () => {
|
||||
@@ -278,10 +353,12 @@ medusaIntegrationTestRunner({
|
||||
await api.get("/admin/notifications", adminHeaders)
|
||||
).data.notifications
|
||||
|
||||
await compareCSVs(
|
||||
notifications[0].data.file.url,
|
||||
path.join(__dirname, "__fixtures__", "product-with-categories.csv")
|
||||
const exportedFileContents = await getCSVContents(
|
||||
notifications[0].data.file.url
|
||||
)
|
||||
|
||||
assertExportedColumns(exportedFileContents)
|
||||
expect(exportedFileContents).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("should export a csv file with region prices", async () => {
|
||||
@@ -338,10 +415,12 @@ medusaIntegrationTestRunner({
|
||||
await api.get("/admin/notifications", adminHeaders)
|
||||
).data.notifications
|
||||
|
||||
await compareCSVs(
|
||||
notifications[0].data.file.url,
|
||||
path.join(__dirname, "__fixtures__", "prices-with-region.csv")
|
||||
const exportedFileContents = await getCSVContents(
|
||||
notifications[0].data.file.url
|
||||
)
|
||||
|
||||
assertExportedColumns(exportedFileContents)
|
||||
expect(exportedFileContents).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("should export a csv file filtered by specific products", async () => {
|
||||
@@ -367,10 +446,12 @@ medusaIntegrationTestRunner({
|
||||
|
||||
expect(notifications.length).toBe(1)
|
||||
|
||||
await compareCSVs(
|
||||
notifications[0].data.file.url,
|
||||
path.join(__dirname, "__fixtures__", "filtered-products.csv")
|
||||
const exportedFileContents = await getCSVContents(
|
||||
notifications[0].data.file.url
|
||||
)
|
||||
|
||||
assertExportedColumns(exportedFileContents)
|
||||
expect(exportedFileContents).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { csv2json, json2csv } from "json-2-csv"
|
||||
import {
|
||||
medusaIntegrationTestRunner,
|
||||
TestEventUtils,
|
||||
@@ -13,6 +14,17 @@ import {
|
||||
} from "../../../../helpers/create-admin-user"
|
||||
import { getProductFixture } from "../../../../helpers/fixtures"
|
||||
|
||||
const UNALLOWED_EXPORTED_COLUMNS = [
|
||||
"Product Is Giftcard",
|
||||
"Product Created At",
|
||||
"Product Updated At",
|
||||
"Product Deleted At",
|
||||
"Variant Product Id",
|
||||
"Variant Created At",
|
||||
"Variant Updated At",
|
||||
"Variant Deleted At",
|
||||
]
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const getUploadReq = (file: { name: string; content: string }) => {
|
||||
@@ -29,7 +41,21 @@ const getUploadReq = (file: { name: string; content: string }) => {
|
||||
}
|
||||
}
|
||||
|
||||
function prepareCSVForImport(fileContents: string, delimiter: string = ",") {
|
||||
const CSVFileAsJSON = csv2json(fileContents, {
|
||||
delimiter: { field: delimiter },
|
||||
})
|
||||
CSVFileAsJSON.forEach((row) => {
|
||||
UNALLOWED_EXPORTED_COLUMNS.forEach((col) => {
|
||||
delete row[col]
|
||||
})
|
||||
})
|
||||
|
||||
return json2csv(CSVFileAsJSON)
|
||||
}
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
dbName: "bulk-uploads-local",
|
||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||
let baseCollection
|
||||
let baseType
|
||||
@@ -66,15 +92,27 @@ medusaIntegrationTestRunner({
|
||||
).data.product_type
|
||||
|
||||
baseTag1 = (
|
||||
await api.post("/admin/product-tags", { value: "123" }, adminHeaders)
|
||||
await api.post(
|
||||
"/admin/product-tags",
|
||||
{ value: "tag-123" },
|
||||
adminHeaders
|
||||
)
|
||||
).data.product_tag
|
||||
|
||||
baseTag2 = (
|
||||
await api.post("/admin/product-tags", { value: "123_1" }, adminHeaders)
|
||||
await api.post(
|
||||
"/admin/product-tags",
|
||||
{ value: "tag-123_1" },
|
||||
adminHeaders
|
||||
)
|
||||
).data.product_tag
|
||||
|
||||
baseTag3 = (
|
||||
await api.post("/admin/product-tags", { value: "456" }, adminHeaders)
|
||||
await api.post(
|
||||
"/admin/product-tags",
|
||||
{ value: "tag-456" },
|
||||
adminHeaders
|
||||
)
|
||||
).data.product_tag
|
||||
|
||||
newTag = (
|
||||
@@ -129,13 +167,18 @@ medusaIntegrationTestRunner({
|
||||
;(eventBus as any).eventEmitter_.removeAllListeners()
|
||||
})
|
||||
|
||||
describe("POST /admin/products/export", () => {
|
||||
describe("POST /admin/products/import", () => {
|
||||
// We want to ensure files with different delimiters are supported
|
||||
;[
|
||||
{ file: "exported-products-comma.csv", name: "delimited with comma" },
|
||||
{
|
||||
file: "exported-products-semicolon.csv",
|
||||
file: "products-comma.csv",
|
||||
name: "delimited with comma",
|
||||
delimiter: ",",
|
||||
},
|
||||
{
|
||||
file: "products-semicolon.csv",
|
||||
name: "delimited with semicolon",
|
||||
delimiter: ";",
|
||||
},
|
||||
].forEach((testcase) => {
|
||||
it(`should import a previously exported products CSV file ${testcase.name}`, async () => {
|
||||
@@ -157,8 +200,12 @@ medusaIntegrationTestRunner({
|
||||
/variant_01J44RRJZW1T9KQB6XG7Q6K61F/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(/tag-123/g, baseTag1.id)
|
||||
fileContent = fileContent.replace(/tag-456/g, baseTag3.id)
|
||||
fileContent = fileContent.replace(/new-tag/g, newTag.id)
|
||||
|
||||
fileContent = fileContent.replace(
|
||||
/import-shipping-profile*/g,
|
||||
@@ -167,7 +214,7 @@ medusaIntegrationTestRunner({
|
||||
|
||||
const { form, meta } = getUploadReq({
|
||||
name: "test.csv",
|
||||
content: fileContent,
|
||||
content: prepareCSVForImport(fileContent, testcase.delimiter),
|
||||
})
|
||||
|
||||
// BREAKING: The batch endpoints moved to the domain routes (admin/batch-jobs -> /admin/products/import). The payload and response changed as well.
|
||||
@@ -209,192 +256,193 @@ medusaIntegrationTestRunner({
|
||||
.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.arrayContaining([
|
||||
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,
|
||||
expect(dbProducts[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.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "size",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
collection: expect.objectContaining({
|
||||
id: baseCollection.id,
|
||||
expect.objectContaining({
|
||||
title: "color",
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Test variant 2",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 50,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 65,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 200,
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
]),
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
url: "test-image-2.png",
|
||||
}),
|
||||
]),
|
||||
tags: [
|
||||
expect.objectContaining({
|
||||
id: baseTag1.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: baseTag3.id,
|
||||
}),
|
||||
],
|
||||
type: expect.objectContaining({
|
||||
id: baseType.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
handle: "proposed-product",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-image.png",
|
||||
status: "proposed",
|
||||
description: "test-product-description",
|
||||
options: expect.arrayContaining([
|
||||
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.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
collection: expect.objectContaining({
|
||||
id: baseCollection.id,
|
||||
}),
|
||||
])
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Test variant 2",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 50,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 65,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 200,
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
|
||||
expect(dbProducts[1]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
handle: "proposed-product",
|
||||
is_giftcard: false,
|
||||
thumbnail: "test-image.png",
|
||||
status: "proposed",
|
||||
description: "test-product-description",
|
||||
options: expect.arrayContaining([
|
||||
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.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "eur",
|
||||
amount: 45,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -414,6 +462,9 @@ medusaIntegrationTestRunner({
|
||||
fileContent = fileContent.replace(/pcol_\w*\d*/g, baseCollection.id)
|
||||
fileContent = fileContent.replace(/ptyp_\w*\d*/g, baseType.id)
|
||||
fileContent = fileContent.replace(/pcat_\w*\d*/g, baseCategory.id)
|
||||
fileContent = fileContent.replace(/tag-123/g, baseTag1.id)
|
||||
fileContent = fileContent.replace(/tag-456/g, baseTag3.id)
|
||||
fileContent = fileContent.replace(/new-tag/g, newTag.id)
|
||||
|
||||
fileContent = fileContent.replace(
|
||||
/import-shipping-profile*/g,
|
||||
@@ -422,7 +473,7 @@ medusaIntegrationTestRunner({
|
||||
|
||||
const { form, meta } = getUploadReq({
|
||||
name: "test.csv",
|
||||
content: fileContent,
|
||||
content: prepareCSVForImport(fileContent),
|
||||
})
|
||||
|
||||
const batchJobRes = await api.post("/admin/products/import", form, meta)
|
||||
@@ -454,36 +505,7 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail on invalid region in prices being present in the CSV", async () => {
|
||||
let fileContent = await fs.readFile(
|
||||
path.join(__dirname, "__fixtures__", "invalid-prices.csv"),
|
||||
{ encoding: "utf-8" }
|
||||
)
|
||||
|
||||
fileContent = fileContent.replace(
|
||||
/import-shipping-profile*/g,
|
||||
shippingProfile.id
|
||||
)
|
||||
|
||||
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(
|
||||
"Region with name nonexistent not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should ignore non-existent fields being present in the CSV that don't start with Product or Variant", async () => {
|
||||
const subscriberExecution = TestEventUtils.waitSubscribersExecution(
|
||||
`${Modules.NOTIFICATION}.notification.${CommonEvents.CREATED}`,
|
||||
eventBus
|
||||
)
|
||||
|
||||
it("should complain about non-existent fields being present in the CSV", async () => {
|
||||
let fileContent = await fs.readFile(
|
||||
path.join(__dirname, "__fixtures__", "unrelated-column.csv"),
|
||||
{ encoding: "utf-8" }
|
||||
@@ -491,6 +513,9 @@ medusaIntegrationTestRunner({
|
||||
|
||||
fileContent = fileContent.replace(/pcol_\w*\d*/g, baseCollection.id)
|
||||
fileContent = fileContent.replace(/ptyp_\w*\d*/g, baseType.id)
|
||||
fileContent = fileContent.replace(/tag-123/g, baseTag1.id)
|
||||
fileContent = fileContent.replace(/tag-456/g, baseTag3.id)
|
||||
fileContent = fileContent.replace(/new-tag/g, newTag.id)
|
||||
|
||||
fileContent = fileContent.replace(
|
||||
/import-shipping-profile*/g,
|
||||
@@ -499,46 +524,19 @@ medusaIntegrationTestRunner({
|
||||
|
||||
const { form, meta } = getUploadReq({
|
||||
name: "test.csv",
|
||||
content: fileContent,
|
||||
content: prepareCSVForImport(fileContent),
|
||||
})
|
||||
|
||||
const batchJobRes = await api.post("/admin/products/import", form, meta)
|
||||
const batchJobRes = await api
|
||||
.post("/admin/products/import", form, meta)
|
||||
.catch((e) => e)
|
||||
|
||||
const transactionId = batchJobRes.data.transaction_id
|
||||
expect(transactionId).toBeTruthy()
|
||||
expect(batchJobRes.data.summary).toEqual({
|
||||
toCreate: 1,
|
||||
toUpdate: 0,
|
||||
})
|
||||
|
||||
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!`,
|
||||
}),
|
||||
})
|
||||
expect(batchJobRes.response.data.message).toEqual(
|
||||
'Invalid column name(s) "Some field"'
|
||||
)
|
||||
})
|
||||
|
||||
it("should successfully skip non-existent product fields being present in the CSV", async () => {
|
||||
const subscriberExecution = TestEventUtils.waitSubscribersExecution(
|
||||
`${Modules.NOTIFICATION}.notification.${CommonEvents.CREATED}`,
|
||||
eventBus
|
||||
)
|
||||
|
||||
let fileContent = await fs.readFile(
|
||||
path.join(__dirname, "__fixtures__", "invalid-column.csv"),
|
||||
{ encoding: "utf-8" }
|
||||
@@ -554,313 +552,15 @@ medusaIntegrationTestRunner({
|
||||
|
||||
const { form, meta } = getUploadReq({
|
||||
name: "test.csv",
|
||||
content: fileContent,
|
||||
content: prepareCSVForImport(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: 1,
|
||||
toUpdate: 0,
|
||||
})
|
||||
|
||||
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 [importedProduct] = (
|
||||
await api.get("/admin/products?limit=1&order=-id", adminHeaders)
|
||||
).data.products
|
||||
|
||||
expect(importedProduct).not.toEqual(
|
||||
expect.objectContaining({
|
||||
field: "Test product",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("supports importing the v1 template", async () => {
|
||||
const subscriberExecution = TestEventUtils.waitSubscribersExecution(
|
||||
`${Modules.NOTIFICATION}.notification.${CommonEvents.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
|
||||
)
|
||||
|
||||
fileContent = fileContent.replace(
|
||||
/import-shipping-profile*/g,
|
||||
shippingProfile.id
|
||||
)
|
||||
|
||||
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.arrayContaining([
|
||||
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.arrayContaining([
|
||||
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: expect.arrayContaining([
|
||||
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",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
]),
|
||||
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.arrayContaining([
|
||||
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.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
sku: "test-sku-1-1",
|
||||
barcode: "test-barcode-1-1",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
rules: {
|
||||
region_id: baseRegion.id,
|
||||
},
|
||||
amount: 1,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 1.1,
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
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
|
||||
const batchJobRes = 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"
|
||||
|
||||
expect(batchJobRes.response.data.message).toEqual(
|
||||
'Invalid column name(s) "Product field"'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"@swc/core": "^1.4.8",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"jest": "^29.7.0",
|
||||
"json-2-csv": "^5.5.9",
|
||||
"typescript": "^5.6.2"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user