product.e2e-spec.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. import {
  2. AddOptionGroupToProduct,
  3. AddOptionGroupToProductVariables,
  4. ApplyFacetValuesToProductVariants,
  5. ApplyFacetValuesToProductVariantsVariables,
  6. CreateProduct,
  7. CreateProduct_createProduct,
  8. CreateProductVariables,
  9. GenerateProductVariants,
  10. GenerateProductVariants_generateVariantsForProduct_variants,
  11. GenerateProductVariantsVariables,
  12. GetProductList,
  13. GetProductListVariables,
  14. GetProductWithVariants,
  15. GetProductWithVariantsVariables,
  16. LanguageCode,
  17. RemoveOptionGroupFromProduct,
  18. RemoveOptionGroupFromProductVariables,
  19. SortOrder,
  20. UpdateProduct,
  21. UpdateProductVariables,
  22. UpdateProductVariants,
  23. UpdateProductVariantsVariables,
  24. } from 'shared/generated-types';
  25. import { omit } from 'shared/omit';
  26. import {
  27. ADD_OPTION_GROUP_TO_PRODUCT,
  28. APPLY_FACET_VALUE_TO_PRODUCT_VARIANTS,
  29. CREATE_PRODUCT,
  30. GENERATE_PRODUCT_VARIANTS,
  31. REMOVE_OPTION_GROUP_FROM_PRODUCT,
  32. UPDATE_PRODUCT,
  33. UPDATE_PRODUCT_VARIANTS,
  34. } from '../../admin-ui/src/app/data/mutations/product-mutations';
  35. import {
  36. GET_PRODUCT_LIST,
  37. GET_PRODUCT_WITH_VARIANTS,
  38. } from '../../admin-ui/src/app/data/queries/product-queries';
  39. import { TestClient } from './test-client';
  40. import { TestServer } from './test-server';
  41. describe('Product resolver', () => {
  42. const client = new TestClient();
  43. const server = new TestServer();
  44. beforeAll(async () => {
  45. const token = await server.init({
  46. logging: true,
  47. disableAuth: true,
  48. productCount: 20,
  49. customerCount: 1,
  50. });
  51. await client.init();
  52. }, 30000);
  53. afterAll(async () => {
  54. await server.destroy();
  55. });
  56. describe('products list query', () => {
  57. it('returns all products when no options passed', async () => {
  58. const result = await client.query<GetProductList, GetProductListVariables>(GET_PRODUCT_LIST, {
  59. languageCode: LanguageCode.en,
  60. });
  61. expect(result.products.items.length).toBe(20);
  62. expect(result.products.totalItems).toBe(20);
  63. });
  64. it('limits result set with skip & take', async () => {
  65. const result = await client.query<GetProductList, GetProductListVariables>(GET_PRODUCT_LIST, {
  66. languageCode: LanguageCode.en,
  67. options: {
  68. skip: 0,
  69. take: 3,
  70. },
  71. });
  72. expect(result.products.items.length).toBe(3);
  73. expect(result.products.totalItems).toBe(20);
  74. });
  75. it('filters by name', async () => {
  76. const result = await client.query<GetProductList, GetProductListVariables>(GET_PRODUCT_LIST, {
  77. languageCode: LanguageCode.en,
  78. options: {
  79. filter: {
  80. name: {
  81. contains: 'fish',
  82. },
  83. },
  84. },
  85. });
  86. expect(result.products.items.length).toBe(2);
  87. expect(result.products.items[0].name).toBe('en Practical Frozen Fish');
  88. expect(result.products.items[1].name).toBe('en Sleek Wooden Fish');
  89. });
  90. it('sorts by name', async () => {
  91. const result = await client.query<GetProductList, GetProductListVariables>(GET_PRODUCT_LIST, {
  92. languageCode: LanguageCode.en,
  93. options: {
  94. sort: {
  95. name: SortOrder.ASC,
  96. },
  97. },
  98. });
  99. expect(result.products.items.map(p => p.name)).toMatchSnapshot();
  100. });
  101. });
  102. describe('product query', () => {
  103. it('returns expected properties', async () => {
  104. const result = await client.query<GetProductWithVariants, GetProductWithVariantsVariables>(
  105. GET_PRODUCT_WITH_VARIANTS,
  106. {
  107. languageCode: LanguageCode.en,
  108. id: '2',
  109. },
  110. );
  111. if (!result.product) {
  112. fail('Product not found');
  113. return;
  114. }
  115. expect(omit(result.product, ['variants'])).toMatchSnapshot();
  116. expect(result.product.variants.length).toBe(2);
  117. });
  118. it('returns null when id not found', async () => {
  119. const result = await client.query<GetProductWithVariants, GetProductWithVariantsVariables>(
  120. GET_PRODUCT_WITH_VARIANTS,
  121. {
  122. languageCode: LanguageCode.en,
  123. id: 'bad_id',
  124. },
  125. );
  126. expect(result.product).toBeNull();
  127. });
  128. });
  129. describe('product mutation', () => {
  130. let newProduct: CreateProduct_createProduct;
  131. it('createProduct creates a new Product', async () => {
  132. const result = await client.query<CreateProduct, CreateProductVariables>(CREATE_PRODUCT, {
  133. input: {
  134. image: 'baked-potato',
  135. translations: [
  136. {
  137. languageCode: LanguageCode.en,
  138. name: 'en Baked Potato',
  139. slug: 'en-baked-potato',
  140. description: 'A baked potato',
  141. },
  142. {
  143. languageCode: LanguageCode.de,
  144. name: 'de Baked Potato',
  145. slug: 'de-baked-potato',
  146. description: 'Eine baked Erdapfel',
  147. },
  148. ],
  149. },
  150. });
  151. newProduct = result.createProduct;
  152. expect(newProduct).toMatchSnapshot();
  153. });
  154. it('updateProduct updates a Product', async () => {
  155. const result = await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
  156. input: {
  157. id: newProduct.id,
  158. image: 'mashed-potato',
  159. translations: [
  160. {
  161. languageCode: LanguageCode.en,
  162. name: 'en Mashed Potato',
  163. slug: 'en-mashed-potato',
  164. description: 'A blob of mashed potato',
  165. },
  166. {
  167. languageCode: LanguageCode.de,
  168. name: 'de Mashed Potato',
  169. slug: 'de-mashed-potato',
  170. description: 'Eine blob von gemashed Erdapfel',
  171. },
  172. ],
  173. },
  174. });
  175. expect(result.updateProduct).toMatchSnapshot();
  176. });
  177. it('updateProduct errors with an invalid productId', async () => {
  178. try {
  179. await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
  180. input: {
  181. id: '999',
  182. image: 'mashed-potato',
  183. translations: [
  184. {
  185. languageCode: LanguageCode.en,
  186. name: 'en Mashed Potato',
  187. slug: 'en-mashed-potato',
  188. description: 'A blob of mashed potato',
  189. },
  190. {
  191. languageCode: LanguageCode.de,
  192. name: 'de Mashed Potato',
  193. slug: 'de-mashed-potato',
  194. description: 'Eine blob von gemashed Erdapfel',
  195. },
  196. ],
  197. },
  198. });
  199. fail('Should have thrown');
  200. } catch (err) {
  201. expect(err.message).toEqual(
  202. expect.stringContaining("No Product with the id '999' could be found"),
  203. );
  204. }
  205. });
  206. it('addOptionGroupToProduct adds an option group', async () => {
  207. const result = await client.query<AddOptionGroupToProduct, AddOptionGroupToProductVariables>(
  208. ADD_OPTION_GROUP_TO_PRODUCT,
  209. {
  210. optionGroupId: '1',
  211. productId: newProduct.id,
  212. },
  213. );
  214. expect(result.addOptionGroupToProduct.optionGroups.length).toBe(1);
  215. expect(result.addOptionGroupToProduct.optionGroups[0].id).toBe('1');
  216. });
  217. it('addOptionGroupToProduct errors with an invalid productId', async () => {
  218. try {
  219. await client.query<AddOptionGroupToProduct, AddOptionGroupToProductVariables>(
  220. ADD_OPTION_GROUP_TO_PRODUCT,
  221. {
  222. optionGroupId: '1',
  223. productId: '999',
  224. },
  225. );
  226. fail('Should have thrown');
  227. } catch (err) {
  228. expect(err.message).toEqual(
  229. expect.stringContaining("No Product with the id '999' could be found"),
  230. );
  231. }
  232. });
  233. it('addOptionGroupToProduct errors with an invalid optionGroupId', async () => {
  234. try {
  235. await client.query<AddOptionGroupToProduct, AddOptionGroupToProductVariables>(
  236. ADD_OPTION_GROUP_TO_PRODUCT,
  237. {
  238. optionGroupId: '999',
  239. productId: newProduct.id,
  240. },
  241. );
  242. fail('Should have thrown');
  243. } catch (err) {
  244. expect(err.message).toEqual(
  245. expect.stringContaining("No OptionGroup with the id '999' could be found"),
  246. );
  247. }
  248. });
  249. it('removeOptionGroupFromProduct removes an option group', async () => {
  250. const result = await client.query<
  251. RemoveOptionGroupFromProduct,
  252. RemoveOptionGroupFromProductVariables
  253. >(REMOVE_OPTION_GROUP_FROM_PRODUCT, {
  254. optionGroupId: '1',
  255. productId: '1',
  256. });
  257. expect(result.removeOptionGroupFromProduct.optionGroups.length).toBe(0);
  258. });
  259. it('removeOptionGroupFromProduct errors with an invalid productId', async () => {
  260. try {
  261. await client.query<RemoveOptionGroupFromProduct, RemoveOptionGroupFromProductVariables>(
  262. REMOVE_OPTION_GROUP_FROM_PRODUCT,
  263. {
  264. optionGroupId: '1',
  265. productId: '999',
  266. },
  267. );
  268. fail('Should have thrown');
  269. } catch (err) {
  270. expect(err.message).toEqual(
  271. expect.stringContaining("No Product with the id '999' could be found"),
  272. );
  273. }
  274. });
  275. describe('variants', () => {
  276. let variants: GenerateProductVariants_generateVariantsForProduct_variants[];
  277. it('generateVariantsForProduct generates variants', async () => {
  278. const result = await client.query<GenerateProductVariants, GenerateProductVariantsVariables>(
  279. GENERATE_PRODUCT_VARIANTS,
  280. {
  281. productId: newProduct.id,
  282. defaultPrice: 123,
  283. defaultSku: 'ABC',
  284. },
  285. );
  286. variants = result.generateVariantsForProduct.variants;
  287. expect(variants.length).toBe(2);
  288. expect(variants[0].options.length).toBe(1);
  289. expect(variants[1].options.length).toBe(1);
  290. });
  291. it('generateVariantsForProduct throws with an invalid productId', async () => {
  292. try {
  293. await client.query<GenerateProductVariants, GenerateProductVariantsVariables>(
  294. GENERATE_PRODUCT_VARIANTS,
  295. {
  296. productId: '999',
  297. },
  298. );
  299. fail('Should have thrown');
  300. } catch (err) {
  301. expect(err.message).toEqual(
  302. expect.stringContaining("No Product with the id '999' could be found"),
  303. );
  304. }
  305. });
  306. it('updateProductVariants updates variants', async () => {
  307. const firstVariant = variants[0];
  308. const result = await client.query<UpdateProductVariants, UpdateProductVariantsVariables>(
  309. UPDATE_PRODUCT_VARIANTS,
  310. {
  311. input: [
  312. {
  313. id: firstVariant.id,
  314. translations: firstVariant.translations,
  315. sku: 'ABC',
  316. image: 'new-image',
  317. price: 432,
  318. },
  319. ],
  320. },
  321. );
  322. const updatedVariant = result.updateProductVariants[0];
  323. if (!updatedVariant) {
  324. fail('no updated variant returned.');
  325. return;
  326. }
  327. expect(updatedVariant.sku).toBe('ABC');
  328. expect(updatedVariant.image).toBe('new-image');
  329. expect(updatedVariant.price).toBe(432);
  330. });
  331. it('updateProductVariants throws with an invalid variant id', async () => {
  332. try {
  333. await client.query<UpdateProductVariants, UpdateProductVariantsVariables>(
  334. UPDATE_PRODUCT_VARIANTS,
  335. {
  336. input: [
  337. {
  338. id: '999',
  339. translations: variants[0].translations,
  340. sku: 'ABC',
  341. image: 'new-image',
  342. price: 432,
  343. },
  344. ],
  345. },
  346. );
  347. fail('Should have thrown');
  348. } catch (err) {
  349. expect(err.message).toEqual(
  350. expect.stringContaining("No ProductVariant with the id '999' could be found"),
  351. );
  352. }
  353. });
  354. it('applyFacetValuesToProductVariants adds facets to variants', async () => {
  355. const result = await client.query<
  356. ApplyFacetValuesToProductVariants,
  357. ApplyFacetValuesToProductVariantsVariables
  358. >(APPLY_FACET_VALUE_TO_PRODUCT_VARIANTS, {
  359. facetValueIds: ['1', '3', '5'],
  360. productVariantIds: variants.map(v => v.id),
  361. });
  362. expect(result.applyFacetValuesToProductVariants.length).toBe(2);
  363. expect(result.applyFacetValuesToProductVariants[0].facetValues).toMatchSnapshot();
  364. expect(result.applyFacetValuesToProductVariants[1].facetValues).toMatchSnapshot();
  365. });
  366. it('applyFacetValuesToProductVariants errors with invalid facet value id', async () => {
  367. try {
  368. await client.query<
  369. ApplyFacetValuesToProductVariants,
  370. ApplyFacetValuesToProductVariantsVariables
  371. >(APPLY_FACET_VALUE_TO_PRODUCT_VARIANTS, {
  372. facetValueIds: ['999', '888'],
  373. productVariantIds: variants.map(v => v.id),
  374. });
  375. fail('Should have thrown');
  376. } catch (err) {
  377. expect(err.message).toEqual(
  378. expect.stringContaining("No FacetValue with the id '999' could be found"),
  379. );
  380. }
  381. });
  382. it('applyFacetValuesToProductVariants errors with invalid variant id', async () => {
  383. try {
  384. await client.query<
  385. ApplyFacetValuesToProductVariants,
  386. ApplyFacetValuesToProductVariantsVariables
  387. >(APPLY_FACET_VALUE_TO_PRODUCT_VARIANTS, {
  388. facetValueIds: ['1', '3', '5'],
  389. productVariantIds: ['999'],
  390. });
  391. fail('Should have thrown');
  392. } catch (err) {
  393. expect(err.message).toEqual(
  394. expect.stringContaining("No ProductVariant with the id '999' could be found"),
  395. );
  396. }
  397. });
  398. });
  399. });
  400. });