chore(): Reorganize modules (#7210)
**What** Move all modules to the modules directory
This commit is contained in:
committed by
GitHub
parent
7a351eef09
commit
4eae25e1ef
@@ -0,0 +1,52 @@
|
||||
import { Context } from "@medusajs/types"
|
||||
import { DALUtils } from "@medusajs/utils"
|
||||
|
||||
class CustomRepository extends DALUtils.MikroOrmBaseRepository {
|
||||
constructor({ manager }) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
}
|
||||
|
||||
find = jest.fn().mockImplementation(async () => [])
|
||||
findAndCount = jest.fn().mockImplementation(async () => [])
|
||||
create = jest.fn()
|
||||
update = jest.fn()
|
||||
delete = jest.fn()
|
||||
softDelete = jest.fn()
|
||||
restore = jest.fn()
|
||||
|
||||
async transaction<TManager = unknown>(
|
||||
task: (transactionManager: TManager) => Promise<any>,
|
||||
options: {
|
||||
isolationLevel?: string
|
||||
enableNestedTransactions?: boolean
|
||||
transaction?: TManager
|
||||
} = {}
|
||||
): Promise<any> {
|
||||
return super.transaction(task, options)
|
||||
}
|
||||
|
||||
getActiveManager<TManager = unknown>({
|
||||
transactionManager,
|
||||
manager,
|
||||
}: Context = {}): TManager {
|
||||
return super.getActiveManager({ transactionManager, manager })
|
||||
}
|
||||
|
||||
getFreshManager<TManager = unknown>(): TManager {
|
||||
return super.getFreshManager()
|
||||
}
|
||||
|
||||
async serialize<TOutput extends object | object[]>(
|
||||
data: any,
|
||||
options?: any
|
||||
): Promise<TOutput> {
|
||||
return super.serialize(data, options)
|
||||
}
|
||||
}
|
||||
|
||||
export class ProductRepository extends CustomRepository {}
|
||||
export class ProductTagRepository extends CustomRepository {}
|
||||
export class ProductCollectionRepository extends CustomRepository {}
|
||||
export class ProductVariantRepository extends CustomRepository {}
|
||||
export class ProductCategoryRepository extends CustomRepository {}
|
||||
@@ -0,0 +1,191 @@
|
||||
export const productCategoriesData = [
|
||||
{
|
||||
id: "category-0",
|
||||
name: "category 0",
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
id: "category-1",
|
||||
name: "category 1",
|
||||
parent_category_id: "category-0",
|
||||
},
|
||||
{
|
||||
id: "category-1-a",
|
||||
name: "category 1 a",
|
||||
parent_category_id: "category-1",
|
||||
},
|
||||
{
|
||||
id: "category-1-b",
|
||||
name: "category 1 b",
|
||||
parent_category_id: "category-1",
|
||||
is_internal: true,
|
||||
},
|
||||
{
|
||||
id: "category-1-b-1",
|
||||
name: "category 1 b 1",
|
||||
parent_category_id: "category-1-b",
|
||||
},
|
||||
]
|
||||
|
||||
export const productCategoriesRankData = [
|
||||
{
|
||||
id: "category-0-0",
|
||||
name: "category 0 0",
|
||||
parent_category_id: null,
|
||||
rank: 0,
|
||||
},
|
||||
{
|
||||
id: "category-0-1",
|
||||
name: "category 0 1",
|
||||
parent_category_id: null,
|
||||
rank: 1,
|
||||
},
|
||||
{
|
||||
id: "category-0-2",
|
||||
name: "category 0 2",
|
||||
parent_category_id: null,
|
||||
rank: 2,
|
||||
},
|
||||
{
|
||||
id: "category-0-0-0",
|
||||
name: "category 0 0-0",
|
||||
parent_category_id: "category-0-0",
|
||||
rank: 0,
|
||||
},
|
||||
{
|
||||
id: "category-0-0-1",
|
||||
name: "category 0 0-1",
|
||||
parent_category_id: "category-0-0",
|
||||
rank: 1,
|
||||
},
|
||||
{
|
||||
id: "category-0-0-2",
|
||||
name: "category 0 0-2",
|
||||
parent_category_id: "category-0-0",
|
||||
rank: 2,
|
||||
},
|
||||
]
|
||||
|
||||
export const eletronicsCategoriesData = eval(`[
|
||||
{
|
||||
id: "electronics",
|
||||
name: "Electronics",
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
id: "computers",
|
||||
name: "Computers & Accessories",
|
||||
parent_category_id: "electronics",
|
||||
},
|
||||
{
|
||||
id: "desktops",
|
||||
name: "Desktops",
|
||||
parent_category_id: "computers",
|
||||
},
|
||||
{
|
||||
id: "gaming-desktops",
|
||||
name: "Gaming Desktops",
|
||||
parent_category_id: "desktops",
|
||||
},
|
||||
{
|
||||
id: "office-desktops",
|
||||
name: "Office Desktops",
|
||||
parent_category_id: "desktops",
|
||||
},
|
||||
{
|
||||
id: "laptops",
|
||||
name: "Laptops",
|
||||
parent_category_id: "computers",
|
||||
},
|
||||
{
|
||||
id: "gaming-laptops",
|
||||
name: "Gaming Laptops",
|
||||
parent_category_id: "laptops",
|
||||
},
|
||||
{
|
||||
id: "budget-gaming",
|
||||
name: "Budget Gaming Laptops",
|
||||
parent_category_id: "gaming-laptops",
|
||||
},
|
||||
{
|
||||
id: "high-performance",
|
||||
name: "High Performance Gaming Laptops",
|
||||
parent_category_id: "gaming-laptops",
|
||||
},
|
||||
{
|
||||
id: "vr-ready",
|
||||
name: "VR-Ready High Performance Gaming Laptops",
|
||||
parent_category_id: "high-performance",
|
||||
},
|
||||
{
|
||||
id: "4k-gaming",
|
||||
name: "4K Gaming Laptops",
|
||||
parent_category_id: "high-performance",
|
||||
},
|
||||
{
|
||||
id: "ultrabooks",
|
||||
name: "Ultrabooks",
|
||||
parent_category_id: "laptops",
|
||||
},
|
||||
{
|
||||
id: "thin-light",
|
||||
name: "Thin & Light Ultrabooks",
|
||||
parent_category_id: "ultrabooks",
|
||||
},
|
||||
{
|
||||
id: "convertible-ultrabooks",
|
||||
name: "Convertible Ultrabooks",
|
||||
parent_category_id: "ultrabooks",
|
||||
},
|
||||
{
|
||||
id: "touchscreen-ultrabooks",
|
||||
name: "Touchscreen Ultrabooks",
|
||||
parent_category_id: "convertible-ultrabooks",
|
||||
},
|
||||
{
|
||||
id: "detachable-ultrabooks",
|
||||
name: "Detachable Ultrabooks",
|
||||
parent_category_id: "convertible-ultrabooks",
|
||||
},
|
||||
|
||||
{
|
||||
id: "mobile",
|
||||
name: "Mobile Phones & Accessories",
|
||||
parent_category_id: "electronics",
|
||||
},
|
||||
{
|
||||
id: "smartphones",
|
||||
name: "Smartphones",
|
||||
parent_category_id: "mobile",
|
||||
},
|
||||
{
|
||||
id: "android-phones",
|
||||
name: "Android Phones",
|
||||
parent_category_id: "smartphones",
|
||||
},
|
||||
{
|
||||
id: "flagship-phones",
|
||||
name: "Flagship Smartphones",
|
||||
parent_category_id: "android-phones",
|
||||
},
|
||||
{
|
||||
id: "budget-phones",
|
||||
name: "Budget Smartphones",
|
||||
parent_category_id: "android-phones",
|
||||
},
|
||||
{
|
||||
id: "iphones",
|
||||
name: "iPhones",
|
||||
parent_category_id: "smartphones",
|
||||
},
|
||||
{
|
||||
id: "pro-phones",
|
||||
name: "Pro Models",
|
||||
parent_category_id: "iphones",
|
||||
},
|
||||
{
|
||||
id: "mini-phones",
|
||||
name: "Mini Models",
|
||||
parent_category_id: "iphones",
|
||||
},
|
||||
]`)
|
||||
@@ -0,0 +1,31 @@
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { ProductCategory } from "@models"
|
||||
|
||||
export async function createProductCategories(
|
||||
manager: SqlEntityManager,
|
||||
categoriesData: any[]
|
||||
): Promise<ProductCategory[]> {
|
||||
const categories: ProductCategory[] = []
|
||||
|
||||
for (let categoryData of categoriesData) {
|
||||
let categoryDataClone = { ...categoryData }
|
||||
let parentCategory: ProductCategory | null = null
|
||||
const parentCategoryId = categoryDataClone.parent_category_id as string
|
||||
delete categoryDataClone.parent_category_id
|
||||
|
||||
if (parentCategoryId) {
|
||||
parentCategory = await manager.findOne(ProductCategory, parentCategoryId)
|
||||
}
|
||||
|
||||
const category = manager.create(ProductCategory, {
|
||||
...categoryDataClone,
|
||||
parent_category: parentCategory,
|
||||
})
|
||||
|
||||
categories.push(category)
|
||||
}
|
||||
|
||||
await manager.persistAndFlush(categories)
|
||||
|
||||
return categories
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export const categoriesData = [
|
||||
{
|
||||
id: "category-0",
|
||||
name: "category 0",
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
id: "category-1",
|
||||
name: "category 1",
|
||||
parent_category_id: "category-0",
|
||||
},
|
||||
{
|
||||
id: "category-1-a",
|
||||
name: "category 1 a",
|
||||
parent_category_id: "category-1",
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,83 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { Image } from "@models"
|
||||
import faker from "faker"
|
||||
|
||||
export const buildProductOnlyData = ({
|
||||
title,
|
||||
description,
|
||||
subtitle,
|
||||
is_giftcard,
|
||||
discountable,
|
||||
thumbnail,
|
||||
images,
|
||||
status,
|
||||
}: {
|
||||
title?: string
|
||||
description?: string
|
||||
subtitle?: string
|
||||
is_giftcard?: boolean
|
||||
discountable?: boolean
|
||||
thumbnail?: string
|
||||
images?: { id?: string; url: string }[]
|
||||
status?: ProductTypes.ProductStatus
|
||||
} = {}) => {
|
||||
return {
|
||||
title: title ?? faker.commerce.productName(),
|
||||
description: description ?? faker.commerce.productName(),
|
||||
subtitle: subtitle ?? faker.commerce.productName(),
|
||||
is_giftcard: is_giftcard ?? false,
|
||||
discountable: discountable ?? true,
|
||||
thumbnail: thumbnail as string,
|
||||
status: status ?? ProductTypes.ProductStatus.PUBLISHED,
|
||||
images: (images ?? []) as Image[],
|
||||
}
|
||||
}
|
||||
|
||||
export const buildProductAndRelationsData = ({
|
||||
title,
|
||||
description,
|
||||
subtitle,
|
||||
is_giftcard,
|
||||
discountable,
|
||||
thumbnail,
|
||||
images,
|
||||
status,
|
||||
type_id,
|
||||
tags,
|
||||
options,
|
||||
variants,
|
||||
collection_id,
|
||||
}: Partial<ProductTypes.CreateProductDTO>) => {
|
||||
const defaultOptionTitle = "test-option"
|
||||
const defaultOptionValue = "test-value"
|
||||
|
||||
return {
|
||||
title: title ?? faker.commerce.productName(),
|
||||
description: description ?? faker.commerce.productName(),
|
||||
subtitle: subtitle ?? faker.commerce.productName(),
|
||||
is_giftcard: is_giftcard ?? false,
|
||||
discountable: discountable ?? true,
|
||||
thumbnail: thumbnail as string,
|
||||
status: status ?? ProductTypes.ProductStatus.PUBLISHED,
|
||||
images: (images ?? []) as Image[],
|
||||
type_id,
|
||||
tags: tags ?? [{ value: "tag-1" }],
|
||||
collection_id,
|
||||
options: options ?? [
|
||||
{
|
||||
title: defaultOptionTitle,
|
||||
values: [defaultOptionValue],
|
||||
},
|
||||
],
|
||||
variants: variants ?? [
|
||||
{
|
||||
title: faker.commerce.productName(),
|
||||
sku: faker.commerce.productName(),
|
||||
options: {
|
||||
[defaultOptionTitle]: defaultOptionValue,
|
||||
},
|
||||
},
|
||||
],
|
||||
// TODO: add categories, must be created first
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./categories"
|
||||
export * from "./products"
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
|
||||
export const productsData = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
tags: [
|
||||
{
|
||||
id: "tag-1",
|
||||
value: "France",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "product",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
tags: [
|
||||
{
|
||||
id: "tag-2",
|
||||
value: "Germany",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "test-3",
|
||||
title: "product 3",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
tags: [
|
||||
{
|
||||
id: "tag-3",
|
||||
value: "Netherlands",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export const variantsData = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "variant title",
|
||||
sku: "sku 1",
|
||||
product: { id: productsData[0].id },
|
||||
inventory_quantity: 10,
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "variant title",
|
||||
sku: "sku 2",
|
||||
product: { id: productsData[1].id },
|
||||
inventory_quantity: 10,
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,146 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import {
|
||||
Image,
|
||||
Product,
|
||||
ProductCategory,
|
||||
ProductCollection,
|
||||
ProductType,
|
||||
ProductVariant,
|
||||
} from "@models"
|
||||
|
||||
import ProductOption from "../../../src/models/product-option"
|
||||
|
||||
export * from "./data/create-product"
|
||||
|
||||
export async function createProductAndTags(
|
||||
manager: SqlEntityManager,
|
||||
data: {
|
||||
id?: string
|
||||
title: string
|
||||
status: ProductTypes.ProductStatus
|
||||
tags?: { id: string; value: string }[]
|
||||
collection_id?: string
|
||||
}[]
|
||||
) {
|
||||
const products: any[] = data.map((productData) => {
|
||||
return manager.create(Product, productData)
|
||||
})
|
||||
|
||||
await manager.persistAndFlush(products)
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
export async function createProductAndTypes(
|
||||
manager: SqlEntityManager,
|
||||
data: {
|
||||
id?: string
|
||||
title: string
|
||||
status: ProductTypes.ProductStatus
|
||||
type?: { id: string; value: string }
|
||||
}[]
|
||||
) {
|
||||
const products: any[] = data.map((productData) => {
|
||||
return manager.create(Product, productData)
|
||||
})
|
||||
|
||||
await manager.persistAndFlush(products)
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
export async function createProductVariants(
|
||||
manager: SqlEntityManager,
|
||||
data: any[]
|
||||
) {
|
||||
const variants: any[] = data.map((variantsData) => {
|
||||
return manager.create(ProductVariant, variantsData)
|
||||
})
|
||||
|
||||
await manager.persistAndFlush(variants)
|
||||
|
||||
return variants
|
||||
}
|
||||
|
||||
export async function createCollections(
|
||||
manager: SqlEntityManager,
|
||||
collectionData: {
|
||||
id?: string
|
||||
title: string
|
||||
handle?: string
|
||||
}[]
|
||||
) {
|
||||
const collections: any[] = collectionData.map((collectionData) => {
|
||||
return manager.create(ProductCollection, collectionData)
|
||||
})
|
||||
|
||||
await manager.persistAndFlush(collections)
|
||||
|
||||
return collections
|
||||
}
|
||||
|
||||
export async function createTypes(
|
||||
manager: SqlEntityManager,
|
||||
typesData: {
|
||||
id?: string
|
||||
value: string
|
||||
}[]
|
||||
) {
|
||||
const types: any[] = typesData.map((typesData) => {
|
||||
return manager.create(ProductType, typesData)
|
||||
})
|
||||
|
||||
await manager.persistAndFlush(types)
|
||||
|
||||
return types
|
||||
}
|
||||
|
||||
export async function createOptions(
|
||||
manager: SqlEntityManager,
|
||||
optionsData: {
|
||||
id?: string
|
||||
product: { id: string }
|
||||
title: string
|
||||
value?: string
|
||||
values?: {
|
||||
id?: string
|
||||
value: string
|
||||
variant?: { id: string } & any
|
||||
}[]
|
||||
variant?: { id: string } & any
|
||||
}[]
|
||||
) {
|
||||
const options: any[] = optionsData.map((option) => {
|
||||
return manager.create(ProductOption, option)
|
||||
})
|
||||
|
||||
await manager.persistAndFlush(options)
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
export async function createImages(
|
||||
manager: SqlEntityManager,
|
||||
imagesData: string[]
|
||||
) {
|
||||
const images: any[] = imagesData.map((img) => {
|
||||
return manager.create(Image, { url: img })
|
||||
})
|
||||
|
||||
await manager.persistAndFlush(images)
|
||||
|
||||
return images
|
||||
}
|
||||
|
||||
export async function assignCategoriesToProduct(
|
||||
manager: SqlEntityManager,
|
||||
product: Product,
|
||||
categories: ProductCategory[]
|
||||
) {
|
||||
product.categories.add(categories)
|
||||
|
||||
await manager.persistAndFlush(product)
|
||||
|
||||
return product
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import faker from "faker"
|
||||
|
||||
export const buildProductVariantOnlyData = ({
|
||||
title,
|
||||
sku,
|
||||
barcode,
|
||||
ean,
|
||||
upc,
|
||||
allow_backorder,
|
||||
inventory_quantity,
|
||||
manage_inventory,
|
||||
hs_code,
|
||||
origin_country,
|
||||
mid_code,
|
||||
material,
|
||||
weight,
|
||||
length,
|
||||
height,
|
||||
width,
|
||||
options,
|
||||
metadata,
|
||||
}: Partial<ProductTypes.CreateProductVariantOnlyDTO>) => {
|
||||
return {
|
||||
title: title ?? faker.commerce.productName(),
|
||||
sku: sku ?? faker.commerce.productName(),
|
||||
barcode,
|
||||
ean,
|
||||
upc,
|
||||
allow_backorder,
|
||||
inventory_quantity,
|
||||
manage_inventory,
|
||||
hs_code,
|
||||
origin_country,
|
||||
mid_code,
|
||||
material,
|
||||
weight,
|
||||
length,
|
||||
height,
|
||||
width,
|
||||
options,
|
||||
metadata,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./data/create-variant"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,370 @@
|
||||
import { ProductCollectionService } from "@services"
|
||||
|
||||
import { createCollections } from "../../../__fixtures__/product"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
medusaApp,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("Product collection Service", () => {
|
||||
let service: ProductCollectionService
|
||||
|
||||
beforeEach(() => {
|
||||
service = medusaApp.modules["productService"].productCollectionService_
|
||||
})
|
||||
|
||||
describe("list", () => {
|
||||
const data = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
},
|
||||
{
|
||||
id: "test-3",
|
||||
title: "col 3 extra",
|
||||
},
|
||||
{
|
||||
id: "test-4",
|
||||
title: "col 4 extra",
|
||||
},
|
||||
]
|
||||
|
||||
beforeEach(async () => {
|
||||
await createCollections(MikroOrmWrapper.forkManager(), data)
|
||||
})
|
||||
|
||||
it("list product collections", async () => {
|
||||
const productCollectionResults = await service.list()
|
||||
|
||||
expect(productCollectionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-3",
|
||||
title: "col 3 extra",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-4",
|
||||
title: "col 4 extra",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("list product collections by id", async () => {
|
||||
const productCollectionResults = await service.list({
|
||||
id: data![0].id,
|
||||
})
|
||||
|
||||
expect(productCollectionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("list product collections by title matching string", async () => {
|
||||
const productCollectionResults = await service.list({
|
||||
title: "col 3 extra",
|
||||
})
|
||||
|
||||
expect(productCollectionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-3",
|
||||
title: "col 3 extra",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCount", () => {
|
||||
const data = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
},
|
||||
{
|
||||
id: "test-3",
|
||||
title: "col 3 extra",
|
||||
},
|
||||
{
|
||||
id: "test-4",
|
||||
title: "col 4 extra",
|
||||
},
|
||||
]
|
||||
|
||||
beforeEach(async () => {
|
||||
await createCollections(MikroOrmWrapper.forkManager(), data)
|
||||
})
|
||||
|
||||
it("should return all collections and count", async () => {
|
||||
const [productCollectionResults, count] = await service.listAndCount()
|
||||
const serialized = JSON.parse(
|
||||
JSON.stringify(productCollectionResults)
|
||||
)
|
||||
|
||||
expect(serialized).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-3",
|
||||
title: "col 3 extra",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-4",
|
||||
title: "col 4 extra",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return count and collections based on filter data", async () => {
|
||||
const [productCollectionResults, count] = await service.listAndCount({
|
||||
id: data![0].id,
|
||||
})
|
||||
const serialized = JSON.parse(
|
||||
JSON.stringify(productCollectionResults)
|
||||
)
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(serialized).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return count and collections based on config data", async () => {
|
||||
const [productCollectionResults, count] = await service.listAndCount(
|
||||
{},
|
||||
{
|
||||
relations: ["products"],
|
||||
select: ["title"],
|
||||
take: 1,
|
||||
skip: 1,
|
||||
}
|
||||
)
|
||||
const serialized = JSON.parse(
|
||||
JSON.stringify(productCollectionResults)
|
||||
)
|
||||
|
||||
expect(count).toEqual(4)
|
||||
expect(serialized).toEqual([
|
||||
{
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
handle: "col-2",
|
||||
products: [],
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
const collectionData = {
|
||||
id: "collection-1",
|
||||
title: "collection 1",
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await createCollections(MikroOrmWrapper.forkManager(), [
|
||||
collectionData,
|
||||
])
|
||||
})
|
||||
|
||||
it("should return collection for the given id", async () => {
|
||||
const productCollectionResults = await service.retrieve(
|
||||
collectionData.id
|
||||
)
|
||||
|
||||
expect(productCollectionResults).toEqual(
|
||||
expect.objectContaining({
|
||||
id: collectionData.id,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when collection with id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductCollection with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when an id is not provided", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve(undefined as unknown as string)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"productCollection - id must be defined"
|
||||
)
|
||||
})
|
||||
|
||||
it("should return collection based on config select param", async () => {
|
||||
const productCollectionResults = await service.retrieve(
|
||||
collectionData.id,
|
||||
{
|
||||
select: ["id", "title"],
|
||||
}
|
||||
)
|
||||
|
||||
const serialized = JSON.parse(
|
||||
JSON.stringify(productCollectionResults)
|
||||
)
|
||||
|
||||
expect(serialized).toEqual({
|
||||
id: collectionData.id,
|
||||
title: collectionData.title,
|
||||
handle: "collection-1",
|
||||
})
|
||||
})
|
||||
|
||||
it("should return collection based on config relation param", async () => {
|
||||
const productCollectionResults = await service.retrieve(
|
||||
collectionData.id,
|
||||
{
|
||||
select: ["id", "title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
const serialized = JSON.parse(
|
||||
JSON.stringify(productCollectionResults)
|
||||
)
|
||||
|
||||
expect(serialized).toEqual({
|
||||
id: collectionData.id,
|
||||
title: collectionData.title,
|
||||
handle: "collection-1",
|
||||
products: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("delete", () => {
|
||||
const collectionId = "collection-1"
|
||||
const collectionData = {
|
||||
id: collectionId,
|
||||
title: "collection 1",
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await createCollections(MikroOrmWrapper.forkManager(), [
|
||||
collectionData,
|
||||
])
|
||||
})
|
||||
|
||||
it("should delete the product collection given an ID successfully", async () => {
|
||||
await service.delete([collectionId])
|
||||
|
||||
const collections = await service.list({
|
||||
id: collectionId,
|
||||
})
|
||||
|
||||
expect(collections).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
const collectionId = "collection-1"
|
||||
const collectionData = {
|
||||
id: collectionId,
|
||||
title: "collection 1",
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await createCollections(MikroOrmWrapper.forkManager(), [
|
||||
collectionData,
|
||||
])
|
||||
})
|
||||
|
||||
it("should update the value of the collection successfully", async () => {
|
||||
await service.update([
|
||||
{
|
||||
id: collectionId,
|
||||
title: "New Collection",
|
||||
},
|
||||
])
|
||||
|
||||
const productCollection = await service.retrieve(collectionId)
|
||||
|
||||
expect(productCollection.title).toEqual("New Collection")
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.update([
|
||||
{
|
||||
id: "does-not-exist",
|
||||
title: "New Collection",
|
||||
},
|
||||
])
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
'ProductCollection with id "does-not-exist" not found'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
it("should create a collection successfully", async () => {
|
||||
await service.create([
|
||||
{
|
||||
title: "New Collection",
|
||||
},
|
||||
])
|
||||
|
||||
const [productCollection] = await service.list({
|
||||
title: "New Collection",
|
||||
})
|
||||
|
||||
expect(productCollection.title).toEqual("New Collection")
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,614 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||
import { Product, ProductCategory } from "@models"
|
||||
import {
|
||||
MockEventBusService,
|
||||
SuiteOptions,
|
||||
moduleIntegrationTestRunner,
|
||||
} from "medusa-test-utils"
|
||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||
import { productCategoriesRankData } from "../../../__fixtures__/product-category/data"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
injectedDependencies: {
|
||||
eventBusModuleService: new MockEventBusService(),
|
||||
},
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductModuleService product categories", () => {
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
let productCategoryOne: ProductCategory
|
||||
let productCategoryTwo: ProductCategory
|
||||
let productCategories: ProductCategory[]
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
productTwo = testManager.create(Product, {
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
const productCategoriesData = [
|
||||
{
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [productOne],
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
name: "category",
|
||||
products: [productTwo],
|
||||
},
|
||||
]
|
||||
|
||||
productCategories = await createProductCategories(
|
||||
testManager,
|
||||
productCategoriesData
|
||||
)
|
||||
|
||||
productCategoryOne = productCategories[0]
|
||||
productCategoryTwo = productCategories[1]
|
||||
|
||||
await testManager.persistAndFlush([
|
||||
productCategoryOne,
|
||||
productCategoryTwo,
|
||||
])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("listCategories", () => {
|
||||
it("should return categories queried by ID", async () => {
|
||||
const results = await service.listCategories({
|
||||
id: productCategoryOne.id,
|
||||
})
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return categories based on the options and filter parameter", async () => {
|
||||
let results = await service.listCategories(
|
||||
{
|
||||
id: productCategoryOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listCategories({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for categories", async () => {
|
||||
const results = await service.listCategories(
|
||||
{
|
||||
id: productCategoryOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "name", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCountCategories", () => {
|
||||
it("should return categories and count queried by ID", async () => {
|
||||
const results = await service.listAndCountCategories({
|
||||
id: productCategoryOne.id,
|
||||
})
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return categories and count based on the options and filter parameter", async () => {
|
||||
let results = await service.listAndCountCategories(
|
||||
{
|
||||
id: productCategoryOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listAndCountCategories({}, { take: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
|
||||
results = await service.listAndCountCategories(
|
||||
{},
|
||||
{ take: 1, skip: 1 }
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for categories", async () => {
|
||||
const results = await service.listAndCountCategories(
|
||||
{
|
||||
id: productCategoryOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "name", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveCategory", () => {
|
||||
it("should return the requested category", async () => {
|
||||
const result = await service.retrieveCategory(productCategoryOne.id, {
|
||||
select: ["id", "name"],
|
||||
})
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const result = await service.retrieveCategory(productCategoryOne.id, {
|
||||
select: ["id", "name", "products.title"],
|
||||
relations: ["products"],
|
||||
})
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a category with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieveCategory("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductCategory with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("createCategory", () => {
|
||||
it("should create a category successfully", async () => {
|
||||
await service.createCategory({
|
||||
name: "New Category",
|
||||
parent_category_id: productCategoryOne.id,
|
||||
})
|
||||
|
||||
const [productCategory] = await service.listCategories(
|
||||
{
|
||||
name: "New Category",
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategory).toEqual(
|
||||
expect.objectContaining({
|
||||
name: "New Category",
|
||||
rank: "0",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should emit events through event bus", async () => {
|
||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||
|
||||
const category = await service.createCategory({
|
||||
name: "New Category",
|
||||
parent_category_id: productCategoryOne.id,
|
||||
})
|
||||
|
||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||
expect(eventBusSpy).toHaveBeenCalledWith("product-category.created", {
|
||||
id: category.id,
|
||||
})
|
||||
})
|
||||
|
||||
it("should append rank from an existing category depending on parent", async () => {
|
||||
await service.createCategory({
|
||||
name: "New Category",
|
||||
parent_category_id: productCategoryOne.id,
|
||||
rank: 0,
|
||||
})
|
||||
|
||||
await service.createCategory({
|
||||
name: "New Category 2",
|
||||
parent_category_id: productCategoryOne.id,
|
||||
})
|
||||
|
||||
const [productCategoryNew] = await service.listCategories(
|
||||
{
|
||||
name: "New Category 2",
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryNew).toEqual(
|
||||
expect.objectContaining({
|
||||
name: "New Category 2",
|
||||
rank: "1",
|
||||
})
|
||||
)
|
||||
|
||||
await service.createCategory({
|
||||
name: "New Category 2.1",
|
||||
parent_category_id: productCategoryNew.id,
|
||||
})
|
||||
|
||||
const [productCategoryWithParent] = await service.listCategories(
|
||||
{
|
||||
name: "New Category 2.1",
|
||||
},
|
||||
{
|
||||
select: ["name", "rank", "parent_category_id"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryWithParent).toEqual(
|
||||
expect.objectContaining({
|
||||
name: "New Category 2.1",
|
||||
parent_category_id: productCategoryNew.id,
|
||||
rank: "0",
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateCategory", () => {
|
||||
let productCategoryZero
|
||||
let productCategoryOne
|
||||
let productCategoryTwo
|
||||
let productCategoryZeroZero
|
||||
let productCategoryZeroOne
|
||||
let productCategoryZeroTwo
|
||||
let categories
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
categories = await createProductCategories(
|
||||
testManager,
|
||||
productCategoriesRankData
|
||||
)
|
||||
|
||||
productCategoryZero = categories[0]
|
||||
productCategoryOne = categories[1]
|
||||
productCategoryTwo = categories[2]
|
||||
productCategoryZeroZero = categories[3]
|
||||
productCategoryZeroOne = categories[4]
|
||||
productCategoryZeroTwo = categories[5]
|
||||
})
|
||||
|
||||
it("should emit events through event bus", async () => {
|
||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||
await service.updateCategory(productCategoryZero.id, {
|
||||
name: "New Category",
|
||||
})
|
||||
|
||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||
expect(eventBusSpy).toHaveBeenCalledWith("product-category.updated", {
|
||||
id: productCategoryZero.id,
|
||||
})
|
||||
})
|
||||
|
||||
it("should update the name of the category successfully", async () => {
|
||||
await service.updateCategory(productCategoryZero.id, {
|
||||
name: "New Category",
|
||||
})
|
||||
|
||||
const productCategory = await service.retrieveCategory(
|
||||
productCategoryZero.id,
|
||||
{
|
||||
select: ["name"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategory.name).toEqual("New Category")
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.updateCategory("does-not-exist", {
|
||||
name: "New Category",
|
||||
})
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
`ProductCategory not found ({ id: 'does-not-exist' })`
|
||||
)
|
||||
})
|
||||
|
||||
it("should reorder rank successfully in the same parent", async () => {
|
||||
await service.updateCategory(productCategoryTwo.id, {
|
||||
rank: 0,
|
||||
})
|
||||
|
||||
const productCategories = await service.listCategories(
|
||||
{
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategories).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
rank: "0",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productCategoryZero.id,
|
||||
rank: "1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
rank: "2",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should reorder rank successfully when changing parent", async () => {
|
||||
await service.updateCategory(productCategoryTwo.id, {
|
||||
rank: 0,
|
||||
parent_category_id: productCategoryZero.id,
|
||||
})
|
||||
|
||||
const productCategories = await service.listCategories(
|
||||
{
|
||||
parent_category_id: productCategoryZero.id,
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategories).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
rank: "0",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productCategoryZeroZero.id,
|
||||
rank: "1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productCategoryZeroOne.id,
|
||||
rank: "2",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productCategoryZeroTwo.id,
|
||||
rank: "3",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should reorder rank successfully when changing parent and in first position", async () => {
|
||||
await service.updateCategory(productCategoryTwo.id, {
|
||||
rank: 0,
|
||||
parent_category_id: productCategoryZero.id,
|
||||
})
|
||||
|
||||
const productCategories = await service.listCategories(
|
||||
{
|
||||
parent_category_id: productCategoryZero.id,
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategories).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
rank: "0",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productCategoryZeroZero.id,
|
||||
rank: "1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productCategoryZeroOne.id,
|
||||
rank: "2",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productCategoryZeroTwo.id,
|
||||
rank: "3",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("deleteCategory", () => {
|
||||
let productCategoryZero
|
||||
let productCategoryOne
|
||||
let productCategoryTwo
|
||||
let categories
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
categories = await createProductCategories(
|
||||
testManager,
|
||||
productCategoriesRankData
|
||||
)
|
||||
|
||||
productCategoryZero = categories[0]
|
||||
productCategoryOne = categories[1]
|
||||
productCategoryTwo = categories[2]
|
||||
})
|
||||
|
||||
it("should emit events through event bus", async () => {
|
||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||
await service.deleteCategory(productCategoryOne.id)
|
||||
|
||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||
expect(eventBusSpy).toHaveBeenCalledWith("product-category.deleted", {
|
||||
id: productCategoryOne.id,
|
||||
})
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.deleteCategory("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
`ProductCategory not found ({ id: 'does-not-exist' })`
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when it has children", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.deleteCategory(productCategoryZero.id)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
`Deleting ProductCategory (category-0-0) with category children is not allowed`
|
||||
)
|
||||
})
|
||||
|
||||
it("should reorder siblings rank successfully on deleting", async () => {
|
||||
await service.deleteCategory(productCategoryOne.id)
|
||||
|
||||
const productCategories = await service.listCategories(
|
||||
{
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
select: ["id", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategories).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: productCategoryZero.id,
|
||||
rank: "0",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
rank: "1",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,440 @@
|
||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||
import { Product, ProductCollection } from "@models"
|
||||
import { MockEventBusService } from "medusa-test-utils"
|
||||
import { createCollections } from "../../../__fixtures__/product"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
injectedDependencies: {
|
||||
eventBusModuleService: new MockEventBusService(),
|
||||
},
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductModuleService product collections", () => {
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
let productCollectionOne: ProductCollection
|
||||
let productCollectionTwo: ProductCollection
|
||||
let productCollections: ProductCollection[]
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
productTwo = testManager.create(Product, {
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
const productCollectionsData = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
products: [productOne],
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "collection",
|
||||
products: [productTwo],
|
||||
},
|
||||
]
|
||||
|
||||
productCollections = await createCollections(
|
||||
testManager,
|
||||
productCollectionsData
|
||||
)
|
||||
|
||||
productCollectionOne = productCollections[0]
|
||||
productCollectionTwo = productCollections[1]
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("listCollections", () => {
|
||||
it("should return collections queried by ID", async () => {
|
||||
const results = await service.listCollections({
|
||||
id: productCollectionOne.id,
|
||||
})
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return collections based on the options and filter parameter", async () => {
|
||||
let results = await service.listCollections(
|
||||
{
|
||||
id: productCollectionOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listCollections({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for collections", async () => {
|
||||
const results = await service.listCollections(
|
||||
{
|
||||
id: productCollectionOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "title", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCountCollections", () => {
|
||||
it("should return collections and count queried by ID", async () => {
|
||||
const results = await service.listAndCountCollections({
|
||||
id: productCollectionOne.id,
|
||||
})
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return collections and count based on the options and filter parameter", async () => {
|
||||
let results = await service.listAndCountCollections(
|
||||
{
|
||||
id: productCollectionOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listAndCountCollections({}, { take: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
|
||||
results = await service.listAndCountCollections(
|
||||
{},
|
||||
{ take: 1, skip: 1 }
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productCollectionTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for collections", async () => {
|
||||
const results = await service.listAndCountCollections(
|
||||
{
|
||||
id: productCollectionOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "title", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveCollection", () => {
|
||||
it("should return the requested collection", async () => {
|
||||
const result = await service.retrieveCollection(
|
||||
productCollectionOne.id
|
||||
)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const result = await service.retrieveCollection(
|
||||
productCollectionOne.id,
|
||||
{
|
||||
select: ["id", "title", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "collection 1",
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a collection with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieveCollection("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductCollection with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("deleteCollections", () => {
|
||||
const collectionId = "test-1"
|
||||
|
||||
it("should delete the product collection given an ID successfully", async () => {
|
||||
await service.deleteCollections([collectionId])
|
||||
|
||||
const collections = await service.listCollections({
|
||||
id: collectionId,
|
||||
})
|
||||
|
||||
expect(collections).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should emit events through event bus", async () => {
|
||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||
await service.deleteCollections([collectionId])
|
||||
|
||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||
expect(eventBusSpy).toHaveBeenCalledWith([
|
||||
{
|
||||
eventName: "product-collection.deleted",
|
||||
data: { id: collectionId },
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateCollections", () => {
|
||||
const collectionId = "test-1"
|
||||
|
||||
it("should emit events through event bus", async () => {
|
||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||
|
||||
await service.upsertCollections([
|
||||
{
|
||||
id: collectionId,
|
||||
title: "New Collection",
|
||||
},
|
||||
])
|
||||
|
||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||
expect(eventBusSpy).toHaveBeenCalledWith([
|
||||
{
|
||||
eventName: "product-collection.updated",
|
||||
data: { id: collectionId },
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should update the value of the collection successfully", async () => {
|
||||
await service.upsertCollections([
|
||||
{
|
||||
id: collectionId,
|
||||
title: "New Collection",
|
||||
},
|
||||
])
|
||||
|
||||
const productCollection = await service.retrieveCollection(
|
||||
collectionId
|
||||
)
|
||||
|
||||
expect(productCollection.title).toEqual("New Collection")
|
||||
})
|
||||
|
||||
it("should add products to a collection successfully", async () => {
|
||||
await service.upsertCollections([
|
||||
{
|
||||
id: collectionId,
|
||||
product_ids: [productOne.id, productTwo.id],
|
||||
},
|
||||
])
|
||||
|
||||
const productCollection = await service.retrieveCollection(
|
||||
collectionId,
|
||||
{
|
||||
select: ["products.id"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCollection.products).toHaveLength(2)
|
||||
expect(productCollection).toEqual(
|
||||
expect.objectContaining({
|
||||
products: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: productOne.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productTwo.id,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.updateCollections("does-not-exist", {
|
||||
title: "New Collection",
|
||||
})
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductCollection with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("createCollections", () => {
|
||||
it("should create a collection successfully", async () => {
|
||||
const res = await service.createCollections([
|
||||
{
|
||||
title: "New Collection",
|
||||
},
|
||||
])
|
||||
|
||||
const [productCollection] = await service.listCollections({
|
||||
title: "New Collection",
|
||||
})
|
||||
|
||||
expect(productCollection.title).toEqual("New Collection")
|
||||
})
|
||||
|
||||
it("should create collection with products successfully", async () => {
|
||||
await service.createCollections([
|
||||
{
|
||||
title: "New Collection with products",
|
||||
handle: "new-collection-with-products",
|
||||
product_ids: [productOne.id, productTwo.id],
|
||||
},
|
||||
])
|
||||
|
||||
const [productCollection] = await service.listCollections(
|
||||
{
|
||||
handle: "new-collection-with-products",
|
||||
},
|
||||
{
|
||||
select: ["title", "handle", "products.id"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCollection).toEqual(
|
||||
expect.objectContaining({
|
||||
title: "New Collection with products",
|
||||
handle: "new-collection-with-products",
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: productOne.id,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: productTwo.id,
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should emit events through event bus", async () => {
|
||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||
|
||||
const collections = await service.createCollections([
|
||||
{
|
||||
title: "New Collection",
|
||||
},
|
||||
])
|
||||
|
||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||
expect(eventBusSpy).toHaveBeenCalledWith([
|
||||
{
|
||||
eventName: "product-collection.created",
|
||||
data: { id: collections[0].id },
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,312 @@
|
||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||
import { Product, ProductOption } from "@models"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductModuleService product options", () => {
|
||||
let optionOne: ProductOption
|
||||
let optionTwo: ProductOption
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
productTwo = testManager.create(Product, {
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
optionOne = testManager.create(ProductOption, {
|
||||
id: "option-1",
|
||||
title: "option 1",
|
||||
product: productOne,
|
||||
})
|
||||
|
||||
optionTwo = testManager.create(ProductOption, {
|
||||
id: "option-2",
|
||||
title: "option 1",
|
||||
product: productTwo,
|
||||
})
|
||||
|
||||
await testManager.persistAndFlush([optionOne, optionTwo])
|
||||
})
|
||||
|
||||
describe("listOptions", () => {
|
||||
it("should return options and count queried by ID", async () => {
|
||||
const options = await service.listOptions({
|
||||
id: optionOne.id,
|
||||
})
|
||||
|
||||
expect(options).toEqual([
|
||||
expect.objectContaining({
|
||||
id: optionOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return options and count based on the options and filter parameter", async () => {
|
||||
let options = await service.listOptions(
|
||||
{
|
||||
id: optionOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(options).toEqual([
|
||||
expect.objectContaining({
|
||||
id: optionOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
options = await service.listOptions({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(options).toEqual([
|
||||
expect.objectContaining({
|
||||
id: optionTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for options", async () => {
|
||||
const options = await service.listOptions(
|
||||
{
|
||||
id: optionOne.id,
|
||||
},
|
||||
{
|
||||
select: ["title", "product.id"],
|
||||
relations: ["product"],
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(options).toEqual([
|
||||
{
|
||||
id: optionOne.id,
|
||||
title: optionOne.title,
|
||||
product_id: productOne.id,
|
||||
product: {
|
||||
id: productOne.id,
|
||||
type_id: null,
|
||||
collection_id: null,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCountOptions", () => {
|
||||
it("should return options and count queried by ID", async () => {
|
||||
const [options, count] = await service.listAndCountOptions({
|
||||
id: optionOne.id,
|
||||
})
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(options).toEqual([
|
||||
expect.objectContaining({
|
||||
id: optionOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return options and count based on the options and filter parameter", async () => {
|
||||
let [options, count] = await service.listAndCountOptions(
|
||||
{
|
||||
id: optionOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(options).toEqual([
|
||||
expect.objectContaining({
|
||||
id: optionOne.id,
|
||||
}),
|
||||
])
|
||||
;[options, count] = await service.listAndCountOptions({}, { take: 1 })
|
||||
|
||||
expect(count).toEqual(2)
|
||||
;[options, count] = await service.listAndCountOptions(
|
||||
{},
|
||||
{ take: 1, skip: 1 }
|
||||
)
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(options).toEqual([
|
||||
expect.objectContaining({
|
||||
id: optionTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for options", async () => {
|
||||
const [options, count] = await service.listAndCountOptions(
|
||||
{
|
||||
id: optionOne.id,
|
||||
},
|
||||
{
|
||||
select: ["title", "product.id"],
|
||||
relations: ["product"],
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(options).toEqual([
|
||||
{
|
||||
id: optionOne.id,
|
||||
title: optionOne.title,
|
||||
product_id: productOne.id,
|
||||
product: {
|
||||
id: productOne.id,
|
||||
type_id: null,
|
||||
collection_id: null,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveOption", () => {
|
||||
it("should return the requested option", async () => {
|
||||
const option = await service.retrieveOption(optionOne.id)
|
||||
|
||||
expect(option).toEqual(
|
||||
expect.objectContaining({
|
||||
id: optionOne.id,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const option = await service.retrieveOption(optionOne.id, {
|
||||
select: ["id", "product.title"],
|
||||
relations: ["product"],
|
||||
})
|
||||
|
||||
expect(option).toEqual(
|
||||
expect.objectContaining({
|
||||
id: optionOne.id,
|
||||
product: {
|
||||
id: "product-1",
|
||||
handle: "product-1",
|
||||
title: "product 1",
|
||||
type_id: null,
|
||||
collection_id: null,
|
||||
},
|
||||
product_id: "product-1",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a option with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieveOption("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
`ProductOption with id: does-not-exist was not found`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("deleteOptions", () => {
|
||||
const optionId = "option-1"
|
||||
|
||||
it("should delete the product option given an ID successfully", async () => {
|
||||
await service.deleteOptions([optionId])
|
||||
|
||||
const options = await service.listOptions({
|
||||
id: optionId,
|
||||
})
|
||||
|
||||
expect(options).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateOptions", () => {
|
||||
const optionId = "option-1"
|
||||
|
||||
it("should update the title of the option successfully", async () => {
|
||||
await service.upsertOptions([
|
||||
{
|
||||
id: optionId,
|
||||
title: "new test",
|
||||
},
|
||||
])
|
||||
|
||||
const productOption = await service.retrieveOption(optionId)
|
||||
|
||||
expect(productOption.title).toEqual("new test")
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.updateOptions("does-not-exist", {})
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
`ProductOption with id: does-not-exist was not found`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("createOptions", () => {
|
||||
it("should create a option successfully", async () => {
|
||||
const res = await service.createOptions([
|
||||
{
|
||||
title: "test",
|
||||
values: [],
|
||||
product_id: productOne.id,
|
||||
},
|
||||
])
|
||||
|
||||
const [productOption] = await service.listOptions(
|
||||
{
|
||||
title: "test",
|
||||
},
|
||||
{
|
||||
select: ["id", "title", "product.id"],
|
||||
relations: ["product"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productOption).toEqual(
|
||||
expect.objectContaining({
|
||||
title: "test",
|
||||
product: expect.objectContaining({
|
||||
id: productOne.id,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,302 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||
import { Product, ProductTag } from "@models"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductModuleService product tags", () => {
|
||||
let tagOne: ProductTag
|
||||
let tagTwo: ProductTag
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
productTwo = testManager.create(Product, {
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
tagOne = testManager.create(ProductTag, {
|
||||
id: "tag-1",
|
||||
value: "tag 1",
|
||||
products: [productOne],
|
||||
})
|
||||
|
||||
tagTwo = testManager.create(ProductTag, {
|
||||
id: "tag-2",
|
||||
value: "tag",
|
||||
products: [productTwo],
|
||||
})
|
||||
|
||||
await testManager.persistAndFlush([tagOne, tagTwo])
|
||||
})
|
||||
|
||||
describe("listTags", () => {
|
||||
it("should return tags and count queried by ID", async () => {
|
||||
const tags = await service.listTags({
|
||||
id: tagOne.id,
|
||||
})
|
||||
|
||||
expect(tags).toEqual([
|
||||
expect.objectContaining({
|
||||
id: tagOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return tags and count based on the options and filter parameter", async () => {
|
||||
let tags = await service.listTags(
|
||||
{
|
||||
id: tagOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(tags).toEqual([
|
||||
expect.objectContaining({
|
||||
id: tagOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
tags = await service.listTags({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(tags).toEqual([
|
||||
expect.objectContaining({
|
||||
id: tagTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for tags", async () => {
|
||||
const tags = await service.listTags(
|
||||
{
|
||||
id: tagOne.id,
|
||||
},
|
||||
{
|
||||
select: ["value", "products.id"],
|
||||
relations: ["products"],
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(tags).toEqual([
|
||||
{
|
||||
id: tagOne.id,
|
||||
value: tagOne.value,
|
||||
products: [
|
||||
{
|
||||
collection_id: null,
|
||||
type_id: null,
|
||||
id: productOne.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCountTags", () => {
|
||||
it("should return tags and count queried by ID", async () => {
|
||||
const [tags, count] = await service.listAndCountTags({
|
||||
id: tagOne.id,
|
||||
})
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(tags).toEqual([
|
||||
expect.objectContaining({
|
||||
id: tagOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return tags and count based on the options and filter parameter", async () => {
|
||||
let [tags, count] = await service.listAndCountTags(
|
||||
{
|
||||
id: tagOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(tags).toEqual([
|
||||
expect.objectContaining({
|
||||
id: tagOne.id,
|
||||
}),
|
||||
])
|
||||
;[tags, count] = await service.listAndCountTags({}, { take: 1 })
|
||||
|
||||
expect(count).toEqual(2)
|
||||
;[tags, count] = await service.listAndCountTags(
|
||||
{},
|
||||
{ take: 1, skip: 1 }
|
||||
)
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(tags).toEqual([
|
||||
expect.objectContaining({
|
||||
id: tagTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for tags", async () => {
|
||||
const [tags, count] = await service.listAndCountTags(
|
||||
{
|
||||
id: tagOne.id,
|
||||
},
|
||||
{
|
||||
select: ["value", "products.id"],
|
||||
relations: ["products"],
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(tags).toEqual([
|
||||
{
|
||||
id: tagOne.id,
|
||||
value: tagOne.value,
|
||||
products: [
|
||||
{
|
||||
collection_id: null,
|
||||
type_id: null,
|
||||
id: productOne.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveTag", () => {
|
||||
it("should return the requested tag", async () => {
|
||||
const tag = await service.retrieveTag(tagOne.id)
|
||||
|
||||
expect(tag).toEqual(
|
||||
expect.objectContaining({
|
||||
id: tagOne.id,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const tag = await service.retrieveTag(tagOne.id, {
|
||||
select: ["id", "value", "products.title"],
|
||||
relations: ["products"],
|
||||
})
|
||||
|
||||
expect(tag).toEqual(
|
||||
expect.objectContaining({
|
||||
id: tagOne.id,
|
||||
value: tagOne.value,
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a tag with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieveTag("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductTag with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("deleteTags", () => {
|
||||
const tagId = "tag-1"
|
||||
|
||||
it("should delete the product tag given an ID successfully", async () => {
|
||||
await service.deleteTags([tagId])
|
||||
|
||||
const tags = await service.listTags({
|
||||
id: tagId,
|
||||
})
|
||||
|
||||
expect(tags).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateTags", () => {
|
||||
const tagId = "tag-1"
|
||||
|
||||
it("should update the value of the tag successfully", async () => {
|
||||
await service.updateTags([
|
||||
{
|
||||
id: tagId,
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
|
||||
const productTag = await service.retrieveTag(tagId)
|
||||
|
||||
expect(productTag.value).toEqual("UK")
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.updateTags([
|
||||
{
|
||||
id: "does-not-exist",
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
'ProductTag with id "does-not-exist" not found'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("createTags", () => {
|
||||
it("should create a tag successfully", async () => {
|
||||
const res = await service.createTags([
|
||||
{
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
|
||||
const productTag = await service.listTags({
|
||||
value: "UK",
|
||||
})
|
||||
|
||||
expect(productTag[0]?.value).toEqual("UK")
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,257 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { ProductType } from "@models"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductModuleService product types", () => {
|
||||
let typeOne: ProductType
|
||||
let typeTwo: ProductType
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
typeOne = testManager.create(ProductType, {
|
||||
id: "type-1",
|
||||
value: "type 1",
|
||||
})
|
||||
|
||||
typeTwo = testManager.create(ProductType, {
|
||||
id: "type-2",
|
||||
value: "type",
|
||||
})
|
||||
|
||||
await testManager.persistAndFlush([typeOne, typeTwo])
|
||||
})
|
||||
|
||||
describe("listTypes", () => {
|
||||
it("should return types and count queried by ID", async () => {
|
||||
const types = await service.listTypes({
|
||||
id: typeOne.id,
|
||||
})
|
||||
|
||||
expect(types).toEqual([
|
||||
expect.objectContaining({
|
||||
id: typeOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return types and count based on the options and filter parameter", async () => {
|
||||
let types = await service.listTypes(
|
||||
{
|
||||
id: typeOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(types).toEqual([
|
||||
expect.objectContaining({
|
||||
id: typeOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
types = await service.listTypes({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(types).toEqual([
|
||||
expect.objectContaining({
|
||||
id: typeTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields for types", async () => {
|
||||
const types = await service.listTypes(
|
||||
{
|
||||
id: typeOne.id,
|
||||
},
|
||||
{
|
||||
select: ["value"],
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(types).toEqual([
|
||||
{
|
||||
id: typeOne.id,
|
||||
value: typeOne.value,
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCountTypes", () => {
|
||||
it("should return types and count queried by ID", async () => {
|
||||
const [types, count] = await service.listAndCountTypes({
|
||||
id: typeOne.id,
|
||||
})
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(types).toEqual([
|
||||
expect.objectContaining({
|
||||
id: typeOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return types and count based on the options and filter parameter", async () => {
|
||||
let [types, count] = await service.listAndCountTypes(
|
||||
{
|
||||
id: typeOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(types).toEqual([
|
||||
expect.objectContaining({
|
||||
id: typeOne.id,
|
||||
}),
|
||||
])
|
||||
;[types, count] = await service.listAndCountTypes({}, { take: 1 })
|
||||
|
||||
expect(count).toEqual(2)
|
||||
;[types, count] = await service.listAndCountTypes(
|
||||
{},
|
||||
{ take: 1, skip: 1 }
|
||||
)
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(types).toEqual([
|
||||
expect.objectContaining({
|
||||
id: typeTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields for types", async () => {
|
||||
const [types, count] = await service.listAndCountTypes(
|
||||
{
|
||||
id: typeOne.id,
|
||||
},
|
||||
{
|
||||
select: ["value"],
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(types).toEqual([
|
||||
{
|
||||
id: typeOne.id,
|
||||
value: typeOne.value,
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveType", () => {
|
||||
it("should return the requested type", async () => {
|
||||
const type = await service.retrieveType(typeOne.id)
|
||||
|
||||
expect(type).toEqual(
|
||||
expect.objectContaining({
|
||||
id: typeOne.id,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const type = await service.retrieveType(typeOne.id, {
|
||||
select: ["id", "value"],
|
||||
})
|
||||
|
||||
expect(type).toEqual({
|
||||
id: typeOne.id,
|
||||
value: typeOne.value,
|
||||
})
|
||||
})
|
||||
|
||||
it("should throw an error when a type with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieveType("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductType with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("deleteTypes", () => {
|
||||
const typeId = "type-1"
|
||||
|
||||
it("should delete the product type given an ID successfully", async () => {
|
||||
await service.deleteTypes([typeId])
|
||||
|
||||
const types = await service.listTypes({
|
||||
id: typeId,
|
||||
})
|
||||
|
||||
expect(types).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateTypes", () => {
|
||||
const typeId = "type-1"
|
||||
|
||||
it("should update the value of the type successfully", async () => {
|
||||
await service.updateTypes(typeId, {
|
||||
value: "UK",
|
||||
})
|
||||
|
||||
const productType = await service.retrieveType(typeId)
|
||||
|
||||
expect(productType.value).toEqual("UK")
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.updateTypes("does-not-exist", {
|
||||
value: "UK",
|
||||
})
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductType with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("createTypes", () => {
|
||||
it("should create a type successfully", async () => {
|
||||
const res = await service.createTypes([
|
||||
{
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
|
||||
const productType = await service.listTypes({
|
||||
value: "UK",
|
||||
})
|
||||
|
||||
expect(productType[0]?.value).toEqual("UK")
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,273 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||
import { Product, ProductVariant } from "@models"
|
||||
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductModuleService product variants", () => {
|
||||
let variantOne: ProductVariant
|
||||
let variantTwo: ProductVariant
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
|
||||
beforeEach(async () => {
|
||||
productOne = await service.create({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
options: [
|
||||
{
|
||||
title: "size",
|
||||
values: ["large", "small"],
|
||||
},
|
||||
{
|
||||
title: "color",
|
||||
values: ["red", "blue"],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
productTwo = await service.create({
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
variantOne = await service.createVariants({
|
||||
id: "test-1",
|
||||
title: "variant 1",
|
||||
inventory_quantity: 10,
|
||||
product_id: productOne.id,
|
||||
options: { size: "large" },
|
||||
})
|
||||
|
||||
variantTwo = await service.createVariants({
|
||||
id: "test-2",
|
||||
title: "variant",
|
||||
inventory_quantity: 10,
|
||||
product_id: productTwo.id,
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCountVariants", () => {
|
||||
it("should return variants and count queried by ID", async () => {
|
||||
const results = await service.listAndCountVariants({
|
||||
id: variantOne.id,
|
||||
})
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: variantOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return variants and count based on the options and filter parameter", async () => {
|
||||
let results = await service.listAndCountVariants(
|
||||
{
|
||||
id: variantOne.id,
|
||||
},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: variantOne.id,
|
||||
}),
|
||||
])
|
||||
|
||||
results = await service.listAndCountVariants({}, { take: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
|
||||
results = await service.listAndCountVariants({}, { take: 1, skip: 1 })
|
||||
|
||||
expect(results[1]).toEqual(2)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: variantTwo.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return only requested fields and relations for variants", async () => {
|
||||
const results = await service.listAndCountVariants(
|
||||
{
|
||||
id: variantOne.id,
|
||||
},
|
||||
{
|
||||
select: ["id", "title", "product.title"] as any,
|
||||
relations: ["product"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results[1]).toEqual(1)
|
||||
expect(results[0]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "variant 1",
|
||||
// TODO: investigate why this is returning more than the expected results
|
||||
product: expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieveVariant", () => {
|
||||
it("should return the requested variant", async () => {
|
||||
const result = await service.retrieveVariant(variantOne.id)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "variant 1",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const result = await service.retrieveVariant(variantOne.id, {
|
||||
select: ["id", "title", "product.title"] as any,
|
||||
relations: ["product"],
|
||||
})
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
title: "variant 1",
|
||||
product: expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a variant with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieveVariant("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductVariant with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("updateVariants", () => {
|
||||
it("should update the title of the variant successfully", async () => {
|
||||
await service.upsertVariants([
|
||||
{
|
||||
id: variantOne.id,
|
||||
title: "new test",
|
||||
},
|
||||
])
|
||||
|
||||
const productVariant = await service.retrieveVariant(variantOne.id)
|
||||
expect(productVariant.title).toEqual("new test")
|
||||
})
|
||||
|
||||
it("should upsert the options of a variant successfully", async () => {
|
||||
await service.upsertVariants([
|
||||
{
|
||||
id: variantOne.id,
|
||||
options: { size: "small" },
|
||||
},
|
||||
])
|
||||
|
||||
const productVariant = await service.retrieveVariant(variantOne.id, {
|
||||
relations: ["options"],
|
||||
})
|
||||
expect(productVariant.options).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should do a partial update on the options of a variant successfully", async () => {
|
||||
await service.updateVariants(variantOne.id, {
|
||||
options: { size: "small", color: "red" },
|
||||
})
|
||||
|
||||
const productVariant = await service.retrieveVariant(variantOne.id, {
|
||||
relations: ["options"],
|
||||
})
|
||||
|
||||
expect(productVariant.options).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "red",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.updateVariants("does-not-exist", {})
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
`Cannot update non-existing variants with ids: does-not-exist`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("softDelete variant", () => {
|
||||
it("should soft delete a variant and its relations", async () => {
|
||||
const beforeDeletedVariants = await service.listVariants(
|
||||
{ id: variantOne.id },
|
||||
{
|
||||
relations: ["options"],
|
||||
}
|
||||
)
|
||||
|
||||
await service.softDeleteVariants([variantOne.id])
|
||||
const deletedVariants = await service.listVariants(
|
||||
{ id: variantOne.id },
|
||||
{
|
||||
relations: ["options"],
|
||||
withDeleted: true,
|
||||
}
|
||||
)
|
||||
|
||||
expect(deletedVariants).toHaveLength(1)
|
||||
expect(deletedVariants[0].deleted_at).not.toBeNull()
|
||||
|
||||
for (const variantOption of deletedVariants[0].options) {
|
||||
expect(variantOption?.deleted_at).toBeNull()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,869 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||
import { kebabCase } from "@medusajs/utils"
|
||||
import {
|
||||
Product,
|
||||
ProductCategory,
|
||||
ProductCollection,
|
||||
ProductType,
|
||||
ProductVariant,
|
||||
} from "@models"
|
||||
|
||||
import {
|
||||
MockEventBusService,
|
||||
moduleIntegrationTestRunner,
|
||||
SuiteOptions,
|
||||
} from "medusa-test-utils"
|
||||
import { createCollections, createTypes } from "../../../__fixtures__/product"
|
||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||
import { buildProductAndRelationsData } from "../../../__fixtures__/product/data/create-product"
|
||||
import { UpdateProductInput } from "@types"
|
||||
|
||||
jest.setTimeout(300000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
injectedDependencies: {
|
||||
eventBusModuleService: new MockEventBusService(),
|
||||
},
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductModuleService products", function () {
|
||||
let productCollectionOne: ProductCollection
|
||||
let productCollectionTwo: ProductCollection
|
||||
|
||||
const productCollectionsData = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
},
|
||||
]
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("update", function () {
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
let productCategoryOne: ProductCategory
|
||||
let productCategoryTwo: ProductCategory
|
||||
let productTypeOne: ProductType
|
||||
let productTypeTwo: ProductType
|
||||
let images = [{ url: "image-1" }]
|
||||
|
||||
const productCategoriesData = [
|
||||
{
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
name: "category 2",
|
||||
},
|
||||
]
|
||||
|
||||
const productTypesData = [
|
||||
{
|
||||
id: "type-1",
|
||||
value: "type 1",
|
||||
},
|
||||
{
|
||||
id: "type-2",
|
||||
value: "type 2",
|
||||
},
|
||||
]
|
||||
|
||||
const tagsData = [
|
||||
{
|
||||
id: "tag-1",
|
||||
value: "tag 1",
|
||||
},
|
||||
]
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = MikroOrmWrapper.forkManager()
|
||||
|
||||
const collections = await createCollections(
|
||||
testManager,
|
||||
productCollectionsData
|
||||
)
|
||||
|
||||
productCollectionOne = collections[0]
|
||||
productCollectionTwo = collections[1]
|
||||
|
||||
const types = await createTypes(testManager, productTypesData)
|
||||
|
||||
productTypeOne = types[0]
|
||||
productTypeTwo = types[1]
|
||||
|
||||
const categories = await createProductCategories(
|
||||
testManager,
|
||||
productCategoriesData
|
||||
)
|
||||
|
||||
productCategoryOne = categories[0]
|
||||
productCategoryTwo = categories[1]
|
||||
|
||||
productOne = service.create({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
variants: [
|
||||
{
|
||||
id: "variant-1",
|
||||
title: "variant 1",
|
||||
inventory_quantity: 10,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
productTwo = service.create({
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
categories: [{ id: productCategoryOne.id }],
|
||||
collection_id: productCollectionOne.id,
|
||||
tags: tagsData,
|
||||
options: [
|
||||
{
|
||||
title: "size",
|
||||
values: ["large", "small"],
|
||||
},
|
||||
{
|
||||
title: "color",
|
||||
values: ["red", "blue"],
|
||||
},
|
||||
],
|
||||
variants: [
|
||||
{
|
||||
id: "variant-2",
|
||||
title: "variant 2",
|
||||
inventory_quantity: 10,
|
||||
},
|
||||
{
|
||||
id: "variant-3",
|
||||
title: "variant 3",
|
||||
inventory_quantity: 10,
|
||||
options: {
|
||||
size: "small",
|
||||
color: "red",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const res = await Promise.all([productOne, productTwo])
|
||||
productOne = res[0]
|
||||
productTwo = res[1]
|
||||
})
|
||||
|
||||
it("should update a product and upsert relations that are not created yet", async () => {
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const variantTitle = data.variants[0].title
|
||||
|
||||
const productBefore = (await service.retrieve(productOne.id, {
|
||||
relations: [
|
||||
"images",
|
||||
"variants",
|
||||
"options",
|
||||
"options.values",
|
||||
"variants.options",
|
||||
"tags",
|
||||
"type",
|
||||
],
|
||||
})) as unknown as UpdateProductInput
|
||||
|
||||
productBefore.title = "updated title"
|
||||
productBefore.variants = [
|
||||
...productBefore.variants!,
|
||||
...data.variants,
|
||||
]
|
||||
productBefore.options = data.options
|
||||
productBefore.images = data.images
|
||||
productBefore.thumbnail = data.thumbnail
|
||||
productBefore.tags = data.tags
|
||||
const updatedProducts = await service.upsert([productBefore])
|
||||
expect(updatedProducts).toHaveLength(1)
|
||||
|
||||
const product = await service.retrieve(productBefore.id, {
|
||||
relations: [
|
||||
"images",
|
||||
"variants",
|
||||
"options",
|
||||
"options.values",
|
||||
"variants.options",
|
||||
"tags",
|
||||
"type",
|
||||
],
|
||||
})
|
||||
|
||||
const createdVariant = product.variants.find(
|
||||
(v) => v.title === variantTitle
|
||||
)!
|
||||
|
||||
expect(product.images).toHaveLength(1)
|
||||
expect(createdVariant?.options).toHaveLength(1)
|
||||
expect(product.tags).toHaveLength(1)
|
||||
expect(product.variants).toHaveLength(2)
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: "updated title",
|
||||
description: productBefore.description,
|
||||
subtitle: productBefore.subtitle,
|
||||
is_giftcard: productBefore.is_giftcard,
|
||||
discountable: productBefore.discountable,
|
||||
thumbnail: images[0].url,
|
||||
status: productBefore.status,
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
url: images[0].url,
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: productBefore.options?.[0].title,
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.options[0].values[0],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
tags: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: productBefore.tags?.[0].value,
|
||||
}),
|
||||
]),
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: createdVariant.title,
|
||||
sku: createdVariant.sku,
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
inventory_quantity: 100,
|
||||
variant_rank: 0,
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.options[0].values[0],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should emit events through event bus", async () => {
|
||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const updateData = {
|
||||
...data,
|
||||
options: data.options,
|
||||
id: productOne.id,
|
||||
title: "updated title",
|
||||
}
|
||||
|
||||
await service.upsert([updateData])
|
||||
|
||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||
expect(eventBusSpy).toHaveBeenCalledWith([
|
||||
{
|
||||
eventName: "product.updated",
|
||||
data: { id: productOne.id },
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should add relationships to a product", async () => {
|
||||
const updateData = {
|
||||
id: productOne.id,
|
||||
categories: [
|
||||
{
|
||||
id: productCategoryOne.id,
|
||||
},
|
||||
],
|
||||
collection_id: productCollectionOne.id,
|
||||
type_id: productTypeOne.id,
|
||||
}
|
||||
|
||||
await service.upsert([updateData])
|
||||
|
||||
const product = await service.retrieve(updateData.id, {
|
||||
relations: ["categories", "collection", "type"],
|
||||
})
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productOne.id,
|
||||
categories: [
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
}),
|
||||
],
|
||||
collection: expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
type: expect.objectContaining({
|
||||
id: productTypeOne.id,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should upsert a product type when type object is passed", async () => {
|
||||
let updateData = {
|
||||
id: productTwo.id,
|
||||
type_id: productTypeOne.id,
|
||||
}
|
||||
|
||||
await service.upsert([updateData])
|
||||
|
||||
let product = await service.retrieve(updateData.id, {
|
||||
relations: ["type"],
|
||||
})
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productTwo.id,
|
||||
type: expect.objectContaining({
|
||||
id: productTypeOne.id,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should replace relationships of a product", async () => {
|
||||
const newTagData = {
|
||||
id: "tag-2",
|
||||
value: "tag 2",
|
||||
}
|
||||
|
||||
const updateData = {
|
||||
id: productTwo.id,
|
||||
categories: [
|
||||
{
|
||||
id: productCategoryTwo.id,
|
||||
},
|
||||
],
|
||||
collection_id: productCollectionTwo.id,
|
||||
type_id: productTypeTwo.id,
|
||||
tags: [newTagData],
|
||||
}
|
||||
|
||||
await service.upsert([updateData])
|
||||
|
||||
const product = await service.retrieve(updateData.id, {
|
||||
relations: ["categories", "collection", "tags", "type"],
|
||||
})
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productTwo.id,
|
||||
categories: [
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
}),
|
||||
],
|
||||
collection: expect.objectContaining({
|
||||
id: productCollectionTwo.id,
|
||||
}),
|
||||
tags: [
|
||||
expect.objectContaining({
|
||||
id: newTagData.id,
|
||||
value: newTagData.value,
|
||||
}),
|
||||
],
|
||||
type: expect.objectContaining({
|
||||
id: productTypeTwo.id,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should remove relationships of a product", async () => {
|
||||
const updateData = {
|
||||
id: productTwo.id,
|
||||
categories: [],
|
||||
collection_id: null,
|
||||
type_id: null,
|
||||
tags: [],
|
||||
}
|
||||
|
||||
await service.upsert([updateData])
|
||||
|
||||
const product = await service.retrieve(updateData.id, {
|
||||
relations: ["categories", "collection", "tags"],
|
||||
})
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productTwo.id,
|
||||
categories: [],
|
||||
tags: [],
|
||||
collection: null,
|
||||
type: null,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when product ID does not exist", async () => {
|
||||
let error
|
||||
try {
|
||||
await service.update("does-not-exist", { title: "test" })
|
||||
} catch (e) {
|
||||
error = e.message
|
||||
}
|
||||
|
||||
expect(error).toEqual(`Product with id: does-not-exist was not found`)
|
||||
})
|
||||
|
||||
it("should update, create and delete variants", async () => {
|
||||
const updateData = {
|
||||
id: productTwo.id,
|
||||
// Note: VariantThree is already assigned to productTwo, that should be deleted
|
||||
variants: [
|
||||
{
|
||||
id: productTwo.variants[0].id,
|
||||
title: "updated-variant",
|
||||
},
|
||||
{
|
||||
title: "created-variant",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
await service.upsert([updateData])
|
||||
|
||||
const product = await service.retrieve(updateData.id, {
|
||||
relations: ["variants"],
|
||||
})
|
||||
|
||||
expect(product.variants).toHaveLength(2)
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: productTwo.variants[0].id,
|
||||
title: "updated-variant",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: "created-variant",
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should do a partial update on the options of a variant successfully", async () => {
|
||||
await service.update(productTwo.id, {
|
||||
variants: [
|
||||
{
|
||||
id: "variant-3",
|
||||
options: { size: "small", color: "blue" },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const fetchedProduct = await service.retrieve(productTwo.id, {
|
||||
relations: ["variants", "variants.options"],
|
||||
})
|
||||
|
||||
expect(fetchedProduct.variants[0].options).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "blue",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should createa variant with id that was passed if it does not exist", async () => {
|
||||
const updateData = {
|
||||
id: productTwo.id,
|
||||
// Note: VariantThree is already assigned to productTwo, that should be deleted
|
||||
variants: [
|
||||
{
|
||||
id: "passed-id",
|
||||
title: "updated-variant",
|
||||
},
|
||||
{
|
||||
title: "created-variant",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
await service.upsert([updateData])
|
||||
const retrieved = await service.retrieve(updateData.id, {
|
||||
relations: ["variants"],
|
||||
})
|
||||
|
||||
expect(retrieved.variants).toHaveLength(2)
|
||||
expect(retrieved.variants).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "passed-id",
|
||||
title: "updated-variant",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: "created-variant",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", function () {
|
||||
let images = [{ url: "image-1" }]
|
||||
it("should create a product", async () => {
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const productsCreated = await service.create([data])
|
||||
|
||||
const products = await service.list(
|
||||
{ id: productsCreated[0].id },
|
||||
{
|
||||
relations: [
|
||||
"images",
|
||||
"categories",
|
||||
"variants",
|
||||
"variants.options",
|
||||
"options",
|
||||
"options.values",
|
||||
"tags",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
expect(products).toHaveLength(1)
|
||||
expect(products[0].images).toHaveLength(1)
|
||||
expect(products[0].options).toHaveLength(1)
|
||||
expect(products[0].tags).toHaveLength(1)
|
||||
expect(products[0].categories).toHaveLength(0)
|
||||
expect(products[0].variants).toHaveLength(1)
|
||||
|
||||
expect(products[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.title,
|
||||
handle: kebabCase(data.title),
|
||||
description: data.description,
|
||||
subtitle: data.subtitle,
|
||||
is_giftcard: data.is_giftcard,
|
||||
discountable: data.discountable,
|
||||
thumbnail: images[0].url,
|
||||
status: data.status,
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
url: images[0].url,
|
||||
}),
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.options[0].title,
|
||||
values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.options[0].values[0],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
tags: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.tags[0].value,
|
||||
}),
|
||||
]),
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.variants[0].title,
|
||||
sku: data.variants[0].sku,
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
inventory_quantity: 100,
|
||||
variant_rank: 0,
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.options[0].values[0],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should emit events through eventBus", async () => {
|
||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||
expect(eventBusSpy).toHaveBeenCalledWith([
|
||||
{
|
||||
eventName: "product.created",
|
||||
data: { id: products[0].id },
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("softDelete", function () {
|
||||
let images = [{ url: "image-1" }]
|
||||
it("should soft delete a product and its cascaded relations", async () => {
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
|
||||
await service.softDelete([products[0].id])
|
||||
|
||||
const deletedProducts = await service.list(
|
||||
{ id: products[0].id },
|
||||
{
|
||||
relations: [
|
||||
"variants",
|
||||
"variants.options",
|
||||
"options",
|
||||
"options.values",
|
||||
],
|
||||
withDeleted: true,
|
||||
}
|
||||
)
|
||||
|
||||
expect(deletedProducts).toHaveLength(1)
|
||||
expect(deletedProducts[0].deleted_at).not.toBeNull()
|
||||
|
||||
for (const option of deletedProducts[0].options) {
|
||||
expect(option.deleted_at).not.toBeNull()
|
||||
}
|
||||
|
||||
const productOptionsValues = deletedProducts[0].options
|
||||
.map((o) => o.values)
|
||||
.flat()
|
||||
|
||||
for (const optionValue of productOptionsValues) {
|
||||
expect(optionValue.deleted_at).not.toBeNull()
|
||||
}
|
||||
|
||||
for (const variant of deletedProducts[0].variants) {
|
||||
expect(variant.deleted_at).not.toBeNull()
|
||||
}
|
||||
|
||||
const variantsOptions = deletedProducts[0].options
|
||||
.map((o) => o.values)
|
||||
.flat()
|
||||
|
||||
for (const option of variantsOptions) {
|
||||
expect(option.deleted_at).not.toBeNull()
|
||||
}
|
||||
})
|
||||
|
||||
it("should retrieve soft-deleted products if filtered on deleted_at", async () => {
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
|
||||
await service.softDelete([products[0].id])
|
||||
|
||||
const softDeleted = await service.list({
|
||||
deleted_at: { $gt: "01-01-2022" },
|
||||
})
|
||||
|
||||
expect(softDeleted).toHaveLength(1)
|
||||
})
|
||||
|
||||
it("should emit events through eventBus", async () => {
|
||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
|
||||
await service.softDelete([products[0].id])
|
||||
|
||||
expect(eventBusSpy).toHaveBeenCalledWith([
|
||||
{
|
||||
eventName: "product.created",
|
||||
data: { id: products[0].id },
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("restore", function () {
|
||||
let images = [{ url: "image-1" }]
|
||||
|
||||
it("should restore a soft deleted product and its cascaded relations", async () => {
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
|
||||
let retrievedProducts = await service.list({ id: products[0].id })
|
||||
|
||||
expect(retrievedProducts).toHaveLength(1)
|
||||
expect(retrievedProducts[0].deleted_at).toBeNull()
|
||||
|
||||
await service.softDelete([products[0].id])
|
||||
|
||||
retrievedProducts = await service.list(
|
||||
{ id: products[0].id },
|
||||
{
|
||||
withDeleted: true,
|
||||
}
|
||||
)
|
||||
|
||||
expect(retrievedProducts).toHaveLength(1)
|
||||
expect(retrievedProducts[0].deleted_at).not.toBeNull()
|
||||
|
||||
await service.restore([products[0].id])
|
||||
|
||||
const deletedProducts = await service.list(
|
||||
{ id: products[0].id },
|
||||
{
|
||||
relations: [
|
||||
"variants",
|
||||
"variants.options",
|
||||
"options",
|
||||
"options.values",
|
||||
],
|
||||
withDeleted: true,
|
||||
}
|
||||
)
|
||||
|
||||
expect(deletedProducts).toHaveLength(1)
|
||||
expect(deletedProducts[0].deleted_at).toBeNull()
|
||||
|
||||
for (const option of deletedProducts[0].options) {
|
||||
expect(option.deleted_at).toBeNull()
|
||||
}
|
||||
|
||||
const productOptionsValues = deletedProducts[0].options
|
||||
.map((o) => o.values)
|
||||
.flat()
|
||||
|
||||
for (const optionValue of productOptionsValues) {
|
||||
expect(optionValue.deleted_at).toBeNull()
|
||||
}
|
||||
|
||||
for (const variant of deletedProducts[0].variants) {
|
||||
expect(variant.deleted_at).toBeNull()
|
||||
}
|
||||
|
||||
const variantsOptions = deletedProducts[0].options
|
||||
.map((o) => o.values)
|
||||
.flat()
|
||||
|
||||
for (const option of variantsOptions) {
|
||||
expect(option.deleted_at).toBeNull()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("list", function () {
|
||||
beforeEach(async () => {
|
||||
const collections = await createCollections(
|
||||
MikroOrmWrapper.forkManager(),
|
||||
productCollectionsData
|
||||
)
|
||||
|
||||
productCollectionOne = collections[0]
|
||||
productCollectionTwo = collections[1]
|
||||
|
||||
const productOneData = buildProductAndRelationsData({
|
||||
collection_id: productCollectionOne.id,
|
||||
})
|
||||
|
||||
const productTwoData = buildProductAndRelationsData({
|
||||
collection_id: productCollectionTwo.id,
|
||||
tags: [],
|
||||
})
|
||||
|
||||
await service.create([productOneData, productTwoData])
|
||||
})
|
||||
|
||||
it("should return a list of products scoped by collection id", async () => {
|
||||
const productsWithCollectionOne = await service.list(
|
||||
{ collection_id: productCollectionOne.id },
|
||||
{
|
||||
relations: ["collection"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productsWithCollectionOne).toHaveLength(1)
|
||||
|
||||
expect([
|
||||
expect.objectContaining({
|
||||
collection: expect.objectContaining({
|
||||
id: productCollectionOne.id,
|
||||
}),
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return empty array when querying for a collection that doesnt exist", async () => {
|
||||
const products = await service.list(
|
||||
{
|
||||
categories: { id: ["collection-doesnt-exist-id"] },
|
||||
},
|
||||
{
|
||||
select: ["title", "collection.title"],
|
||||
relations: ["collection"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(products).toEqual([])
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,371 @@
|
||||
import { ProductOptionService } from "@services"
|
||||
import { Product } from "@models"
|
||||
|
||||
import { createOptions } from "../../../__fixtures__/product"
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
medusaApp,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductOption Service", () => {
|
||||
let service: ProductOptionService
|
||||
|
||||
beforeEach(() => {
|
||||
service = medusaApp.modules["productService"].productOptionService_
|
||||
})
|
||||
|
||||
let productOne: Product
|
||||
let productTwo: Product
|
||||
|
||||
const productOneData = {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
}
|
||||
|
||||
const productTwoData = {
|
||||
id: "product-2",
|
||||
title: "product 2",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = MikroOrmWrapper.forkManager()
|
||||
productOne = testManager.create(Product, productOneData)
|
||||
productTwo = testManager.create(Product, productTwoData)
|
||||
|
||||
await createOptions(testManager, [
|
||||
{
|
||||
id: "option-1",
|
||||
title: "Option 1",
|
||||
product: productOne,
|
||||
},
|
||||
{
|
||||
id: "option-2",
|
||||
title: "Option 2",
|
||||
product: productOne,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
describe("list", () => {
|
||||
it("list product option", async () => {
|
||||
const optionResults = await service.list()
|
||||
|
||||
expect(optionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "option-1",
|
||||
title: "Option 1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "option-2",
|
||||
title: "Option 2",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("list product option by id", async () => {
|
||||
const optionResults = await service.list({ id: "option-2" })
|
||||
|
||||
expect(optionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "option-2",
|
||||
title: "Option 2",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("list product option by title matching string", async () => {
|
||||
const optionResults = await service.list({ title: "Option 1" })
|
||||
|
||||
expect(optionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "option-1",
|
||||
title: "Option 1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCount", () => {
|
||||
it("should return product option and count", async () => {
|
||||
const [optionResults, count] = await service.listAndCount()
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(optionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "option-1",
|
||||
title: "Option 1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "option-2",
|
||||
title: "Option 2",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return product option and count when filtered", async () => {
|
||||
const [optionResults, count] = await service.listAndCount({
|
||||
id: "option-2",
|
||||
})
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(optionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "option-2",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return product option and count when using skip and take", async () => {
|
||||
const [optionResults, count] = await service.listAndCount(
|
||||
{},
|
||||
{ skip: 1, take: 1 }
|
||||
)
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(optionResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "option-2",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return requested fields", async () => {
|
||||
const [optionResults, count] = await service.listAndCount(
|
||||
{},
|
||||
{
|
||||
take: 1,
|
||||
select: ["title"],
|
||||
}
|
||||
)
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(optionResults))
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(serialized).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "option-1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
const optionId = "option-1"
|
||||
const optionValue = "Option 1"
|
||||
|
||||
it("should return option for the given id", async () => {
|
||||
const option = await service.retrieve(optionId)
|
||||
|
||||
expect(option).toEqual(
|
||||
expect.objectContaining({
|
||||
id: optionId,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when option with id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductOption with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when an id is not provided", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve(undefined as unknown as string)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("productOption - id must be defined")
|
||||
})
|
||||
|
||||
it("should return option based on config select param", async () => {
|
||||
const option = await service.retrieve(optionId, {
|
||||
select: ["id", "title"],
|
||||
})
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(option))
|
||||
|
||||
expect(serialized).toEqual({
|
||||
id: optionId,
|
||||
title: optionValue,
|
||||
product_id: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("delete", () => {
|
||||
const optionId = "option-1"
|
||||
|
||||
it("should delete the product option given an ID successfully", async () => {
|
||||
await service.delete([optionId])
|
||||
|
||||
const options = await service.list({
|
||||
id: optionId,
|
||||
})
|
||||
|
||||
expect(options).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
const optionId = "option-1"
|
||||
|
||||
it("should update the title of the option successfully", async () => {
|
||||
await service.update([
|
||||
{
|
||||
id: optionId,
|
||||
title: "UK",
|
||||
},
|
||||
])
|
||||
|
||||
const productOption = await service.retrieve(optionId)
|
||||
|
||||
expect(productOption.title).toEqual("UK")
|
||||
})
|
||||
|
||||
it("should update the relationship of the option successfully", async () => {
|
||||
await service.update([
|
||||
{
|
||||
id: optionId,
|
||||
product_id: productTwo.id,
|
||||
},
|
||||
])
|
||||
|
||||
const productOption = await service.retrieve(optionId, {
|
||||
relations: ["product"],
|
||||
})
|
||||
|
||||
expect(productOption).toEqual(
|
||||
expect.objectContaining({
|
||||
id: optionId,
|
||||
product: expect.objectContaining({
|
||||
id: productTwo.id,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.update([
|
||||
{
|
||||
id: "does-not-exist",
|
||||
title: "UK",
|
||||
},
|
||||
])
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
'ProductOption with id "does-not-exist" not found'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
it("should create a option successfully", async () => {
|
||||
await service.create([
|
||||
{
|
||||
title: "UK",
|
||||
product: productOne,
|
||||
},
|
||||
])
|
||||
|
||||
const [productOption] = await service.list(
|
||||
{
|
||||
title: "UK",
|
||||
},
|
||||
{
|
||||
relations: ["product"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productOption).toEqual(
|
||||
expect.objectContaining({
|
||||
title: "UK",
|
||||
product: expect.objectContaining({
|
||||
id: productOne.id,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("upsert", function () {
|
||||
it("should create an option and update another option successfully", async () => {
|
||||
const productOption = (
|
||||
await service.create([
|
||||
{
|
||||
title: "UK",
|
||||
product: productOne,
|
||||
},
|
||||
])
|
||||
)[0]
|
||||
|
||||
const optionToUpdate = {
|
||||
id: productOption.id,
|
||||
title: "US",
|
||||
}
|
||||
|
||||
const newOption = {
|
||||
title: "US2",
|
||||
product_id: productOne.id,
|
||||
}
|
||||
|
||||
await service.upsert([optionToUpdate, newOption])
|
||||
|
||||
const productOptions = await service.list(
|
||||
{
|
||||
q: "US%",
|
||||
},
|
||||
{
|
||||
relations: ["product"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(JSON.parse(JSON.stringify(productOptions))).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "US",
|
||||
product: expect.objectContaining({
|
||||
id: productOne.id,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: newOption.title,
|
||||
product: expect.objectContaining({
|
||||
id: productOne.id,
|
||||
}),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,338 @@
|
||||
import { Product } from "@models"
|
||||
import { ProductTagService } from "@services"
|
||||
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { createProductAndTags } from "../../../__fixtures__/product"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
medusaApp,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductTag Service", () => {
|
||||
let data!: Product[]
|
||||
let service: ProductTagService
|
||||
|
||||
beforeEach(() => {
|
||||
service = medusaApp.modules["productService"].productTagService_
|
||||
})
|
||||
|
||||
const productsData = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
tags: [
|
||||
{
|
||||
id: "tag-1",
|
||||
value: "France",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "product",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
tags: [
|
||||
{
|
||||
id: "tag-2",
|
||||
value: "Germany",
|
||||
},
|
||||
{
|
||||
id: "tag-3",
|
||||
value: "United States",
|
||||
},
|
||||
{
|
||||
id: "tag-4",
|
||||
value: "United Kingdom",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
beforeEach(async () => {
|
||||
data = await createProductAndTags(
|
||||
MikroOrmWrapper.forkManager(),
|
||||
productsData
|
||||
)
|
||||
})
|
||||
|
||||
describe("list", () => {
|
||||
it("list product tags", async () => {
|
||||
const tagsResults = await service.list()
|
||||
|
||||
expect(tagsResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "tag-1",
|
||||
value: "France",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "tag-2",
|
||||
value: "Germany",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "tag-3",
|
||||
value: "United States",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "tag-4",
|
||||
value: "United Kingdom",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("list product tags by id", async () => {
|
||||
const tagsResults = await service.list({ id: data[0].tags![0].id })
|
||||
|
||||
expect(tagsResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "tag-1",
|
||||
value: "France",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("list product tags by value matching string", async () => {
|
||||
const tagsResults = await service.list({ q: "united kingdom" })
|
||||
|
||||
expect(tagsResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "tag-4",
|
||||
value: "United Kingdom",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCount", () => {
|
||||
it("should return product tags and count", async () => {
|
||||
const [tagsResults, count] = await service.listAndCount()
|
||||
|
||||
expect(count).toEqual(4)
|
||||
expect(tagsResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "tag-1",
|
||||
value: "France",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "tag-2",
|
||||
value: "Germany",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "tag-3",
|
||||
value: "United States",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "tag-4",
|
||||
value: "United Kingdom",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return product tags and count when filtered", async () => {
|
||||
const [tagsResults, count] = await service.listAndCount({
|
||||
id: data[0].tags![0].id,
|
||||
})
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(tagsResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "tag-1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return product tags and count when using skip and take", async () => {
|
||||
const [tagsResults, count] = await service.listAndCount(
|
||||
{},
|
||||
{ skip: 1, take: 2 }
|
||||
)
|
||||
|
||||
expect(count).toEqual(4)
|
||||
expect(tagsResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "tag-2",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "tag-3",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return requested fields and relations", async () => {
|
||||
const [tagsResults, count] = await service.listAndCount(
|
||||
{},
|
||||
{
|
||||
take: 1,
|
||||
select: ["value", "products.id"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(tagsResults))
|
||||
|
||||
expect(count).toEqual(4)
|
||||
expect(serialized).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "tag-1",
|
||||
products: [
|
||||
{
|
||||
id: "test-1",
|
||||
collection_id: null,
|
||||
type_id: null,
|
||||
},
|
||||
],
|
||||
value: "France",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
const tagId = "tag-1"
|
||||
const tagValue = "France"
|
||||
const productId = "test-1"
|
||||
|
||||
it("should return tag for the given id", async () => {
|
||||
const tag = await service.retrieve(tagId)
|
||||
|
||||
expect(tag).toEqual(
|
||||
expect.objectContaining({
|
||||
id: tagId,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when tag with id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductTag with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when an id is not provided", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve(undefined as unknown as string)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("productTag - id must be defined")
|
||||
})
|
||||
|
||||
it("should return tag based on config select param", async () => {
|
||||
const tag = await service.retrieve(tagId, {
|
||||
select: ["id", "value"],
|
||||
})
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(tag))
|
||||
|
||||
expect(serialized).toEqual({
|
||||
id: tagId,
|
||||
value: tagValue,
|
||||
})
|
||||
})
|
||||
|
||||
it("should return tag based on config relation param", async () => {
|
||||
const tag = await service.retrieve(tagId, {
|
||||
select: ["id", "value", "products.id"],
|
||||
relations: ["products"],
|
||||
})
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(tag))
|
||||
|
||||
expect(serialized).toEqual({
|
||||
id: tagId,
|
||||
value: tagValue,
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: productId,
|
||||
}),
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("delete", () => {
|
||||
const tagId = "tag-1"
|
||||
|
||||
it("should delete the product tag given an ID successfully", async () => {
|
||||
await service.delete([tagId])
|
||||
|
||||
const tags = await service.list({
|
||||
id: tagId,
|
||||
})
|
||||
|
||||
expect(tags).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
const tagId = "tag-1"
|
||||
|
||||
it("should update the value of the tag successfully", async () => {
|
||||
await service.update([
|
||||
{
|
||||
id: tagId,
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
|
||||
const productTag = await service.retrieve(tagId)
|
||||
|
||||
expect(productTag.value).toEqual("UK")
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.update([
|
||||
{
|
||||
id: "does-not-exist",
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
'ProductTag with id "does-not-exist" not found'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
it("should create a tag successfully", async () => {
|
||||
await service.create([
|
||||
{
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
|
||||
const [productTag] = await service.list({
|
||||
value: "UK",
|
||||
})
|
||||
|
||||
expect(productTag.value).toEqual("UK")
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,277 @@
|
||||
import { ProductTypeService } from "@services"
|
||||
import { Product } from "@models"
|
||||
|
||||
import { createProductAndTypes } from "../../../__fixtures__/product"
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
medusaApp,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe("ProductType Service", () => {
|
||||
let data!: Product[]
|
||||
let service: ProductTypeService
|
||||
|
||||
beforeEach(() => {
|
||||
service = medusaApp.modules["productService"].productTypeService_
|
||||
})
|
||||
|
||||
const productsData = [
|
||||
{
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
type: {
|
||||
id: "type-1",
|
||||
value: "Type 1",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "product-2",
|
||||
title: "product",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
type: {
|
||||
id: "type-2",
|
||||
value: "Type 2",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
beforeEach(async () => {
|
||||
data = await createProductAndTypes(
|
||||
MikroOrmWrapper.forkManager(),
|
||||
productsData
|
||||
)
|
||||
})
|
||||
describe("list", () => {
|
||||
it("list product type", async () => {
|
||||
const typeResults = await service.list()
|
||||
|
||||
expect(typeResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "type-1",
|
||||
value: "Type 1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "type-2",
|
||||
value: "Type 2",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("list product type by id", async () => {
|
||||
const typeResults = await service.list({ id: data[0].type.id })
|
||||
|
||||
expect(typeResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "type-1",
|
||||
value: "Type 1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("list product type by value matching string", async () => {
|
||||
const typeResults = await service.list({ value: "Type 1" })
|
||||
|
||||
expect(typeResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "type-1",
|
||||
value: "Type 1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCount", () => {
|
||||
it("should return product type and count", async () => {
|
||||
const [typeResults, count] = await service.listAndCount()
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(typeResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "type-1",
|
||||
value: "Type 1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "type-2",
|
||||
value: "Type 2",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return product type and count when filtered", async () => {
|
||||
const [typeResults, count] = await service.listAndCount({
|
||||
id: data[0].type.id,
|
||||
})
|
||||
|
||||
expect(count).toEqual(1)
|
||||
expect(typeResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "type-1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return product type and count when using skip and take", async () => {
|
||||
const [typeResults, count] = await service.listAndCount(
|
||||
{},
|
||||
{ skip: 1, take: 1 }
|
||||
)
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(typeResults).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "type-2",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should return requested fields", async () => {
|
||||
const [typeResults, count] = await service.listAndCount(
|
||||
{},
|
||||
{
|
||||
take: 1,
|
||||
select: ["value"],
|
||||
}
|
||||
)
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(typeResults))
|
||||
|
||||
expect(count).toEqual(2)
|
||||
expect(serialized).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "type-1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
const typeId = "type-1"
|
||||
const typeValue = "Type 1"
|
||||
|
||||
it("should return type for the given id", async () => {
|
||||
const type = await service.retrieve(typeId)
|
||||
|
||||
expect(type).toEqual(
|
||||
expect.objectContaining({
|
||||
id: typeId,
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when type with id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductType with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when an id is not provided", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve(undefined as unknown as string)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("productType - id must be defined")
|
||||
})
|
||||
|
||||
it("should return type based on config select param", async () => {
|
||||
const type = await service.retrieve(typeId, {
|
||||
select: ["id", "value"],
|
||||
})
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(type))
|
||||
|
||||
expect(serialized).toEqual({
|
||||
id: typeId,
|
||||
value: typeValue,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("delete", () => {
|
||||
const typeId = "type-1"
|
||||
|
||||
it("should delete the product type given an ID successfully", async () => {
|
||||
await service.delete([typeId])
|
||||
|
||||
const types = await service.list({
|
||||
id: typeId,
|
||||
})
|
||||
|
||||
expect(types).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
const typeId = "type-1"
|
||||
|
||||
it("should update the value of the type successfully", async () => {
|
||||
await service.update([
|
||||
{
|
||||
id: typeId,
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
|
||||
const productType = await service.retrieve(typeId)
|
||||
|
||||
expect(productType.value).toEqual("UK")
|
||||
})
|
||||
|
||||
it("should throw an error when an id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.update([
|
||||
{
|
||||
id: "does-not-exist",
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
'ProductType with id "does-not-exist" not found'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
it("should create a type successfully", async () => {
|
||||
await service.create([
|
||||
{
|
||||
value: "UK",
|
||||
},
|
||||
])
|
||||
|
||||
const [productType] = await service.list({
|
||||
value: "UK",
|
||||
})
|
||||
|
||||
expect(productType.value).toEqual("UK")
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,328 @@
|
||||
import { ProductOption } from "@medusajs/client-types"
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { Collection } from "@mikro-orm/core"
|
||||
import { Product, ProductTag, ProductVariant } from "@models"
|
||||
import { ProductVariantService } from "@services"
|
||||
import {
|
||||
createOptions,
|
||||
createProductAndTags,
|
||||
createProductVariants,
|
||||
} from "../../../__fixtures__/product"
|
||||
import { productsData, variantsData } from "../../../__fixtures__/product/data"
|
||||
import { buildProductVariantOnlyData } from "../../../__fixtures__/variant/data/create-variant"
|
||||
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
medusaApp,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
describe.skip("ProductVariant Service", () => {
|
||||
let variantOne: ProductVariant
|
||||
let variantTwo: ProductVariant
|
||||
let productOne: Product
|
||||
const productVariantTestOne = "test-1"
|
||||
let service: ProductVariantService
|
||||
|
||||
beforeEach(() => {
|
||||
service = medusaApp.modules["productService"].productVariantService_
|
||||
})
|
||||
|
||||
describe("list", () => {
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
variantOne = testManager.create(ProductVariant, {
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
inventory_quantity: 10,
|
||||
product: productOne,
|
||||
})
|
||||
|
||||
variantTwo = testManager.create(ProductVariant, {
|
||||
id: "test-2",
|
||||
title: "variant",
|
||||
inventory_quantity: 10,
|
||||
product: productOne,
|
||||
})
|
||||
|
||||
await testManager.persistAndFlush([variantOne, variantTwo])
|
||||
})
|
||||
|
||||
it("selecting by properties, scopes out the results", async () => {
|
||||
const results = await service.list({
|
||||
id: variantOne.id,
|
||||
})
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: variantOne.id,
|
||||
title: "variant 1",
|
||||
inventory_quantity: "10",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("passing a limit, scopes the result to the limit", async () => {
|
||||
const results = await service.list(
|
||||
{},
|
||||
{
|
||||
take: 1,
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: variantOne.id,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("passing populate, scopes the results of the response", async () => {
|
||||
const results = await service.list(
|
||||
{
|
||||
id: productVariantTestOne,
|
||||
},
|
||||
{
|
||||
select: ["id", "title", "product.title"] as any,
|
||||
relations: ["product"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(results).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
product: expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
tags: expect.any(Collection<ProductTag>),
|
||||
variants: expect.any(Collection<ProductVariant>),
|
||||
}),
|
||||
}),
|
||||
])
|
||||
|
||||
expect(JSON.parse(JSON.stringify(results))).toEqual([
|
||||
{
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
product: {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("relation: options", () => {
|
||||
let products: Product[]
|
||||
let variants: ProductVariant[]
|
||||
let options: ProductOption[]
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
products = (await createProductAndTags(
|
||||
testManager,
|
||||
productsData
|
||||
)) as Product[]
|
||||
variants = (await createProductVariants(
|
||||
testManager,
|
||||
variantsData
|
||||
)) as ProductVariant[]
|
||||
|
||||
options = await createOptions(testManager, [
|
||||
{
|
||||
id: "test-option-1",
|
||||
title: "size",
|
||||
product: products[0],
|
||||
values: [
|
||||
{
|
||||
id: "value-1",
|
||||
value: "XS",
|
||||
variant: products[0].variants[0],
|
||||
},
|
||||
{
|
||||
id: "value-1",
|
||||
value: "XL",
|
||||
variant: products[0].variants[0],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "test-option-2",
|
||||
title: "color",
|
||||
product: products[0],
|
||||
value: "blue",
|
||||
variant: products[0].variants[0],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("filter by options relation", async () => {
|
||||
const variants = await service.list(
|
||||
{ options: { id: ["value-1"] } },
|
||||
{ relations: ["options"] }
|
||||
)
|
||||
|
||||
expect(JSON.parse(JSON.stringify(variants))).toEqual([
|
||||
expect.objectContaining({
|
||||
id: productVariantTestOne,
|
||||
title: "variant title",
|
||||
sku: "sku 1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", function () {
|
||||
let products: Product[]
|
||||
let productOptions!: ProductOption[]
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
products = (await createProductAndTags(
|
||||
testManager,
|
||||
productsData
|
||||
)) as Product[]
|
||||
|
||||
productOptions = await createOptions(testManager, [
|
||||
{
|
||||
id: "test-option-1",
|
||||
title: "size",
|
||||
product: products[0],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should create a variant", async () => {
|
||||
const data = buildProductVariantOnlyData({
|
||||
options: [
|
||||
{
|
||||
option: productOptions[0],
|
||||
value: "XS",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const variants = await service.create(products[0].id, [data])
|
||||
|
||||
expect(variants).toHaveLength(1)
|
||||
expect(variants[0].options).toHaveLength(1)
|
||||
|
||||
expect(JSON.parse(JSON.stringify(variants[0]))).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.title,
|
||||
sku: data.sku,
|
||||
inventory_quantity: 100,
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
variant_rank: 0,
|
||||
product: expect.objectContaining({
|
||||
id: products[0].id,
|
||||
}),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: data.options![0].value,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
beforeEach(async () => {
|
||||
const testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
variantOne = testManager.create(ProductVariant, {
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
inventory_quantity: 10,
|
||||
product: productOne,
|
||||
})
|
||||
|
||||
await testManager.persistAndFlush([variantOne])
|
||||
})
|
||||
|
||||
it("should return the requested variant", async () => {
|
||||
const result = await service.retrieve(variantOne.id)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const result = await service.retrieve(variantOne.id, {
|
||||
select: ["id", "title", "product.title"] as any,
|
||||
relations: ["product"],
|
||||
})
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productVariantTestOne,
|
||||
title: "variant 1",
|
||||
product_id: "product-1",
|
||||
product: expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when a variant with ID does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"ProductVariant with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when an id is not provided", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve(undefined as unknown as string)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("productVariant - id must be defined")
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,693 @@
|
||||
import { Image, Product, ProductCategory, ProductCollection } from "@models"
|
||||
import {
|
||||
assignCategoriesToProduct,
|
||||
buildProductOnlyData,
|
||||
createCollections,
|
||||
createImages,
|
||||
createProductAndTags,
|
||||
createProductVariants,
|
||||
} from "../../../__fixtures__/product"
|
||||
|
||||
import {
|
||||
categoriesData,
|
||||
productsData,
|
||||
variantsData,
|
||||
} from "../../../__fixtures__/product/data"
|
||||
import { ProductService } from "@services"
|
||||
import {
|
||||
IProductModuleService,
|
||||
ProductDTO,
|
||||
ProductTypes,
|
||||
} from "@medusajs/types"
|
||||
import { kebabCase } from "@medusajs/utils"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { ProductTag } from "../../../../src/models"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.PRODUCT,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
medusaApp,
|
||||
}: SuiteOptions<IProductModuleService>) => {
|
||||
let service: ProductService
|
||||
|
||||
beforeEach(() => {
|
||||
service = medusaApp.modules["productService"].productService_
|
||||
})
|
||||
|
||||
describe("Product Service", () => {
|
||||
let testManager: SqlEntityManager
|
||||
let products!: Product[]
|
||||
let productOne: Product
|
||||
let categories!: ProductCategory[]
|
||||
|
||||
describe("retrieve", () => {
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
await testManager.persistAndFlush([productOne])
|
||||
})
|
||||
|
||||
it("should throw an error when an id is not provided", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve(undefined as unknown as string)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("product - id must be defined")
|
||||
})
|
||||
|
||||
it("should throw an error when product with id does not exist", async () => {
|
||||
let error
|
||||
|
||||
try {
|
||||
await service.retrieve("does-not-exist")
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"Product with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should return a product when product with an id exists", async () => {
|
||||
const result = await service.retrieve(productOne.id)
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productOne.id,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", function () {
|
||||
let images: Image[] = []
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
images = await createImages(testManager, ["image-1"])
|
||||
})
|
||||
|
||||
it("should create a product", async () => {
|
||||
const data = buildProductOnlyData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
|
||||
expect(products).toHaveLength(1)
|
||||
expect(JSON.parse(JSON.stringify(products[0]))).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: data.title,
|
||||
handle: kebabCase(data.title),
|
||||
description: data.description,
|
||||
subtitle: data.subtitle,
|
||||
is_giftcard: data.is_giftcard,
|
||||
discountable: data.discountable,
|
||||
thumbnail: images[0].url,
|
||||
status: data.status,
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: images[0].id,
|
||||
url: images[0].url,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", function () {
|
||||
let images: Image[] = []
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
images = await createImages(testManager, ["image-1", "image-2"])
|
||||
|
||||
productOne = testManager.create(Product, {
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
await testManager.persistAndFlush([productOne])
|
||||
})
|
||||
|
||||
it("should update a product and its allowed relations", async () => {
|
||||
const updateData = [
|
||||
{
|
||||
id: productOne.id,
|
||||
title: "update test 1",
|
||||
images: images,
|
||||
thumbnail: images[0].url,
|
||||
},
|
||||
]
|
||||
|
||||
const products = await service.update(updateData)
|
||||
|
||||
expect(products.length).toEqual(1)
|
||||
|
||||
let result = await service.retrieve(productOne.id, {
|
||||
relations: ["images", "thumbnail"],
|
||||
})
|
||||
let serialized = JSON.parse(JSON.stringify(result))
|
||||
|
||||
expect(serialized).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productOne.id,
|
||||
title: "update test 1",
|
||||
thumbnail: images[0].url,
|
||||
images: [
|
||||
expect.objectContaining({
|
||||
url: images[0].url,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
url: images[1].url,
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when id is not present", async () => {
|
||||
let error
|
||||
const updateData = [
|
||||
{
|
||||
id: productOne.id,
|
||||
title: "update test 1",
|
||||
},
|
||||
{
|
||||
id: undefined as unknown as string,
|
||||
title: "update test 2",
|
||||
},
|
||||
]
|
||||
|
||||
try {
|
||||
await service.update(updateData)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(`Product with id "" not found`)
|
||||
|
||||
let result = await service.retrieve(productOne.id)
|
||||
|
||||
expect(result.title).not.toBe("update test 1")
|
||||
})
|
||||
|
||||
it("should throw an error when product with id does not exist", async () => {
|
||||
let error
|
||||
const updateData = [
|
||||
{
|
||||
id: "does-not-exist",
|
||||
title: "update test 1",
|
||||
},
|
||||
]
|
||||
|
||||
try {
|
||||
await service.update(updateData)
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
`Product with id "does-not-exist" not found`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("list", () => {
|
||||
it("should list all product that match the free text search", async () => {
|
||||
const data = buildProductOnlyData({
|
||||
title: "test product",
|
||||
})
|
||||
const data2 = buildProductOnlyData({
|
||||
title: "space X",
|
||||
})
|
||||
|
||||
const products = await service.create([data, data2])
|
||||
|
||||
const result = await service.list({
|
||||
q: "test",
|
||||
})
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].title).toEqual("test product")
|
||||
|
||||
const result2 = await service.list({
|
||||
q: "space",
|
||||
})
|
||||
|
||||
expect(result2).toHaveLength(1)
|
||||
expect(result2[0].title).toEqual("space X")
|
||||
})
|
||||
|
||||
describe("soft deleted", function () {
|
||||
let product
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
const products = await createProductAndTags(
|
||||
testManager,
|
||||
productsData
|
||||
)
|
||||
|
||||
product = products[1]
|
||||
await service.softDelete([products[0].id])
|
||||
})
|
||||
|
||||
it("should list all products that are not deleted", async () => {
|
||||
const products = await service.list()
|
||||
|
||||
expect(products).toHaveLength(2)
|
||||
expect(products[0].id).toEqual(product.id)
|
||||
})
|
||||
|
||||
it("should list all products including the deleted", async () => {
|
||||
const products = await service.list({}, { withDeleted: true })
|
||||
|
||||
expect(products).toHaveLength(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe("relation: tags", () => {
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
products = await createProductAndTags(testManager, productsData)
|
||||
})
|
||||
|
||||
it("should filter by id and including relations", async () => {
|
||||
const productsResult = await service.list(
|
||||
{
|
||||
id: products[0].id,
|
||||
},
|
||||
{
|
||||
relations: ["tags"],
|
||||
}
|
||||
)
|
||||
|
||||
productsResult.forEach((product, index) => {
|
||||
const tags = product.tags.toArray()
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productsData[index].id,
|
||||
title: productsData[index].title,
|
||||
})
|
||||
)
|
||||
|
||||
tags.forEach((tag, tagIndex) => {
|
||||
expect(tag).toEqual(
|
||||
expect.objectContaining({
|
||||
...productsData[index].tags[tagIndex],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should filter by id and without relations", async () => {
|
||||
const productsResult = await service.list({
|
||||
id: products[0].id,
|
||||
})
|
||||
|
||||
productsResult.forEach((product, index) => {
|
||||
const tags = product.tags.getItems(false)
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productsData[index].id,
|
||||
title: productsData[index].title,
|
||||
})
|
||||
)
|
||||
|
||||
expect(tags.length).toBe(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("relation: categories", () => {
|
||||
let workingProduct: Product
|
||||
let workingCategory: ProductCategory
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
products = await createProductAndTags(testManager, productsData)
|
||||
workingProduct = products.find((p) => p.id === "test-1") as Product
|
||||
categories = await createProductCategories(
|
||||
testManager,
|
||||
categoriesData
|
||||
)
|
||||
workingCategory = (await testManager.findOne(
|
||||
ProductCategory,
|
||||
"category-1"
|
||||
)) as ProductCategory
|
||||
|
||||
workingProduct = await assignCategoriesToProduct(
|
||||
testManager,
|
||||
workingProduct,
|
||||
categories
|
||||
)
|
||||
})
|
||||
|
||||
it("should filter by categories relation and scope fields", async () => {
|
||||
const products = await service.list(
|
||||
{
|
||||
id: workingProduct.id,
|
||||
categories: { id: [workingCategory.id] },
|
||||
},
|
||||
{
|
||||
select: [
|
||||
"title",
|
||||
"categories.name",
|
||||
"categories.handle",
|
||||
"categories.mpath",
|
||||
] as (keyof ProductDTO)[],
|
||||
relations: ["categories"],
|
||||
}
|
||||
)
|
||||
|
||||
const product = products.find(
|
||||
(p) => p.id === workingProduct.id
|
||||
) as unknown as Product
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: workingProduct.id,
|
||||
title: workingProduct.title,
|
||||
})
|
||||
)
|
||||
|
||||
expect(product.categories.toArray()).toEqual([
|
||||
{
|
||||
id: "category-0",
|
||||
name: "category 0",
|
||||
handle: "category-0",
|
||||
mpath: "category-0.",
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
id: "category-1",
|
||||
name: "category 1",
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
id: "category-1-a",
|
||||
name: "category 1 a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: null,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should returns empty array when querying for a category that doesnt exist", async () => {
|
||||
const products = await service.list(
|
||||
{
|
||||
id: workingProduct.id,
|
||||
categories: { id: ["category-doesnt-exist-id"] },
|
||||
},
|
||||
{
|
||||
select: [
|
||||
"title",
|
||||
"categories.name",
|
||||
"categories.handle",
|
||||
] as (keyof ProductDTO)[],
|
||||
relations: ["categories"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(products).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe("relation: collections", () => {
|
||||
let workingProduct: Product
|
||||
let workingProductTwo: Product
|
||||
let workingCollection: ProductCollection
|
||||
let workingCollectionTwo: ProductCollection
|
||||
const collectionData = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "col 1",
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "col 2",
|
||||
},
|
||||
]
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
await createCollections(testManager, collectionData)
|
||||
workingCollection = (await testManager.findOne(
|
||||
ProductCollection,
|
||||
"test-1"
|
||||
)) as ProductCollection
|
||||
workingCollectionTwo = (await testManager.findOne(
|
||||
ProductCollection,
|
||||
"test-2"
|
||||
)) as ProductCollection
|
||||
|
||||
products = await createProductAndTags(testManager, [
|
||||
{
|
||||
...productsData[0],
|
||||
collection_id: workingCollection.id,
|
||||
},
|
||||
{
|
||||
...productsData[1],
|
||||
collection_id: workingCollectionTwo.id,
|
||||
},
|
||||
{
|
||||
...productsData[2],
|
||||
},
|
||||
])
|
||||
|
||||
workingProduct = products.find((p) => p.id === "test-1") as Product
|
||||
workingProductTwo = products.find(
|
||||
(p) => p.id === "test-2"
|
||||
) as Product
|
||||
})
|
||||
|
||||
it("should filter by collection relation and scope fields", async () => {
|
||||
const products = await service.list(
|
||||
{
|
||||
id: workingProduct.id,
|
||||
collection_id: workingCollection.id,
|
||||
},
|
||||
{
|
||||
select: ["title", "collection.title"],
|
||||
relations: ["collection"],
|
||||
}
|
||||
)
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(products))
|
||||
|
||||
expect(serialized.length).toEqual(1)
|
||||
expect(serialized).toEqual([
|
||||
{
|
||||
id: workingProduct.id,
|
||||
title: workingProduct.title,
|
||||
handle: "product-1",
|
||||
collection_id: workingCollection.id,
|
||||
type_id: null,
|
||||
collection: {
|
||||
handle: "col-1",
|
||||
id: workingCollection.id,
|
||||
title: workingCollection.title,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should filter by collection when multiple collection ids are passed", async () => {
|
||||
const products = await service.list(
|
||||
{
|
||||
collection_id: [workingCollection.id, workingCollectionTwo.id],
|
||||
},
|
||||
{
|
||||
select: ["title", "collection.title"],
|
||||
relations: ["collection"],
|
||||
}
|
||||
)
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(products))
|
||||
|
||||
expect(serialized.length).toEqual(2)
|
||||
expect(serialized).toEqual([
|
||||
{
|
||||
id: workingProduct.id,
|
||||
title: workingProduct.title,
|
||||
handle: "product-1",
|
||||
type_id: null,
|
||||
collection_id: workingCollection.id,
|
||||
collection: {
|
||||
handle: "col-1",
|
||||
id: workingCollection.id,
|
||||
title: workingCollection.title,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: workingProductTwo.id,
|
||||
title: workingProductTwo.title,
|
||||
handle: "product",
|
||||
type_id: null,
|
||||
collection_id: workingCollectionTwo.id,
|
||||
collection: {
|
||||
handle: "col-2",
|
||||
id: workingCollectionTwo.id,
|
||||
title: workingCollectionTwo.title,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should returns empty array when querying for a collection that doesnt exist", async () => {
|
||||
const products = await service.list(
|
||||
{
|
||||
id: workingProduct.id,
|
||||
collection_id: "collection-doesnt-exist-id",
|
||||
},
|
||||
{
|
||||
select: ["title", "collection.title"] as (keyof ProductDTO)[],
|
||||
relations: ["collection"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(products).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe("relation: variants", () => {
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
products = await createProductAndTags(testManager, productsData)
|
||||
await createProductVariants(testManager, variantsData)
|
||||
})
|
||||
|
||||
it("should filter by id and including relations", async () => {
|
||||
const productsResult = await service.list(
|
||||
{
|
||||
id: products[0].id,
|
||||
},
|
||||
{
|
||||
relations: ["variants"],
|
||||
}
|
||||
)
|
||||
|
||||
productsResult.forEach((product, index) => {
|
||||
const variants = product.variants.toArray()
|
||||
|
||||
expect(product).toEqual(
|
||||
expect.objectContaining({
|
||||
id: productsData[index].id,
|
||||
title: productsData[index].title,
|
||||
})
|
||||
)
|
||||
|
||||
variants.forEach((variant, variantIndex) => {
|
||||
const expectedVariant = variantsData.filter(
|
||||
(d) => d.product.id === product.id
|
||||
)[variantIndex]
|
||||
|
||||
const variantProduct = variant.product
|
||||
|
||||
expect(variant).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expectedVariant.id,
|
||||
sku: expectedVariant.sku,
|
||||
title: expectedVariant.title,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("softDelete", function () {
|
||||
let images: Image[] = []
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
images = await createImages(testManager, ["image-1"])
|
||||
})
|
||||
|
||||
it("should soft delete a product", async () => {
|
||||
const data = buildProductOnlyData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
await service.softDelete(products.map((p) => p.id))
|
||||
const deleteProducts = await service.list(
|
||||
{ id: products.map((p) => p.id) },
|
||||
{
|
||||
relations: [
|
||||
"variants",
|
||||
"variants.options",
|
||||
"options",
|
||||
"options.values",
|
||||
],
|
||||
withDeleted: true,
|
||||
}
|
||||
)
|
||||
|
||||
expect(deleteProducts).toHaveLength(1)
|
||||
expect(deleteProducts[0].deleted_at).not.toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe("restore", function () {
|
||||
let images: Image[] = []
|
||||
|
||||
beforeEach(async () => {
|
||||
testManager = await MikroOrmWrapper.forkManager()
|
||||
|
||||
images = await createImages(testManager, ["image-1"])
|
||||
})
|
||||
|
||||
it("should restore a soft deleted product", async () => {
|
||||
const data = buildProductOnlyData({
|
||||
images,
|
||||
thumbnail: images[0].url,
|
||||
})
|
||||
|
||||
const products = await service.create([data])
|
||||
const product = products[0]
|
||||
await service.softDelete([product.id])
|
||||
const [restoreProducts] = await service.restore([product.id])
|
||||
|
||||
expect(restoreProducts).toHaveLength(1)
|
||||
expect(restoreProducts[0].deleted_at).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user