product.e2e-spec.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  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. GetAssetList,
  13. GetAssetListVariables,
  14. GetProductList,
  15. GetProductListVariables,
  16. GetProductWithVariants,
  17. GetProductWithVariantsVariables,
  18. LanguageCode,
  19. RemoveOptionGroupFromProduct,
  20. RemoveOptionGroupFromProductVariables,
  21. SortOrder,
  22. UpdateProduct,
  23. UpdateProductVariables,
  24. UpdateProductVariants,
  25. UpdateProductVariantsVariables,
  26. } from 'shared/generated-types';
  27. import { omit } from 'shared/omit';
  28. import {
  29. ADD_OPTION_GROUP_TO_PRODUCT,
  30. APPLY_FACET_VALUE_TO_PRODUCT_VARIANTS,
  31. CREATE_PRODUCT,
  32. GENERATE_PRODUCT_VARIANTS,
  33. GET_ASSET_LIST,
  34. GET_PRODUCT_LIST,
  35. GET_PRODUCT_WITH_VARIANTS,
  36. REMOVE_OPTION_GROUP_FROM_PRODUCT,
  37. UPDATE_PRODUCT,
  38. UPDATE_PRODUCT_VARIANTS,
  39. } from '../../admin-ui/src/app/data/definitions/product-definitions';
  40. import { TestClient } from './test-client';
  41. import { TestServer } from './test-server';
  42. // tslint:disable:no-non-null-assertion
  43. describe('Product resolver', () => {
  44. const client = new TestClient();
  45. const server = new TestServer();
  46. beforeAll(async () => {
  47. const token = await server.init({
  48. productCount: 20,
  49. customerCount: 1,
  50. });
  51. await client.init();
  52. }, 60000);
  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(1);
  87. expect(result.products.items[0].name).toBe('en Practical Frozen Fish');
  88. });
  89. it('sorts by name', async () => {
  90. const result = await client.query<GetProductList, GetProductListVariables>(GET_PRODUCT_LIST, {
  91. languageCode: LanguageCode.en,
  92. options: {
  93. sort: {
  94. name: SortOrder.ASC,
  95. },
  96. },
  97. });
  98. expect(result.products.items.map(p => p.name)).toMatchSnapshot();
  99. });
  100. });
  101. describe('product query', () => {
  102. it('returns expected properties', async () => {
  103. const result = await client.query<GetProductWithVariants, GetProductWithVariantsVariables>(
  104. GET_PRODUCT_WITH_VARIANTS,
  105. {
  106. languageCode: LanguageCode.en,
  107. id: 'T_2',
  108. },
  109. );
  110. if (!result.product) {
  111. fail('Product not found');
  112. return;
  113. }
  114. expect(omit(result.product, ['variants'])).toMatchSnapshot();
  115. expect(result.product.variants.length).toBe(2);
  116. });
  117. it('returns null when id not found', async () => {
  118. const result = await client.query<GetProductWithVariants, GetProductWithVariantsVariables>(
  119. GET_PRODUCT_WITH_VARIANTS,
  120. {
  121. languageCode: LanguageCode.en,
  122. id: 'bad_id',
  123. },
  124. );
  125. expect(result.product).toBeNull();
  126. });
  127. });
  128. describe('product mutation', () => {
  129. let newProduct: CreateProduct_createProduct;
  130. it('createProduct creates a new Product', async () => {
  131. const result = await client.query<CreateProduct, CreateProductVariables>(CREATE_PRODUCT, {
  132. input: {
  133. translations: [
  134. {
  135. languageCode: LanguageCode.en,
  136. name: 'en Baked Potato',
  137. slug: 'en-baked-potato',
  138. description: 'A baked potato',
  139. },
  140. {
  141. languageCode: LanguageCode.de,
  142. name: 'de Baked Potato',
  143. slug: 'de-baked-potato',
  144. description: 'Eine baked Erdapfel',
  145. },
  146. ],
  147. },
  148. });
  149. newProduct = result.createProduct;
  150. expect(newProduct).toMatchSnapshot();
  151. });
  152. it('createProduct creates a new Product with assets', async () => {
  153. const assetsResult = await client.query<GetAssetList, GetAssetListVariables>(GET_ASSET_LIST);
  154. const assetIds = assetsResult.assets.items.slice(0, 2).map(a => a.id);
  155. const featuredAssetId = assetsResult.assets.items[0].id;
  156. const result = await client.query<CreateProduct, CreateProductVariables>(CREATE_PRODUCT, {
  157. input: {
  158. assetIds,
  159. featuredAssetId,
  160. translations: [
  161. {
  162. languageCode: LanguageCode.en,
  163. name: 'en Has Assets',
  164. slug: 'en-has-assets',
  165. description: 'A product with assets',
  166. },
  167. ],
  168. },
  169. });
  170. expect(result.createProduct.assets.map(a => a.id)).toEqual(assetIds);
  171. expect(result.createProduct.featuredAsset!.id).toBe(featuredAssetId);
  172. });
  173. it('updateProduct updates a Product', async () => {
  174. const result = await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
  175. input: {
  176. id: newProduct.id,
  177. translations: [
  178. {
  179. languageCode: LanguageCode.en,
  180. name: 'en Mashed Potato',
  181. slug: 'en-mashed-potato',
  182. description: 'A blob of mashed potato',
  183. },
  184. {
  185. languageCode: LanguageCode.de,
  186. name: 'de Mashed Potato',
  187. slug: 'de-mashed-potato',
  188. description: 'Eine blob von gemashed Erdapfel',
  189. },
  190. ],
  191. },
  192. });
  193. expect(result.updateProduct).toMatchSnapshot();
  194. });
  195. it('updateProduct accepts partial input', async () => {
  196. const result = await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
  197. input: {
  198. id: newProduct.id,
  199. translations: [
  200. {
  201. languageCode: LanguageCode.en,
  202. name: 'en Very Mashed Potato',
  203. },
  204. ],
  205. },
  206. });
  207. expect(result.updateProduct.translations.length).toBe(2);
  208. expect(result.updateProduct.translations[0].name).toBe('en Very Mashed Potato');
  209. expect(result.updateProduct.translations[0].description).toBe('A blob of mashed potato');
  210. expect(result.updateProduct.translations[1].name).toBe('de Mashed Potato');
  211. });
  212. it('updateProduct adds Assets to a product and sets featured asset', async () => {
  213. const assetsResult = await client.query<GetAssetList, GetAssetListVariables>(GET_ASSET_LIST);
  214. const assetIds = assetsResult.assets.items.map(a => a.id);
  215. const featuredAssetId = assetsResult.assets.items[2].id;
  216. const result = await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
  217. input: {
  218. id: newProduct.id,
  219. assetIds,
  220. featuredAssetId,
  221. },
  222. });
  223. expect(result.updateProduct.assets.map(a => a.id)).toEqual(assetIds);
  224. expect(result.updateProduct.featuredAsset!.id).toBe(featuredAssetId);
  225. });
  226. it('updateProduct sets a featured asset', async () => {
  227. const productResult = await client.query<GetProductWithVariants, GetProductWithVariantsVariables>(
  228. GET_PRODUCT_WITH_VARIANTS,
  229. {
  230. id: newProduct.id,
  231. languageCode: LanguageCode.en,
  232. },
  233. );
  234. const assets = productResult.product!.assets;
  235. const result = await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
  236. input: {
  237. id: newProduct.id,
  238. featuredAssetId: assets[0].id,
  239. },
  240. });
  241. expect(result.updateProduct.featuredAsset!.id).toBe(assets[0].id);
  242. });
  243. it('updateProduct errors with an invalid productId', async () => {
  244. try {
  245. await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
  246. input: {
  247. id: '999',
  248. translations: [
  249. {
  250. languageCode: LanguageCode.en,
  251. name: 'en Mashed Potato',
  252. slug: 'en-mashed-potato',
  253. description: 'A blob of mashed potato',
  254. },
  255. {
  256. languageCode: LanguageCode.de,
  257. name: 'de Mashed Potato',
  258. slug: 'de-mashed-potato',
  259. description: 'Eine blob von gemashed Erdapfel',
  260. },
  261. ],
  262. },
  263. });
  264. fail('Should have thrown');
  265. } catch (err) {
  266. expect(err.message).toEqual(
  267. expect.stringContaining(`No Product with the id '999' could be found`),
  268. );
  269. }
  270. });
  271. it('addOptionGroupToProduct adds an option group', async () => {
  272. const result = await client.query<AddOptionGroupToProduct, AddOptionGroupToProductVariables>(
  273. ADD_OPTION_GROUP_TO_PRODUCT,
  274. {
  275. optionGroupId: 'T_1',
  276. productId: newProduct.id,
  277. },
  278. );
  279. expect(result.addOptionGroupToProduct.optionGroups.length).toBe(1);
  280. expect(result.addOptionGroupToProduct.optionGroups[0].id).toBe('T_1');
  281. });
  282. it('addOptionGroupToProduct errors with an invalid productId', async () => {
  283. try {
  284. await client.query<AddOptionGroupToProduct, AddOptionGroupToProductVariables>(
  285. ADD_OPTION_GROUP_TO_PRODUCT,
  286. {
  287. optionGroupId: 'T_1',
  288. productId: '999',
  289. },
  290. );
  291. fail('Should have thrown');
  292. } catch (err) {
  293. expect(err.message).toEqual(
  294. expect.stringContaining(`No Product with the id '999' could be found`),
  295. );
  296. }
  297. });
  298. it('addOptionGroupToProduct errors with an invalid optionGroupId', async () => {
  299. try {
  300. await client.query<AddOptionGroupToProduct, AddOptionGroupToProductVariables>(
  301. ADD_OPTION_GROUP_TO_PRODUCT,
  302. {
  303. optionGroupId: '999',
  304. productId: newProduct.id,
  305. },
  306. );
  307. fail('Should have thrown');
  308. } catch (err) {
  309. expect(err.message).toEqual(
  310. expect.stringContaining(`No OptionGroup with the id '999' could be found`),
  311. );
  312. }
  313. });
  314. it('removeOptionGroupFromProduct removes an option group', async () => {
  315. const result = await client.query<
  316. RemoveOptionGroupFromProduct,
  317. RemoveOptionGroupFromProductVariables
  318. >(REMOVE_OPTION_GROUP_FROM_PRODUCT, {
  319. optionGroupId: '1',
  320. productId: '1',
  321. });
  322. expect(result.removeOptionGroupFromProduct.optionGroups.length).toBe(0);
  323. });
  324. it('removeOptionGroupFromProduct errors with an invalid productId', async () => {
  325. try {
  326. await client.query<RemoveOptionGroupFromProduct, RemoveOptionGroupFromProductVariables>(
  327. REMOVE_OPTION_GROUP_FROM_PRODUCT,
  328. {
  329. optionGroupId: '1',
  330. productId: '999',
  331. },
  332. );
  333. fail('Should have thrown');
  334. } catch (err) {
  335. expect(err.message).toEqual(
  336. expect.stringContaining(`No Product with the id '999' could be found`),
  337. );
  338. }
  339. });
  340. describe('variants', () => {
  341. let variants: GenerateProductVariants_generateVariantsForProduct_variants[];
  342. it('generateVariantsForProduct generates variants', async () => {
  343. const result = await client.query<GenerateProductVariants, GenerateProductVariantsVariables>(
  344. GENERATE_PRODUCT_VARIANTS,
  345. {
  346. productId: newProduct.id,
  347. defaultPrice: 123,
  348. defaultSku: 'ABC',
  349. },
  350. );
  351. variants = result.generateVariantsForProduct.variants;
  352. expect(variants.length).toBe(2);
  353. expect(variants[0].options.length).toBe(1);
  354. expect(variants[1].options.length).toBe(1);
  355. });
  356. it('generateVariantsForProduct throws with an invalid productId', async () => {
  357. try {
  358. await client.query<GenerateProductVariants, GenerateProductVariantsVariables>(
  359. GENERATE_PRODUCT_VARIANTS,
  360. {
  361. productId: '999',
  362. },
  363. );
  364. fail('Should have thrown');
  365. } catch (err) {
  366. expect(err.message).toEqual(
  367. expect.stringContaining(`No Product with the id '999' could be found`),
  368. );
  369. }
  370. });
  371. it('updateProductVariants updates variants', async () => {
  372. const firstVariant = variants[0];
  373. const result = await client.query<UpdateProductVariants, UpdateProductVariantsVariables>(
  374. UPDATE_PRODUCT_VARIANTS,
  375. {
  376. input: [
  377. {
  378. id: firstVariant.id,
  379. translations: firstVariant.translations,
  380. sku: 'ABC',
  381. price: 432,
  382. },
  383. ],
  384. },
  385. );
  386. const updatedVariant = result.updateProductVariants[0];
  387. if (!updatedVariant) {
  388. fail('no updated variant returned.');
  389. return;
  390. }
  391. expect(updatedVariant.sku).toBe('ABC');
  392. expect(updatedVariant.price).toBe(432);
  393. });
  394. it('updateProductVariants throws with an invalid variant id', async () => {
  395. try {
  396. await client.query<UpdateProductVariants, UpdateProductVariantsVariables>(
  397. UPDATE_PRODUCT_VARIANTS,
  398. {
  399. input: [
  400. {
  401. id: '999',
  402. translations: variants[0].translations,
  403. sku: 'ABC',
  404. price: 432,
  405. },
  406. ],
  407. },
  408. );
  409. fail('Should have thrown');
  410. } catch (err) {
  411. expect(err.message).toEqual(
  412. expect.stringContaining(`No ProductVariant with the id '999' could be found`),
  413. );
  414. }
  415. });
  416. it('applyFacetValuesToProductVariants adds facets to variants', async () => {
  417. const result = await client.query<
  418. ApplyFacetValuesToProductVariants,
  419. ApplyFacetValuesToProductVariantsVariables
  420. >(APPLY_FACET_VALUE_TO_PRODUCT_VARIANTS, {
  421. facetValueIds: ['1', '3', '5'],
  422. productVariantIds: variants.map(v => v.id),
  423. });
  424. expect(result.applyFacetValuesToProductVariants.length).toBe(2);
  425. expect(result.applyFacetValuesToProductVariants[0].facetValues).toMatchSnapshot();
  426. expect(result.applyFacetValuesToProductVariants[1].facetValues).toMatchSnapshot();
  427. });
  428. it('applyFacetValuesToProductVariants errors with invalid facet value id', async () => {
  429. try {
  430. await client.query<
  431. ApplyFacetValuesToProductVariants,
  432. ApplyFacetValuesToProductVariantsVariables
  433. >(APPLY_FACET_VALUE_TO_PRODUCT_VARIANTS, {
  434. facetValueIds: ['999', '888'],
  435. productVariantIds: variants.map(v => v.id),
  436. });
  437. fail('Should have thrown');
  438. } catch (err) {
  439. expect(err.message).toEqual(
  440. expect.stringContaining(`No FacetValue with the id '999' could be found`),
  441. );
  442. }
  443. });
  444. it('applyFacetValuesToProductVariants errors with invalid variant id', async () => {
  445. try {
  446. await client.query<
  447. ApplyFacetValuesToProductVariants,
  448. ApplyFacetValuesToProductVariantsVariables
  449. >(APPLY_FACET_VALUE_TO_PRODUCT_VARIANTS, {
  450. facetValueIds: ['1', '3', '5'],
  451. productVariantIds: ['999'],
  452. });
  453. fail('Should have thrown');
  454. } catch (err) {
  455. expect(err.message).toEqual(
  456. expect.stringContaining(`No ProductVariant with the id '999' could be found`),
  457. );
  458. }
  459. });
  460. });
  461. });
  462. });