Merge pull request #373 from medusajs/feat/product-variant-rank
Feat: Add product variant rank
This commit is contained in:
@@ -0,0 +1,382 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`/admin/products GET /admin/products returns a list of products with child entities 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"collection": Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"handle": "test-collection",
|
||||||
|
"id": StringMatching /\\^test-\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"title": "Test collection",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
},
|
||||||
|
"collection_id": "test-collection",
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"description": "test-product-description",
|
||||||
|
"discountable": true,
|
||||||
|
"handle": "test-product",
|
||||||
|
"height": null,
|
||||||
|
"hs_code": null,
|
||||||
|
"id": StringMatching /\\^test-\\*/,
|
||||||
|
"images": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"url": "test-image.png",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"is_giftcard": false,
|
||||||
|
"length": null,
|
||||||
|
"material": null,
|
||||||
|
"metadata": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"options": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"product_id": StringMatching /\\^test-\\*/,
|
||||||
|
"title": "test-option",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"origin_country": null,
|
||||||
|
"profile_id": StringMatching /\\^sp_\\*/,
|
||||||
|
"subtitle": null,
|
||||||
|
"tags": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^tag\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "123",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"thumbnail": null,
|
||||||
|
"title": "Test product",
|
||||||
|
"type": Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "test-type",
|
||||||
|
},
|
||||||
|
"type_id": "test-type",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"variants": Array [
|
||||||
|
Object {
|
||||||
|
"allow_backorder": false,
|
||||||
|
"barcode": "test-barcode",
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"ean": "test-ean",
|
||||||
|
"height": null,
|
||||||
|
"hs_code": null,
|
||||||
|
"id": "test-variant",
|
||||||
|
"inventory_quantity": 10,
|
||||||
|
"length": null,
|
||||||
|
"manage_inventory": true,
|
||||||
|
"material": null,
|
||||||
|
"metadata": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"options": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-variant-option\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"option_id": StringMatching /\\^test-opt\\*/,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "Default variant",
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"origin_country": null,
|
||||||
|
"prices": Array [
|
||||||
|
Object {
|
||||||
|
"amount": 100,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"currency_code": "usd",
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-price\\*/,
|
||||||
|
"region_id": null,
|
||||||
|
"sale_amount": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"product_id": StringMatching /\\^test-\\*/,
|
||||||
|
"sku": "test-sku",
|
||||||
|
"title": "Test variant",
|
||||||
|
"upc": "test-upc",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"weight": null,
|
||||||
|
"width": null,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"allow_backorder": false,
|
||||||
|
"barcode": null,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"ean": "test-ean2",
|
||||||
|
"height": null,
|
||||||
|
"hs_code": null,
|
||||||
|
"id": "test-variant_2",
|
||||||
|
"inventory_quantity": 10,
|
||||||
|
"length": null,
|
||||||
|
"manage_inventory": true,
|
||||||
|
"material": null,
|
||||||
|
"metadata": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"options": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-variant-option\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"option_id": StringMatching /\\^test-opt\\*/,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "Default variant 2",
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"origin_country": null,
|
||||||
|
"prices": Array [
|
||||||
|
Object {
|
||||||
|
"amount": 100,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"currency_code": "usd",
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-price\\*/,
|
||||||
|
"region_id": null,
|
||||||
|
"sale_amount": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"product_id": StringMatching /\\^test-\\*/,
|
||||||
|
"sku": "test-sku2",
|
||||||
|
"title": "Test variant rank (2)",
|
||||||
|
"upc": "test-upc2",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"weight": null,
|
||||||
|
"width": null,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"allow_backorder": false,
|
||||||
|
"barcode": "test-barcode 1",
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"ean": "test-ean1",
|
||||||
|
"height": null,
|
||||||
|
"hs_code": null,
|
||||||
|
"id": "test-variant_1",
|
||||||
|
"inventory_quantity": 10,
|
||||||
|
"length": null,
|
||||||
|
"manage_inventory": true,
|
||||||
|
"material": null,
|
||||||
|
"metadata": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"options": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-variant-option\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"option_id": StringMatching /\\^test-opt\\*/,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "Default variant 1",
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"origin_country": null,
|
||||||
|
"prices": Array [
|
||||||
|
Object {
|
||||||
|
"amount": 100,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"currency_code": "usd",
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-price\\*/,
|
||||||
|
"region_id": null,
|
||||||
|
"sale_amount": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"product_id": StringMatching /\\^test-\\*/,
|
||||||
|
"sku": "test-sku1",
|
||||||
|
"title": "Test variant rank (1)",
|
||||||
|
"upc": "test-upc1",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"weight": null,
|
||||||
|
"width": null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"weight": null,
|
||||||
|
"width": null,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"collection": Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"handle": "test-collection",
|
||||||
|
"id": StringMatching /\\^test-\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"title": "Test collection",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
},
|
||||||
|
"collection_id": "test-collection",
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"description": "test-product-description1",
|
||||||
|
"discountable": true,
|
||||||
|
"handle": "test-product1",
|
||||||
|
"height": null,
|
||||||
|
"hs_code": null,
|
||||||
|
"id": StringMatching /\\^test-\\*/,
|
||||||
|
"images": Array [],
|
||||||
|
"is_giftcard": false,
|
||||||
|
"length": null,
|
||||||
|
"material": null,
|
||||||
|
"metadata": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"options": Array [],
|
||||||
|
"origin_country": null,
|
||||||
|
"profile_id": StringMatching /\\^sp_\\*/,
|
||||||
|
"subtitle": null,
|
||||||
|
"tags": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^tag\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "123",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"thumbnail": null,
|
||||||
|
"title": "Test product1",
|
||||||
|
"type": Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "test-type",
|
||||||
|
},
|
||||||
|
"type_id": "test-type",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"variants": Array [
|
||||||
|
Object {
|
||||||
|
"allow_backorder": false,
|
||||||
|
"barcode": null,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"ean": "test-ean4",
|
||||||
|
"height": null,
|
||||||
|
"hs_code": null,
|
||||||
|
"id": "test-variant_4",
|
||||||
|
"inventory_quantity": 10,
|
||||||
|
"length": null,
|
||||||
|
"manage_inventory": true,
|
||||||
|
"material": null,
|
||||||
|
"metadata": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"options": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-variant-option\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"option_id": StringMatching /\\^test-opt\\*/,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "Default variant 3",
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"origin_country": null,
|
||||||
|
"prices": Array [
|
||||||
|
Object {
|
||||||
|
"amount": 100,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"currency_code": "usd",
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-price\\*/,
|
||||||
|
"region_id": null,
|
||||||
|
"sale_amount": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"product_id": StringMatching /\\^test-\\*/,
|
||||||
|
"sku": "test-sku4",
|
||||||
|
"title": "Test variant rank (2)",
|
||||||
|
"upc": "test-upc4",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"weight": null,
|
||||||
|
"width": null,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"allow_backorder": false,
|
||||||
|
"barcode": null,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"ean": "test-ean3",
|
||||||
|
"height": null,
|
||||||
|
"hs_code": null,
|
||||||
|
"id": "test-variant_3",
|
||||||
|
"inventory_quantity": 10,
|
||||||
|
"length": null,
|
||||||
|
"manage_inventory": true,
|
||||||
|
"material": null,
|
||||||
|
"metadata": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"options": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-variant-option\\*/,
|
||||||
|
"metadata": null,
|
||||||
|
"option_id": StringMatching /\\^test-opt\\*/,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "Default variant 3",
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"origin_country": null,
|
||||||
|
"prices": Array [
|
||||||
|
Object {
|
||||||
|
"amount": 100,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"currency_code": "usd",
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": StringMatching /\\^test-price\\*/,
|
||||||
|
"region_id": null,
|
||||||
|
"sale_amount": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"variant_id": StringMatching /\\^test-variant\\*/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"product_id": StringMatching /\\^test-\\*/,
|
||||||
|
"sku": "test-sku3",
|
||||||
|
"title": "Test variant rank (2)",
|
||||||
|
"upc": "test-upc3",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"weight": null,
|
||||||
|
"width": null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"weight": null,
|
||||||
|
"width": null,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
@@ -1,49 +1,266 @@
|
|||||||
const path = require("path");
|
const path = require("path")
|
||||||
|
|
||||||
const setupServer = require("../../../helpers/setup-server");
|
const setupServer = require("../../../helpers/setup-server")
|
||||||
const { useApi } = require("../../../helpers/use-api");
|
const { useApi } = require("../../../helpers/use-api")
|
||||||
const { initDb, useDb } = require("../../../helpers/use-db");
|
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||||
|
|
||||||
const adminSeeder = require("../../helpers/admin-seeder");
|
const adminSeeder = require("../../helpers/admin-seeder")
|
||||||
const productSeeder = require("../../helpers/product-seeder");
|
const productSeeder = require("../../helpers/product-seeder")
|
||||||
|
|
||||||
jest.setTimeout(30000);
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
describe("/admin/products", () => {
|
describe("/admin/products", () => {
|
||||||
let medusaProcess;
|
let medusaProcess
|
||||||
let dbConnection;
|
let dbConnection
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||||
dbConnection = await initDb({ cwd });
|
dbConnection = await initDb({ cwd })
|
||||||
medusaProcess = await setupServer({ cwd });
|
medusaProcess = await setupServer({ cwd, verbose: true })
|
||||||
});
|
})
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
const db = useDb();
|
const db = useDb()
|
||||||
await db.shutdown();
|
await db.shutdown()
|
||||||
|
|
||||||
medusaProcess.kill();
|
medusaProcess.kill()
|
||||||
});
|
})
|
||||||
|
|
||||||
|
describe("GET /admin/products", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
try {
|
||||||
|
await productSeeder(dbConnection)
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns a list of products with child entities", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/products", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.data.products).toMatchSnapshot([
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-*/),
|
||||||
|
product_id: expect.stringMatching(/^test-*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: "test-variant", //expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
product_id: expect.stringMatching(/^test-*/),
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-price*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-variant-option*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
option_id: expect.stringMatching(/^test-opt*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test-variant_2", //expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
product_id: expect.stringMatching(/^test-*/),
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-price*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-variant-option*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
option_id: expect.stringMatching(/^test-opt*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test-variant_1", // expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
product_id: expect.stringMatching(/^test-*/),
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-price*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-variant-option*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
option_id: expect.stringMatching(/^test-opt*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tags: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^tag*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: {
|
||||||
|
id: expect.stringMatching(/^test-*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
collection: {
|
||||||
|
id: expect.stringMatching(/^test-*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
profile_id: expect.stringMatching(/^sp_*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
options: [],
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: "test-variant_4", //expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
product_id: expect.stringMatching(/^test-*/),
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-price*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-variant-option*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
option_id: expect.stringMatching(/^test-opt*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test-variant_3", //expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
product_id: expect.stringMatching(/^test-*/),
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-price*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^test-variant-option*/),
|
||||||
|
variant_id: expect.stringMatching(/^test-variant*/),
|
||||||
|
option_id: expect.stringMatching(/^test-opt*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tags: [
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^tag*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: {
|
||||||
|
id: expect.stringMatching(/^test-*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
collection: {
|
||||||
|
id: expect.stringMatching(/^test-*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
profile_id: expect.stringMatching(/^sp_*/),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("POST /admin/products", () => {
|
describe("POST /admin/products", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
try {
|
try {
|
||||||
await productSeeder(dbConnection);
|
await productSeeder(dbConnection)
|
||||||
await adminSeeder(dbConnection);
|
await adminSeeder(dbConnection)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
throw err;
|
throw err
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
const db = useDb();
|
const db = useDb()
|
||||||
await db.teardown();
|
await db.teardown()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("creates a product", async () => {
|
it("creates a product", async () => {
|
||||||
const api = useApi();
|
const api = useApi()
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
title: "Test product",
|
title: "Test product",
|
||||||
@@ -61,7 +278,7 @@ describe("/admin/products", () => {
|
|||||||
options: [{ value: "large" }, { value: "green" }],
|
options: [{ value: "large" }, { value: "green" }],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
|
||||||
const response = await api
|
const response = await api
|
||||||
.post("/admin/products", payload, {
|
.post("/admin/products", payload, {
|
||||||
@@ -70,11 +287,10 @@ describe("/admin/products", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(response.status).toEqual(200);
|
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.product).toEqual(
|
expect(response.data.product).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
title: "Test product",
|
title: "Test product",
|
||||||
@@ -133,11 +349,77 @@ describe("/admin/products", () => {
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
|
||||||
|
it("Sets variant ranks when creating a product", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
title: "Test product - 1",
|
||||||
|
description: "test-product-description 1",
|
||||||
|
type: { value: "test-type 1" },
|
||||||
|
images: ["test-image.png", "test-image-2.png"],
|
||||||
|
collection_id: "test-collection",
|
||||||
|
tags: [{ value: "123" }, { value: "456" }],
|
||||||
|
options: [{ title: "size" }, { title: "color" }],
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant 1",
|
||||||
|
inventory_quantity: 10,
|
||||||
|
prices: [{ currency_code: "usd", amount: 100 }],
|
||||||
|
options: [{ value: "large" }, { value: "green" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test variant 2",
|
||||||
|
inventory_quantity: 10,
|
||||||
|
prices: [{ currency_code: "usd", amount: 100 }],
|
||||||
|
options: [{ value: "large" }, { value: "green" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const creationResponse = await api
|
||||||
|
.post("/admin/products", payload, {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(creationResponse.status).toEqual(200)
|
||||||
|
|
||||||
|
const productId = creationResponse.data.product.id
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get(`/admin/products/${productId}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
title: "Test product - 1",
|
||||||
|
variants: [
|
||||||
|
expect.objectContaining({
|
||||||
|
title: "Test variant 1",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
title: "Test variant 2",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("creates a giftcard", async () => {
|
it("creates a giftcard", async () => {
|
||||||
const api = useApi();
|
const api = useApi()
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
title: "Test Giftcard",
|
title: "Test Giftcard",
|
||||||
@@ -151,7 +433,7 @@ describe("/admin/products", () => {
|
|||||||
options: [{ value: "100" }],
|
options: [{ value: "100" }],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
|
||||||
const response = await api
|
const response = await api
|
||||||
.post("/admin/products", payload, {
|
.post("/admin/products", payload, {
|
||||||
@@ -160,21 +442,21 @@ describe("/admin/products", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200)
|
||||||
|
|
||||||
expect(response.data.product).toEqual(
|
expect(response.data.product).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
title: "Test Giftcard",
|
title: "Test Giftcard",
|
||||||
discountable: false,
|
discountable: false,
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
|
||||||
it("updates a product (update prices, tags, delete collection, delete type, replaces images)", async () => {
|
it("updates a product (update prices, tags, delete collection, delete type, replaces images)", async () => {
|
||||||
const api = useApi();
|
const api = useApi()
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
collection_id: null,
|
collection_id: null,
|
||||||
@@ -182,13 +464,19 @@ describe("/admin/products", () => {
|
|||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
id: "test-variant",
|
id: "test-variant",
|
||||||
prices: [{ currency_code: "usd", amount: 100, sale_amount: 75 }],
|
prices: [
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 100,
|
||||||
|
sale_amount: 75,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
tags: [{ value: "123" }],
|
tags: [{ value: "123" }],
|
||||||
images: ["test-image-2.png"],
|
images: ["test-image-2.png"],
|
||||||
type: { value: "test-type-2" },
|
type: { value: "test-type-2" },
|
||||||
};
|
}
|
||||||
|
|
||||||
const response = await api
|
const response = await api
|
||||||
.post("/admin/products/test-product", payload, {
|
.post("/admin/products/test-product", payload, {
|
||||||
@@ -197,10 +485,10 @@ describe("/admin/products", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200)
|
||||||
|
|
||||||
expect(response.data.product).toEqual(
|
expect(response.data.product).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -231,15 +519,69 @@ describe("/admin/products", () => {
|
|||||||
value: "test-type-2",
|
value: "test-type-2",
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
|
||||||
|
it("updates a product (variant ordering)", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
collection_id: null,
|
||||||
|
type: null,
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: "test-variant",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test-variant_1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test-variant_2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.post("/admin/products/test-product", payload, {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
|
||||||
|
expect(response.data.product).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
title: "Test product",
|
||||||
|
variants: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-variant",
|
||||||
|
title: "Test variant",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-variant_1",
|
||||||
|
title: "Test variant rank (1)",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "test-variant_2",
|
||||||
|
title: "Test variant rank (2)",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
type: null,
|
||||||
|
collection: null,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("add option", async () => {
|
it("add option", async () => {
|
||||||
const api = useApi();
|
const api = useApi()
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
title: "should_add",
|
title: "should_add",
|
||||||
};
|
}
|
||||||
|
|
||||||
const response = await api
|
const response = await api
|
||||||
.post("/admin/products/test-product/options", payload, {
|
.post("/admin/products/test-product/options", payload, {
|
||||||
@@ -248,41 +590,41 @@ describe("/admin/products", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200)
|
||||||
|
|
||||||
expect(response.data.product).toEqual(
|
expect(response.data.product).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
options: [
|
options: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
title: "should_add",
|
title: "should_add",
|
||||||
product_id: "test-product",
|
product_id: "test-product",
|
||||||
}),
|
}),
|
||||||
],
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe("testing for soft-deletion + uniqueness on handles, collection and variant properties", () => {
|
describe("testing for soft-deletion + uniqueness on handles, collection and variant properties", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
try {
|
try {
|
||||||
await productSeeder(dbConnection);
|
await productSeeder(dbConnection)
|
||||||
await adminSeeder(dbConnection);
|
await adminSeeder(dbConnection)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
throw err;
|
throw err
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
const db = useDb();
|
const db = useDb()
|
||||||
await db.teardown();
|
await db.teardown()
|
||||||
});
|
})
|
||||||
|
|
||||||
it("successfully deletes a product", async () => {
|
it("successfully deletes a product", async () => {
|
||||||
const api = useApi();
|
const api = useApi()
|
||||||
|
|
||||||
const response = await api
|
const response = await api
|
||||||
.delete("/admin/products/test-product", {
|
.delete("/admin/products/test-product", {
|
||||||
@@ -291,21 +633,21 @@ describe("/admin/products", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200)
|
||||||
|
|
||||||
expect(response.data).toEqual(
|
expect(response.data).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "test-product",
|
id: "test-product",
|
||||||
deleted: true,
|
deleted: true,
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
|
||||||
it("successfully creates product with soft-deleted product handle", async () => {
|
it("successfully creates product with soft-deleted product handle", async () => {
|
||||||
const api = useApi();
|
const api = useApi()
|
||||||
|
|
||||||
// First we soft-delete the product
|
// First we soft-delete the product
|
||||||
const response = await api
|
const response = await api
|
||||||
@@ -315,11 +657,11 @@ describe("/admin/products", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.id).toEqual("test-product");
|
expect(response.data.id).toEqual("test-product")
|
||||||
|
|
||||||
// Lets try to create a product with same handle as deleted one
|
// Lets try to create a product with same handle as deleted one
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -339,20 +681,20 @@ describe("/admin/products", () => {
|
|||||||
options: [{ value: "large" }, { value: "green" }],
|
options: [{ value: "large" }, { value: "green" }],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
|
||||||
const res = await api.post("/admin/products", payload, {
|
const res = await api.post("/admin/products", payload, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: "Bearer test_token",
|
Authorization: "Bearer test_token",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(res.status).toEqual(200);
|
expect(res.status).toEqual(200)
|
||||||
expect(res.data.product.handle).toEqual("test-product");
|
expect(res.data.product.handle).toEqual("test-product")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("successfully deletes product collection", async () => {
|
it("successfully deletes product collection", async () => {
|
||||||
const api = useApi();
|
const api = useApi()
|
||||||
|
|
||||||
// First we soft-delete the product collection
|
// First we soft-delete the product collection
|
||||||
const response = await api
|
const response = await api
|
||||||
@@ -362,15 +704,15 @@ describe("/admin/products", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.id).toEqual("test-collection");
|
expect(response.data.id).toEqual("test-collection")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("successfully creates soft-deleted product collection", async () => {
|
it("successfully creates soft-deleted product collection", async () => {
|
||||||
const api = useApi();
|
const api = useApi()
|
||||||
|
|
||||||
const response = await api
|
const response = await api
|
||||||
.delete("/admin/collections/test-collection", {
|
.delete("/admin/collections/test-collection", {
|
||||||
@@ -379,30 +721,40 @@ describe("/admin/products", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.id).toEqual("test-collection");
|
expect(response.data.id).toEqual("test-collection")
|
||||||
|
|
||||||
// Lets try to create a product collection with same handle as deleted one
|
// Lets try to create a product collection with same handle as deleted one
|
||||||
const payload = {
|
const payload = {
|
||||||
title: "Another test collection",
|
title: "Another test collection",
|
||||||
handle: "test-collection",
|
handle: "test-collection",
|
||||||
};
|
}
|
||||||
|
|
||||||
const res = await api.post("/admin/collections", payload, {
|
const res = await api.post("/admin/collections", payload, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: "Bearer test_token",
|
Authorization: "Bearer test_token",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(res.status).toEqual(200);
|
expect(res.status).toEqual(200)
|
||||||
expect(res.data.collection.handle).toEqual("test-collection");
|
expect(res.data.collection.handle).toEqual("test-collection")
|
||||||
});
|
})
|
||||||
|
|
||||||
it("successfully creates soft-deleted product variant", async () => {
|
it("successfully creates soft-deleted product variant", async () => {
|
||||||
const api = useApi();
|
const api = useApi()
|
||||||
|
|
||||||
|
const product = await api
|
||||||
|
.get("/admin/products/test-product", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
const response = await api
|
const response = await api
|
||||||
.delete("/admin/products/test-product/variants/test-variant", {
|
.delete("/admin/products/test-product/variants/test-variant", {
|
||||||
@@ -411,11 +763,11 @@ describe("/admin/products", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
});
|
})
|
||||||
|
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.variant_id).toEqual("test-variant");
|
expect(response.data.variant_id).toEqual("test-variant")
|
||||||
|
|
||||||
// Lets try to create a product collection with same handle as deleted one
|
// Lets try to create a product collection with same handle as deleted one
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -430,19 +782,18 @@ describe("/admin/products", () => {
|
|||||||
amount: 100,
|
amount: 100,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
options: [{ option_id: "test-option", value: "inserted value" }],
|
||||||
|
}
|
||||||
|
|
||||||
const res = await api.post(
|
const res = await api
|
||||||
"/admin/products/test-product/variants",
|
.post("/admin/products/test-product/variants", payload, {
|
||||||
payload,
|
|
||||||
{
|
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: "Bearer test_token",
|
Authorization: "Bearer test_token",
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
);
|
.catch((err) => console.log(err))
|
||||||
|
|
||||||
expect(res.status).toEqual(200);
|
expect(res.status).toEqual(200)
|
||||||
expect(res.data.product.variants).toEqual(
|
expect(res.data.product.variants).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -453,7 +804,7 @@ describe("/admin/products", () => {
|
|||||||
barcode: "test-barcode",
|
barcode: "test-barcode",
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -2,55 +2,56 @@ const {
|
|||||||
ProductCollection,
|
ProductCollection,
|
||||||
ProductTag,
|
ProductTag,
|
||||||
ProductType,
|
ProductType,
|
||||||
|
ProductOption,
|
||||||
Region,
|
Region,
|
||||||
Product,
|
Product,
|
||||||
ShippingProfile,
|
ShippingProfile,
|
||||||
ProductVariant,
|
ProductVariant,
|
||||||
Image,
|
Image,
|
||||||
} = require("@medusajs/medusa");
|
} = require("@medusajs/medusa")
|
||||||
|
|
||||||
module.exports = async (connection, data = {}) => {
|
module.exports = async (connection, data = {}) => {
|
||||||
const manager = connection.manager;
|
const manager = connection.manager
|
||||||
|
|
||||||
const defaultProfile = await manager.findOne(ShippingProfile, {
|
const defaultProfile = await manager.findOne(ShippingProfile, {
|
||||||
type: "default",
|
type: "default",
|
||||||
});
|
})
|
||||||
|
|
||||||
const coll = manager.create(ProductCollection, {
|
const coll = manager.create(ProductCollection, {
|
||||||
id: "test-collection",
|
id: "test-collection",
|
||||||
handle: "test-collection",
|
handle: "test-collection",
|
||||||
title: "Test collection",
|
title: "Test collection",
|
||||||
});
|
})
|
||||||
|
|
||||||
await manager.save(coll);
|
await manager.save(coll)
|
||||||
|
|
||||||
const tag = manager.create(ProductTag, {
|
const tag = manager.create(ProductTag, {
|
||||||
id: "tag1",
|
id: "tag1",
|
||||||
value: "123",
|
value: "123",
|
||||||
});
|
})
|
||||||
|
|
||||||
await manager.save(tag);
|
await manager.save(tag)
|
||||||
|
|
||||||
const type = manager.create(ProductType, {
|
const type = manager.create(ProductType, {
|
||||||
id: "test-type",
|
id: "test-type",
|
||||||
value: "test-type",
|
value: "test-type",
|
||||||
});
|
})
|
||||||
|
|
||||||
await manager.save(type);
|
await manager.save(type)
|
||||||
|
|
||||||
const image = manager.create(Image, {
|
const image = manager.create(Image, {
|
||||||
id: "test-image",
|
id: "test-image",
|
||||||
url: "test-image.png",
|
url: "test-image.png",
|
||||||
});
|
})
|
||||||
|
|
||||||
await manager.save(image);
|
await manager.save(image)
|
||||||
|
|
||||||
await manager.insert(Region, {
|
await manager.insert(Region, {
|
||||||
id: "test-region",
|
id: "test-region",
|
||||||
name: "Test Region",
|
name: "Test Region",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
tax_rate: 0,
|
tax_rate: 0,
|
||||||
});
|
})
|
||||||
|
|
||||||
const p = manager.create(Product, {
|
const p = manager.create(Product, {
|
||||||
id: "test-product",
|
id: "test-product",
|
||||||
@@ -64,23 +65,138 @@ module.exports = async (connection, data = {}) => {
|
|||||||
{ id: "tag1", value: "123" },
|
{ id: "tag1", value: "123" },
|
||||||
{ tag: "tag2", value: "456" },
|
{ tag: "tag2", value: "456" },
|
||||||
],
|
],
|
||||||
options: [{ id: "test-option", title: "Default value" }],
|
})
|
||||||
});
|
|
||||||
|
|
||||||
p.images = [image];
|
p.images = [image]
|
||||||
|
|
||||||
await manager.save(p);
|
await manager.save(p)
|
||||||
|
|
||||||
await manager.insert(ProductVariant, {
|
await manager.save(ProductOption, {
|
||||||
|
id: "test-option",
|
||||||
|
title: "test-option",
|
||||||
|
product_id: "test-product",
|
||||||
|
})
|
||||||
|
|
||||||
|
const variant1 = await manager.create(ProductVariant, {
|
||||||
id: "test-variant",
|
id: "test-variant",
|
||||||
inventory_quantity: 10,
|
inventory_quantity: 10,
|
||||||
title: "Test variant",
|
title: "Test variant",
|
||||||
|
variant_rank: 0,
|
||||||
sku: "test-sku",
|
sku: "test-sku",
|
||||||
ean: "test-ean",
|
ean: "test-ean",
|
||||||
upc: "test-upc",
|
upc: "test-upc",
|
||||||
barcode: "test-barcode",
|
barcode: "test-barcode",
|
||||||
product_id: "test-product",
|
product_id: "test-product",
|
||||||
prices: [{ id: "test-price", currency_code: "usd", amount: 100 }],
|
prices: [{ id: "test-price", currency_code: "usd", amount: 100 }],
|
||||||
options: [{ id: "test-variant-option", value: "Default variant" }],
|
options: [
|
||||||
});
|
{
|
||||||
};
|
id: "test-variant-option",
|
||||||
|
value: "Default variant",
|
||||||
|
option_id: "test-option",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await manager.save(variant1)
|
||||||
|
|
||||||
|
const variant2 = await manager.create(ProductVariant, {
|
||||||
|
id: "test-variant_1",
|
||||||
|
inventory_quantity: 10,
|
||||||
|
title: "Test variant rank (1)",
|
||||||
|
variant_rank: 2,
|
||||||
|
sku: "test-sku1",
|
||||||
|
ean: "test-ean1",
|
||||||
|
upc: "test-upc1",
|
||||||
|
barcode: "test-barcode 1",
|
||||||
|
product_id: "test-product",
|
||||||
|
prices: [{ id: "test-price1", currency_code: "usd", amount: 100 }],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-variant-option-1",
|
||||||
|
value: "Default variant 1",
|
||||||
|
option_id: "test-option",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await manager.save(variant2)
|
||||||
|
|
||||||
|
const variant3 = await manager.create(ProductVariant, {
|
||||||
|
id: "test-variant_2",
|
||||||
|
inventory_quantity: 10,
|
||||||
|
title: "Test variant rank (2)",
|
||||||
|
variant_rank: 1,
|
||||||
|
sku: "test-sku2",
|
||||||
|
ean: "test-ean2",
|
||||||
|
upc: "test-upc2",
|
||||||
|
product_id: "test-product",
|
||||||
|
prices: [{ id: "test-price2", currency_code: "usd", amount: 100 }],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-variant-option-2",
|
||||||
|
value: "Default variant 2",
|
||||||
|
option_id: "test-option",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await manager.save(variant3)
|
||||||
|
|
||||||
|
const p1 = manager.create(Product, {
|
||||||
|
id: "test-product1",
|
||||||
|
handle: "test-product1",
|
||||||
|
title: "Test product1",
|
||||||
|
profile_id: defaultProfile.id,
|
||||||
|
description: "test-product-description1",
|
||||||
|
collection_id: "test-collection",
|
||||||
|
type: { id: "test-type", value: "test-type" },
|
||||||
|
tags: [
|
||||||
|
{ id: "tag1", value: "123" },
|
||||||
|
{ tag: "tag2", value: "456" },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await manager.save(p1)
|
||||||
|
|
||||||
|
const variant4 = await manager.create(ProductVariant, {
|
||||||
|
id: "test-variant_3",
|
||||||
|
inventory_quantity: 10,
|
||||||
|
title: "Test variant rank (2)",
|
||||||
|
variant_rank: 1,
|
||||||
|
sku: "test-sku3",
|
||||||
|
ean: "test-ean3",
|
||||||
|
upc: "test-upc3",
|
||||||
|
product_id: "test-product1",
|
||||||
|
prices: [{ id: "test-price3", currency_code: "usd", amount: 100 }],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-variant-option-3",
|
||||||
|
value: "Default variant 3",
|
||||||
|
option_id: "test-option",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await manager.save(variant4)
|
||||||
|
|
||||||
|
const variant5 = await manager.create(ProductVariant, {
|
||||||
|
id: "test-variant_4",
|
||||||
|
inventory_quantity: 10,
|
||||||
|
title: "Test variant rank (2)",
|
||||||
|
variant_rank: 0,
|
||||||
|
sku: "test-sku4",
|
||||||
|
ean: "test-ean4",
|
||||||
|
upc: "test-upc4",
|
||||||
|
product_id: "test-product1",
|
||||||
|
prices: [{ id: "test-price4", currency_code: "usd", amount: 100 }],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
id: "test-variant-option-4",
|
||||||
|
value: "Default variant 3",
|
||||||
|
option_id: "test-option",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await manager.save(variant5)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,80 @@
|
|||||||
import { IdMap } from "medusa-test-utils"
|
import { IdMap } from "medusa-test-utils"
|
||||||
import { request } from "../../../../../helpers/test-request"
|
import { request } from "../../../../../helpers/test-request"
|
||||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||||
|
import { ProductVariantServiceMock } from "../../../../../services/__mocks__/product-variant"
|
||||||
import { ShippingProfileServiceMock } from "../../../../../services/__mocks__/shipping-profile"
|
import { ShippingProfileServiceMock } from "../../../../../services/__mocks__/shipping-profile"
|
||||||
|
|
||||||
describe("POST /admin/products", () => {
|
describe("POST /admin/products", () => {
|
||||||
describe("successful creation", () => {
|
describe("successful creation with variants", () => {
|
||||||
|
let subject
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
subject = await request("POST", "/admin/products", {
|
||||||
|
payload: {
|
||||||
|
title: "Test Product with variants",
|
||||||
|
description: "Test Description",
|
||||||
|
tags: [{ id: "test", value: "test" }],
|
||||||
|
handle: "test-product",
|
||||||
|
options: [{ title: "Test" }],
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test",
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
currency_code: "USD",
|
||||||
|
amount: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "100",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminSession: {
|
||||||
|
jwt: {
|
||||||
|
userId: IdMap.getId("admin_user"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns 200", () => {
|
||||||
|
expect(subject.status).toEqual(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("assigns invokes productVariantService with ranked variants", () => {
|
||||||
|
expect(ProductVariantServiceMock.create).toHaveBeenCalledTimes(1)
|
||||||
|
expect(ProductVariantServiceMock.create).toHaveBeenCalledWith(
|
||||||
|
IdMap.getId("productWithOptions"),
|
||||||
|
{
|
||||||
|
title: "Test",
|
||||||
|
variant_rank: 0,
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
currency_code: "USD",
|
||||||
|
amount: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
option_id: IdMap.getId("option1"),
|
||||||
|
value: "100",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
inventory_quantity: 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("successful creation test", () => {
|
||||||
let subject
|
let subject
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@@ -14,6 +84,7 @@ describe("POST /admin/products", () => {
|
|||||||
description: "Test Description",
|
description: "Test Description",
|
||||||
tags: [{ id: "test", value: "test" }],
|
tags: [{ id: "test", value: "test" }],
|
||||||
handle: "test-product",
|
handle: "test-product",
|
||||||
|
options: [{ title: "Denominations" }],
|
||||||
},
|
},
|
||||||
adminSession: {
|
adminSession: {
|
||||||
jwt: {
|
jwt: {
|
||||||
@@ -40,6 +111,7 @@ describe("POST /admin/products", () => {
|
|||||||
tags: [{ id: "test", value: "test" }],
|
tags: [{ id: "test", value: "test" }],
|
||||||
handle: "test-product",
|
handle: "test-product",
|
||||||
is_giftcard: false,
|
is_giftcard: false,
|
||||||
|
options: [{ title: "Denominations" }],
|
||||||
profile_id: IdMap.getId("default_shipping_profile"),
|
profile_id: IdMap.getId("default_shipping_profile"),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -328,6 +328,8 @@ export default async (req, res) => {
|
|||||||
.create({ ...value, profile_id: shippingProfile.id })
|
.create({ ...value, profile_id: shippingProfile.id })
|
||||||
|
|
||||||
if (variants) {
|
if (variants) {
|
||||||
|
for (const [i, variant] of variants.entries()) variant.variant_rank = i
|
||||||
|
|
||||||
const optionIds = value.options.map(
|
const optionIds = value.options.map(
|
||||||
o => newProduct.options.find(newO => newO.title === o.title).id
|
o => newProduct.options.find(newO => newO.title === o.title).id
|
||||||
)
|
)
|
||||||
@@ -341,6 +343,7 @@ export default async (req, res) => {
|
|||||||
option_id: optionIds[index],
|
option_id: optionIds[index],
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
|
|
||||||
await productVariantService
|
await productVariantService
|
||||||
.withTransaction(manager)
|
.withTransaction(manager)
|
||||||
.create(newProduct.id, variant)
|
.create(newProduct.id, variant)
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||||
|
|
||||||
|
export class RankColumnWithDefaultValue1631104895519 implements MigrationInterface {
|
||||||
|
name = 'RankColumnWithDefaultValue1631104895519'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "product_variant" ADD "variant_rank" integer DEFAULT '0'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "product_option_value" DROP CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "product_option_value" ADD CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE cascade ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "money_amount" DROP CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "money_amount" ADD CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE cascade ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "product_variant" DROP COLUMN "variant_rank"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "product_option_value" DROP CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "product_option_value" ADD CONSTRAINT "FK_7234ed737ff4eb1b6ae6e6d7b01" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "money_amount" DROP CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "money_amount" ADD CONSTRAINT "FK_17a06d728e4cfbc5bd2ddb70af0" FOREIGN KEY ("variant_id") REFERENCES "product_variant"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -44,7 +44,11 @@ export class MoneyAmount {
|
|||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
variant_id: string
|
variant_id: string
|
||||||
|
|
||||||
@ManyToOne(() => ProductVariant)
|
@ManyToOne(
|
||||||
|
() => ProductVariant,
|
||||||
|
variant => variant.prices,
|
||||||
|
{ onDelete: "cascade" }
|
||||||
|
)
|
||||||
@JoinColumn({ name: "variant_id" })
|
@JoinColumn({ name: "variant_id" })
|
||||||
variant: ProductVariant
|
variant: ProductVariant
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ export class ProductOptionValue {
|
|||||||
|
|
||||||
@ManyToOne(
|
@ManyToOne(
|
||||||
() => ProductVariant,
|
() => ProductVariant,
|
||||||
variant => variant.options
|
variant => variant.options,
|
||||||
|
{ onDelete: "cascade" }
|
||||||
)
|
)
|
||||||
@JoinColumn({ name: "variant_id" })
|
@JoinColumn({ name: "variant_id" })
|
||||||
variant: ProductVariant
|
variant: ProductVariant
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export class ProductVariant {
|
|||||||
@OneToMany(
|
@OneToMany(
|
||||||
() => MoneyAmount,
|
() => MoneyAmount,
|
||||||
ma => ma.variant,
|
ma => ma.variant,
|
||||||
{ cascade: true }
|
{ cascade: true, onDelete: "CASCADE" }
|
||||||
)
|
)
|
||||||
prices: MoneyAmount[]
|
prices: MoneyAmount[]
|
||||||
|
|
||||||
@@ -63,6 +63,9 @@ export class ProductVariant {
|
|||||||
@Index({ unique: true, where: "deleted_at IS NOT NULL" })
|
@Index({ unique: true, where: "deleted_at IS NOT NULL" })
|
||||||
upc: string
|
upc: string
|
||||||
|
|
||||||
|
@Column({ nullable: true, default: 0, select:false })
|
||||||
|
variant_rank: number
|
||||||
|
|
||||||
@Column({ type: "int" })
|
@Column({ type: "int" })
|
||||||
inventory_quantity: number
|
inventory_quantity: number
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class ProductRepository extends Repository<Product> {
|
|||||||
}
|
}
|
||||||
const entitiesIds = entities.map(({ id }) => id)
|
const entitiesIds = entities.map(({ id }) => id)
|
||||||
|
|
||||||
const groupedRelations = {}
|
const groupedRelations : { [toplevel: string]: string[]} = {}
|
||||||
for (const rel of relations) {
|
for (const rel of relations) {
|
||||||
const [topLevel] = rel.split(".")
|
const [topLevel] = rel.split(".")
|
||||||
if (groupedRelations[topLevel]) {
|
if (groupedRelations[topLevel]) {
|
||||||
@@ -30,13 +30,33 @@ export class ProductRepository extends Repository<Product> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const entitiesIdsWithRelations = await Promise.all(
|
const entitiesIdsWithRelations = await Promise.all(
|
||||||
Object.entries(groupedRelations).map(([_, rels]) => {
|
Object.entries(groupedRelations).map(([toplevel, rels]) => {
|
||||||
return this.findByIds(entitiesIds, {
|
let querybuilder = this.createQueryBuilder("products")
|
||||||
select: ["id"],
|
|
||||||
relations: rels as string[],
|
if (toplevel === "variants") {
|
||||||
|
querybuilder = querybuilder.leftJoinAndSelect(`products.${toplevel}`, toplevel, "variants.deleted_at IS NULL")
|
||||||
|
.orderBy({
|
||||||
|
"variants.variant_rank": "ASC",
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
querybuilder = querybuilder.leftJoinAndSelect(`products.${toplevel}`, toplevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const rel of rels) {
|
||||||
|
const [_, rest] = rel.split(".")
|
||||||
|
if (!rest) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Regex matches all '.' except the rightmost
|
||||||
|
querybuilder = querybuilder.leftJoinAndSelect(rel.replace(/\.(?=[^.]*\.)/g,"__"), rel.replace(".", "__"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return querybuilder
|
||||||
|
.where("products.deleted_at IS NULL AND products.id IN (:...entitiesIds)", { entitiesIds })
|
||||||
|
.getMany();
|
||||||
})
|
})
|
||||||
).then(flatten)
|
).then(flatten)
|
||||||
|
|
||||||
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
||||||
|
|
||||||
const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id")
|
const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id")
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ export const ProductServiceMock = {
|
|||||||
if (data.title === "Test Product") {
|
if (data.title === "Test Product") {
|
||||||
return Promise.resolve(products.product1)
|
return Promise.resolve(products.product1)
|
||||||
}
|
}
|
||||||
|
if (data.title === "Test Product with variants") {
|
||||||
|
return Promise.resolve(products.productWithOptions)
|
||||||
|
}
|
||||||
return Promise.resolve({ ...data })
|
return Promise.resolve({ ...data })
|
||||||
}),
|
}),
|
||||||
count: jest.fn().mockReturnValue(4),
|
count: jest.fn().mockReturnValue(4),
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ describe("ProductVariantService", () => {
|
|||||||
expect(productVariantRepository.create).toHaveBeenCalledWith({
|
expect(productVariantRepository.create).toHaveBeenCalledWith({
|
||||||
id: IdMap.getId("v2"),
|
id: IdMap.getId("v2"),
|
||||||
product_id: IdMap.getId("ironman"),
|
product_id: IdMap.getId("ironman"),
|
||||||
|
variant_rank: 1,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
id: IdMap.getId("test"),
|
id: IdMap.getId("test"),
|
||||||
|
|||||||
@@ -11,9 +11,26 @@ const eventBusService = {
|
|||||||
describe("ProductService", () => {
|
describe("ProductService", () => {
|
||||||
describe("retrieve", () => {
|
describe("retrieve", () => {
|
||||||
const productRepo = MockRepository({
|
const productRepo = MockRepository({
|
||||||
findOneWithRelations: () =>
|
findOneWithRelations: (rels, query) => {
|
||||||
Promise.resolve({ id: IdMap.getId("ironman") }),
|
if (query.where.id === "test id with variants") {
|
||||||
|
return {
|
||||||
|
id: "test id with variants",
|
||||||
|
variants: [
|
||||||
|
{ id: "test_321", title: "Green" },
|
||||||
|
{ id: "test_123", title: "Blue" },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (query.where.id === "test id one variant") {
|
||||||
|
return {
|
||||||
|
id: "test id one variant",
|
||||||
|
variants: [{ id: "test_123", title: "Blue" }],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve({ id: IdMap.getId("ironman") })
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
manager: MockManager,
|
manager: MockManager,
|
||||||
productRepository: productRepo,
|
productRepository: productRepo,
|
||||||
@@ -37,11 +54,12 @@ describe("ProductService", () => {
|
|||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
const productRepository = MockRepository({
|
const productRepository = MockRepository({
|
||||||
create: () => ({
|
create: product => ({
|
||||||
id: IdMap.getId("ironman"),
|
id: IdMap.getId("ironman"),
|
||||||
title: "Suit",
|
title: "Suit",
|
||||||
options: [],
|
options: [],
|
||||||
collection: { id: IdMap.getId("cat"), title: "Suits" },
|
collection: { id: IdMap.getId("cat"), title: "Suits" },
|
||||||
|
variants: product.variants,
|
||||||
}),
|
}),
|
||||||
findOneWithRelations: () => ({
|
findOneWithRelations: () => ({
|
||||||
id: IdMap.getId("ironman"),
|
id: IdMap.getId("ironman"),
|
||||||
@@ -97,6 +115,16 @@ describe("ProductService", () => {
|
|||||||
options: [],
|
options: [],
|
||||||
tags: [{ value: "title" }, { value: "title2" }],
|
tags: [{ value: "title" }, { value: "title2" }],
|
||||||
type: "type-1",
|
type: "type-1",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: "test1",
|
||||||
|
title: "green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test2",
|
||||||
|
title: "blue",
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(eventBusService.emit).toHaveBeenCalledTimes(1)
|
expect(eventBusService.emit).toHaveBeenCalledTimes(1)
|
||||||
@@ -108,6 +136,16 @@ describe("ProductService", () => {
|
|||||||
expect(productRepository.create).toHaveBeenCalledTimes(1)
|
expect(productRepository.create).toHaveBeenCalledTimes(1)
|
||||||
expect(productRepository.create).toHaveBeenCalledWith({
|
expect(productRepository.create).toHaveBeenCalledWith({
|
||||||
title: "Suit",
|
title: "Suit",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: "test1",
|
||||||
|
title: "green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test2",
|
||||||
|
title: "blue",
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(productTagRepository.findOne).toHaveBeenCalledTimes(2)
|
expect(productTagRepository.findOne).toHaveBeenCalledTimes(2)
|
||||||
@@ -124,14 +162,30 @@ describe("ProductService", () => {
|
|||||||
title: "Suit",
|
title: "Suit",
|
||||||
options: [],
|
options: [],
|
||||||
tags: [
|
tags: [
|
||||||
{ id: "tag-1", value: "title" },
|
{
|
||||||
{ id: "tag-2", value: "title2" },
|
id: "tag-1",
|
||||||
|
value: "title",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tag-2",
|
||||||
|
value: "title2",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
type_id: "type",
|
type_id: "type",
|
||||||
collection: {
|
collection: {
|
||||||
id: IdMap.getId("cat"),
|
id: IdMap.getId("cat"),
|
||||||
title: "Suits",
|
title: "Suits",
|
||||||
},
|
},
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: "test1",
|
||||||
|
title: "green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "test2",
|
||||||
|
title: "blue",
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -148,6 +202,15 @@ describe("ProductService", () => {
|
|||||||
if (query.where.id === "123") {
|
if (query.where.id === "123") {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
if (query.where.id === "ranking test") {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "ranking test",
|
||||||
|
variants: [
|
||||||
|
{ id: "test_321", title: "Greener", variant_rank: 1 },
|
||||||
|
{ id: "test_123", title: "Blueer", variant_rank: 0 },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
return Promise.resolve({ id: IdMap.getId("ironman") })
|
return Promise.resolve({ id: IdMap.getId("ironman") })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -165,7 +228,12 @@ describe("ProductService", () => {
|
|||||||
withTransaction: function() {
|
withTransaction: function() {
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
update: () => Promise.resolve(),
|
update: (variant, update) => {
|
||||||
|
if (variant.id) {
|
||||||
|
return update
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const productTagRepository = MockRepository({
|
const productTagRepository = MockRepository({
|
||||||
@@ -248,6 +316,30 @@ describe("ProductService", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("successfully updates variant ranking", async () => {
|
||||||
|
await productService.update("ranking test", {
|
||||||
|
variants: [
|
||||||
|
{ id: "test_321", title: "Greener", variant_rank: 1 },
|
||||||
|
{ id: "test_123", title: "Blueer", variant_rank: 0 },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(eventBusService.emit).toHaveBeenCalledTimes(1)
|
||||||
|
expect(eventBusService.emit).toHaveBeenCalledWith(
|
||||||
|
"product.updated",
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(productRepository.save).toHaveBeenCalledTimes(1)
|
||||||
|
expect(productRepository.save).toHaveBeenCalledWith({
|
||||||
|
id: "ranking test",
|
||||||
|
variants: [
|
||||||
|
{ id: "test_321", title: "Greener", variant_rank: 0 },
|
||||||
|
{ id: "test_123", title: "Blueer", variant_rank: 1 },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("successfully updates tags", async () => {
|
it("successfully updates tags", async () => {
|
||||||
await productService.update(IdMap.getId("ironman"), {
|
await productService.update(IdMap.getId("ironman"), {
|
||||||
tags: [
|
tags: [
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ class ProductVariantService extends BaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!rest.variant_rank) {
|
||||||
|
rest.variant_rank = product.variants.length
|
||||||
|
}
|
||||||
|
|
||||||
const toCreate = {
|
const toCreate = {
|
||||||
...rest,
|
...rest,
|
||||||
product_id: product.id,
|
product_id: product.id,
|
||||||
|
|||||||
@@ -410,7 +410,9 @@ class ProductService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newVariants = []
|
const newVariants = []
|
||||||
for (const newVariant of variants) {
|
for (const [i, newVariant] of variants.entries()) {
|
||||||
|
newVariant.variant_rank = i
|
||||||
|
|
||||||
if (newVariant.id) {
|
if (newVariant.id) {
|
||||||
const variant = product.variants.find(v => v.id === newVariant.id)
|
const variant = product.variants.find(v => v.id === newVariant.id)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user