| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- import {
- AddOptionGroupToProduct,
- CreateProduct,
- DeletionResult,
- GenerateProductVariants,
- GetAssetList,
- GetProductList,
- GetProductWithVariants,
- LanguageCode,
- ProductWithVariants,
- RemoveOptionGroupFromProduct,
- SortOrder,
- UpdateProduct,
- UpdateProductVariants,
- } from '@vendure/common/lib/generated-types';
- import { omit } from '@vendure/common/lib/omit';
- import gql from 'graphql-tag';
- import path from 'path';
- import {
- ADD_OPTION_GROUP_TO_PRODUCT,
- CREATE_PRODUCT,
- GENERATE_PRODUCT_VARIANTS,
- GET_ASSET_LIST,
- GET_PRODUCT_LIST,
- GET_PRODUCT_WITH_VARIANTS,
- REMOVE_OPTION_GROUP_FROM_PRODUCT,
- UPDATE_PRODUCT,
- UPDATE_PRODUCT_VARIANTS,
- } from '../../../admin-ui/src/app/data/definitions/product-definitions';
- import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
- import { TestAdminClient } from './test-client';
- import { TestServer } from './test-server';
- import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
- // tslint:disable:no-non-null-assertion
- describe('Product resolver', () => {
- const client = new TestAdminClient();
- const server = new TestServer();
- beforeAll(async () => {
- const token = await server.init({
- customerCount: 1,
- productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
- });
- await client.init();
- }, TEST_SETUP_TIMEOUT_MS);
- afterAll(async () => {
- await server.destroy();
- });
- describe('products list query', () => {
- it('returns all products when no options passed', async () => {
- const result = await client.query<GetProductList.Query, GetProductList.Variables>(
- GET_PRODUCT_LIST,
- {
- languageCode: LanguageCode.en,
- },
- );
- expect(result.products.items.length).toBe(20);
- expect(result.products.totalItems).toBe(20);
- });
- it('limits result set with skip & take', async () => {
- const result = await client.query<GetProductList.Query, GetProductList.Variables>(
- GET_PRODUCT_LIST,
- {
- languageCode: LanguageCode.en,
- options: {
- skip: 0,
- take: 3,
- },
- },
- );
- expect(result.products.items.length).toBe(3);
- expect(result.products.totalItems).toBe(20);
- });
- it('filters by name', async () => {
- const result = await client.query<GetProductList.Query, GetProductList.Variables>(
- GET_PRODUCT_LIST,
- {
- languageCode: LanguageCode.en,
- options: {
- filter: {
- name: {
- contains: 'skateboard',
- },
- },
- },
- },
- );
- expect(result.products.items.length).toBe(1);
- expect(result.products.items[0].name).toBe('Cruiser Skateboard');
- });
- it('sorts by name', async () => {
- const result = await client.query<GetProductList.Query, GetProductList.Variables>(
- GET_PRODUCT_LIST,
- {
- languageCode: LanguageCode.en,
- options: {
- sort: {
- name: SortOrder.ASC,
- },
- },
- },
- );
- expect(result.products.items.map(p => p.name)).toMatchSnapshot();
- });
- });
- describe('product query', () => {
- it('returns expected properties', async () => {
- const result = await client.query<GetProductWithVariants.Query, GetProductWithVariants.Variables>(
- GET_PRODUCT_WITH_VARIANTS,
- {
- languageCode: LanguageCode.en,
- id: 'T_2',
- },
- );
- if (!result.product) {
- fail('Product not found');
- return;
- }
- expect(omit(result.product, ['variants'])).toMatchSnapshot();
- expect(result.product.variants.length).toBe(2);
- });
- it('ProductVariant price properties are correct', async () => {
- const result = await client.query<GetProductWithVariants.Query, GetProductWithVariants.Variables>(
- GET_PRODUCT_WITH_VARIANTS,
- {
- languageCode: LanguageCode.en,
- id: 'T_2',
- },
- );
- if (!result.product) {
- fail('Product not found');
- return;
- }
- expect(result.product.variants[0].price).toBe(14374);
- expect(result.product.variants[0].taxCategory).toEqual({
- id: 'T_1',
- name: 'Standard Tax',
- });
- });
- it('returns null when id not found', async () => {
- const result = await client.query<GetProductWithVariants.Query, GetProductWithVariants.Variables>(
- GET_PRODUCT_WITH_VARIANTS,
- {
- languageCode: LanguageCode.en,
- id: 'bad_id',
- },
- );
- expect(result.product).toBeNull();
- });
- });
- describe('product mutation', () => {
- let newProduct: ProductWithVariants.Fragment;
- it('createProduct creates a new Product', async () => {
- const result = await client.query<CreateProduct.Mutation, CreateProduct.Variables>(
- CREATE_PRODUCT,
- {
- input: {
- translations: [
- {
- languageCode: LanguageCode.en,
- name: 'en Baked Potato',
- slug: 'en-baked-potato',
- description: 'A baked potato',
- },
- {
- languageCode: LanguageCode.de,
- name: 'de Baked Potato',
- slug: 'de-baked-potato',
- description: 'Eine baked Erdapfel',
- },
- ],
- },
- },
- );
- newProduct = result.createProduct;
- expect(newProduct).toMatchSnapshot();
- });
- it('createProduct creates a new Product with assets', async () => {
- const assetsResult = await client.query<GetAssetList.Query, GetAssetList.Variables>(
- GET_ASSET_LIST,
- );
- const assetIds = assetsResult.assets.items.slice(0, 2).map(a => a.id);
- const featuredAssetId = assetsResult.assets.items[0].id;
- const result = await client.query<CreateProduct.Mutation, CreateProduct.Variables>(
- CREATE_PRODUCT,
- {
- input: {
- assetIds,
- featuredAssetId,
- translations: [
- {
- languageCode: LanguageCode.en,
- name: 'en Has Assets',
- slug: 'en-has-assets',
- description: 'A product with assets',
- },
- ],
- },
- },
- );
- expect(result.createProduct.assets.map(a => a.id)).toEqual(assetIds);
- expect(result.createProduct.featuredAsset!.id).toBe(featuredAssetId);
- });
- it('updateProduct updates a Product', async () => {
- const result = await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(
- UPDATE_PRODUCT,
- {
- input: {
- id: newProduct.id,
- translations: [
- {
- languageCode: LanguageCode.en,
- name: 'en Mashed Potato',
- slug: 'en-mashed-potato',
- description: 'A blob of mashed potato',
- },
- {
- languageCode: LanguageCode.de,
- name: 'de Mashed Potato',
- slug: 'de-mashed-potato',
- description: 'Eine blob von gemashed Erdapfel',
- },
- ],
- },
- },
- );
- expect(result.updateProduct).toMatchSnapshot();
- });
- it('updateProduct accepts partial input', async () => {
- const result = await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(
- UPDATE_PRODUCT,
- {
- input: {
- id: newProduct.id,
- translations: [
- {
- languageCode: LanguageCode.en,
- name: 'en Very Mashed Potato',
- },
- ],
- },
- },
- );
- expect(result.updateProduct.translations.length).toBe(2);
- expect(result.updateProduct.translations[0].name).toBe('en Very Mashed Potato');
- expect(result.updateProduct.translations[0].description).toBe('A blob of mashed potato');
- expect(result.updateProduct.translations[1].name).toBe('de Mashed Potato');
- });
- it('updateProduct adds Assets to a product and sets featured asset', async () => {
- const assetsResult = await client.query<GetAssetList.Query, GetAssetList.Variables>(
- GET_ASSET_LIST,
- );
- const assetIds = assetsResult.assets.items.map(a => a.id);
- const featuredAssetId = assetsResult.assets.items[2].id;
- const result = await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(
- UPDATE_PRODUCT,
- {
- input: {
- id: newProduct.id,
- assetIds,
- featuredAssetId,
- },
- },
- );
- expect(result.updateProduct.assets.map(a => a.id)).toEqual(assetIds);
- expect(result.updateProduct.featuredAsset!.id).toBe(featuredAssetId);
- });
- it('updateProduct sets a featured asset', async () => {
- const productResult = await client.query<
- GetProductWithVariants.Query,
- GetProductWithVariants.Variables
- >(GET_PRODUCT_WITH_VARIANTS, {
- id: newProduct.id,
- languageCode: LanguageCode.en,
- });
- const assets = productResult.product!.assets;
- const result = await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(
- UPDATE_PRODUCT,
- {
- input: {
- id: newProduct.id,
- featuredAssetId: assets[0].id,
- },
- },
- );
- expect(result.updateProduct.featuredAsset!.id).toBe(assets[0].id);
- });
- it('updateProduct updates FacetValues', async () => {
- const result = await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(
- UPDATE_PRODUCT,
- {
- input: {
- id: newProduct.id,
- facetValueIds: ['T_1'],
- },
- },
- );
- expect(result.updateProduct.facetValues.length).toEqual(1);
- });
- it(
- 'updateProduct errors with an invalid productId',
- assertThrowsWithMessage(
- () =>
- client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
- input: {
- id: '999',
- translations: [
- {
- languageCode: LanguageCode.en,
- name: 'en Mashed Potato',
- slug: 'en-mashed-potato',
- description: 'A blob of mashed potato',
- },
- {
- languageCode: LanguageCode.de,
- name: 'de Mashed Potato',
- slug: 'de-mashed-potato',
- description: 'Eine blob von gemashed Erdapfel',
- },
- ],
- },
- }),
- `No Product with the id '999' could be found`,
- ),
- );
- it('addOptionGroupToProduct adds an option group', async () => {
- const result = await client.query<
- AddOptionGroupToProduct.Mutation,
- AddOptionGroupToProduct.Variables
- >(ADD_OPTION_GROUP_TO_PRODUCT, {
- optionGroupId: 'T_2',
- productId: newProduct.id,
- });
- expect(result.addOptionGroupToProduct.optionGroups.length).toBe(1);
- expect(result.addOptionGroupToProduct.optionGroups[0].id).toBe('T_2');
- });
- it(
- 'addOptionGroupToProduct errors with an invalid productId',
- assertThrowsWithMessage(
- () =>
- client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
- ADD_OPTION_GROUP_TO_PRODUCT,
- {
- optionGroupId: 'T_1',
- productId: '999',
- },
- ),
- `No Product with the id '999' could be found`,
- ),
- );
- it(
- 'addOptionGroupToProduct errors with an invalid optionGroupId',
- assertThrowsWithMessage(
- () =>
- client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
- ADD_OPTION_GROUP_TO_PRODUCT,
- {
- optionGroupId: '999',
- productId: newProduct.id,
- },
- ),
- `No ProductOptionGroup with the id '999' could be found`,
- ),
- );
- it('removeOptionGroupFromProduct removes an option group', async () => {
- const result = await client.query<
- RemoveOptionGroupFromProduct.Mutation,
- RemoveOptionGroupFromProduct.Variables
- >(REMOVE_OPTION_GROUP_FROM_PRODUCT, {
- optionGroupId: 'T_1',
- productId: 'T_1',
- });
- expect(result.removeOptionGroupFromProduct.optionGroups.length).toBe(1);
- expect(result.removeOptionGroupFromProduct.optionGroups[0].code).toBe('laptop-ram');
- });
- it(
- 'removeOptionGroupFromProduct errors with an invalid productId',
- assertThrowsWithMessage(
- () =>
- client.query<
- RemoveOptionGroupFromProduct.Mutation,
- RemoveOptionGroupFromProduct.Variables
- >(REMOVE_OPTION_GROUP_FROM_PRODUCT, {
- optionGroupId: '1',
- productId: '999',
- }),
- `No Product with the id '999' could be found`,
- ),
- );
- describe('variants', () => {
- let variants: ProductWithVariants.Variants[];
- it(
- 'generateVariantsForProduct throws with an invalid productId',
- assertThrowsWithMessage(
- () =>
- client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(
- GENERATE_PRODUCT_VARIANTS,
- {
- productId: '999',
- },
- ),
- `No Product with the id '999' could be found`,
- ),
- );
- it(
- 'generateVariantsForProduct throws with an invalid defaultTaxCategoryId',
- assertThrowsWithMessage(
- () =>
- client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(
- GENERATE_PRODUCT_VARIANTS,
- {
- productId: newProduct.id,
- defaultTaxCategoryId: '999',
- },
- ),
- `No TaxCategory with the id '999' could be found`,
- ),
- );
- it('generateVariantsForProduct generates variants', async () => {
- const result = await client.query<
- GenerateProductVariants.Mutation,
- GenerateProductVariants.Variables
- >(GENERATE_PRODUCT_VARIANTS, {
- productId: newProduct.id,
- defaultPrice: 123,
- defaultSku: 'ABC',
- });
- variants = result.generateVariantsForProduct.variants;
- expect(variants.length).toBe(2);
- expect(variants[0].options.length).toBe(1);
- expect(variants[1].options.length).toBe(1);
- });
- it('updateProductVariants updates variants', async () => {
- const firstVariant = variants[0];
- const result = await client.query<
- UpdateProductVariants.Mutation,
- UpdateProductVariants.Variables
- >(UPDATE_PRODUCT_VARIANTS, {
- input: [
- {
- id: firstVariant.id,
- translations: firstVariant.translations,
- sku: 'ABC',
- price: 432,
- },
- ],
- });
- const updatedVariant = result.updateProductVariants[0];
- if (!updatedVariant) {
- fail('no updated variant returned.');
- return;
- }
- expect(updatedVariant.sku).toBe('ABC');
- expect(updatedVariant.price).toBe(432);
- });
- it('updateProductVariants updates assets', async () => {
- const firstVariant = variants[0];
- const result = await client.query<
- UpdateProductVariants.Mutation,
- UpdateProductVariants.Variables
- >(UPDATE_PRODUCT_VARIANTS, {
- input: [
- {
- id: firstVariant.id,
- assetIds: ['T_1', 'T_2'],
- featuredAssetId: 'T_2',
- },
- ],
- });
- const updatedVariant = result.updateProductVariants[0];
- if (!updatedVariant) {
- fail('no updated variant returned.');
- return;
- }
- expect(updatedVariant.assets.map(a => a.id)).toEqual(['T_1', 'T_2']);
- expect(updatedVariant.featuredAsset!.id).toBe('T_2');
- });
- it('updateProductVariants updates taxCategory and priceBeforeTax', async () => {
- const firstVariant = variants[0];
- const result = await client.query<
- UpdateProductVariants.Mutation,
- UpdateProductVariants.Variables
- >(UPDATE_PRODUCT_VARIANTS, {
- input: [
- {
- id: firstVariant.id,
- price: 105,
- taxCategoryId: 'T_2',
- },
- ],
- });
- const updatedVariant = result.updateProductVariants[0];
- if (!updatedVariant) {
- fail('no updated variant returned.');
- return;
- }
- expect(updatedVariant.price).toBe(105);
- expect(updatedVariant.taxCategory.id).toBe('T_2');
- });
- it('updateProductVariants updates facetValues', async () => {
- const firstVariant = variants[0];
- const result = await client.query<
- UpdateProductVariants.Mutation,
- UpdateProductVariants.Variables
- >(UPDATE_PRODUCT_VARIANTS, {
- input: [
- {
- id: firstVariant.id,
- facetValueIds: ['T_1'],
- },
- ],
- });
- const updatedVariant = result.updateProductVariants[0];
- if (!updatedVariant) {
- fail('no updated variant returned.');
- return;
- }
- expect(updatedVariant.facetValues.length).toBe(1);
- expect(updatedVariant.facetValues[0].id).toBe('T_1');
- });
- it(
- 'updateProductVariants throws with an invalid variant id',
- assertThrowsWithMessage(
- () =>
- client.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
- UPDATE_PRODUCT_VARIANTS,
- {
- input: [
- {
- id: 'T_999',
- translations: variants[0].translations,
- sku: 'ABC',
- price: 432,
- },
- ],
- },
- ),
- `No ProductVariant with the id '999' could be found`,
- ),
- );
- });
- });
- describe('deletion', () => {
- let allProducts: GetProductList.Items[];
- let productToDelete: GetProductList.Items;
- beforeAll(async () => {
- const result = await client.query<GetProductList.Query>(GET_PRODUCT_LIST);
- allProducts = result.products.items;
- });
- it('deletes a product', async () => {
- productToDelete = allProducts[0];
- const result = await client.query(DELETE_PRODUCT, { id: productToDelete.id });
- expect(result.deleteProduct).toEqual({ result: DeletionResult.DELETED });
- });
- it('cannot get a deleted product', async () => {
- const result = await client.query<GetProductWithVariants.Query, GetProductWithVariants.Variables>(
- GET_PRODUCT_WITH_VARIANTS,
- {
- id: productToDelete.id,
- },
- );
- expect(result.product).toBe(null);
- });
- it('deleted product omitted from list', async () => {
- const result = await client.query<GetProductList.Query>(GET_PRODUCT_LIST);
- expect(result.products.items.length).toBe(allProducts.length - 1);
- expect(result.products.items.map(c => c.id).includes(productToDelete.id)).toBe(false);
- });
- it(
- 'updateProduct throws for deleted product',
- assertThrowsWithMessage(
- () =>
- client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
- input: {
- id: productToDelete.id,
- facetValueIds: ['T_1'],
- },
- }),
- `No Product with the id '1' could be found`,
- ),
- );
- it(
- 'addOptionGroupToProduct throws for deleted product',
- assertThrowsWithMessage(
- () =>
- client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
- ADD_OPTION_GROUP_TO_PRODUCT,
- {
- optionGroupId: 'T_1',
- productId: productToDelete.id,
- },
- ),
- `No Product with the id '1' could be found`,
- ),
- );
- it(
- 'removeOptionGroupToProduct throws for deleted product',
- assertThrowsWithMessage(
- () =>
- client.query<
- RemoveOptionGroupFromProduct.Mutation,
- RemoveOptionGroupFromProduct.Variables
- >(REMOVE_OPTION_GROUP_FROM_PRODUCT, {
- optionGroupId: 'T_1',
- productId: productToDelete.id,
- }),
- `No Product with the id '1' could be found`,
- ),
- );
- it(
- 'generateVariantsForProduct throws for deleted product',
- assertThrowsWithMessage(
- () =>
- client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(
- GENERATE_PRODUCT_VARIANTS,
- {
- productId: productToDelete.id,
- },
- ),
- `No Product with the id '1' could be found`,
- ),
- );
- });
- });
- const DELETE_PRODUCT = gql`
- mutation DeleteProduct($id: ID!) {
- deleteProduct(id: $id) {
- result
- }
- }
- `;
|