order-interceptor.e2e-spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import {
  2. mergeConfig,
  3. Order,
  4. OrderInterceptor,
  5. OrderLine,
  6. RequestContext,
  7. WillAddItemToOrderInput,
  8. WillAdjustOrderLineInput,
  9. } from '@vendure/core';
  10. import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
  11. import path from 'path';
  12. import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
  13. import { initialData } from '../../../e2e-common/e2e-initial-data';
  14. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  15. import { FragmentOf } from './graphql/graphql-shop';
  16. import {
  17. addItemToOrderDocument,
  18. adjustItemQuantityDocument,
  19. getActiveOrderDocument,
  20. removeAllOrderLinesDocument,
  21. removeItemFromOrderDocument,
  22. testOrderFragment,
  23. } from './graphql/shop-definitions';
  24. class Interceptor1 implements OrderInterceptor {
  25. willAddItemToOrderSpy = vi.fn();
  26. willAdjustOrderLineSpy = vi.fn();
  27. willRemoveItemFromOrderSpy = vi.fn();
  28. willAddItemToOrder(
  29. ctx: RequestContext,
  30. order: Order,
  31. input: WillAddItemToOrderInput,
  32. ): Promise<void | string> {
  33. this.willAddItemToOrderSpy(ctx, order, input);
  34. return Promise.resolve();
  35. }
  36. willAdjustOrderLine(
  37. ctx: RequestContext,
  38. order: Order,
  39. input: WillAdjustOrderLineInput,
  40. ): Promise<void | string> {
  41. this.willAdjustOrderLineSpy(ctx, order, input);
  42. return Promise.resolve();
  43. }
  44. willRemoveItemFromOrder(ctx: RequestContext, order: Order, orderLine: OrderLine): Promise<void | string> {
  45. this.willRemoveItemFromOrderSpy(ctx, order, orderLine);
  46. return Promise.resolve();
  47. }
  48. }
  49. class Interceptor2 implements OrderInterceptor {
  50. async willAddItemToOrder(ctx: RequestContext, order: Order, input: WillAddItemToOrderInput) {
  51. if (input.productVariant.id === 2 && input.quantity < 2) {
  52. return 'Quantity must be at least 2';
  53. }
  54. }
  55. async willAdjustOrderLine(
  56. ctx: RequestContext,
  57. order: Order,
  58. input: WillAdjustOrderLineInput,
  59. ): Promise<void | string> {
  60. if (input.orderLine.productVariant.id === 2 && input.quantity < 2) {
  61. return 'Quantity must be at least 2';
  62. }
  63. }
  64. async willRemoveItemFromOrder(
  65. ctx: RequestContext,
  66. order: Order,
  67. orderLine: OrderLine,
  68. ): Promise<void | string> {
  69. const overridden = ctx.req?.query?.overridden;
  70. if (overridden) {
  71. return;
  72. }
  73. if (orderLine.productVariant.id === 2) {
  74. return 'Cannot remove this item';
  75. }
  76. }
  77. }
  78. type TestOrderFragmentType = FragmentOf<typeof testOrderFragment>;
  79. type OrderSuccessResult = TestOrderFragmentType;
  80. const orderResultGuard: ErrorResultGuard<OrderSuccessResult> = createErrorResultGuard(input => !!input.lines);
  81. describe('Order interceptor', () => {
  82. const interceptor1 = new Interceptor1();
  83. const interceptor2 = new Interceptor2();
  84. const { server, adminClient, shopClient } = createTestEnvironment(
  85. mergeConfig(testConfig(), {
  86. orderOptions: {
  87. orderInterceptors: [interceptor1, interceptor2],
  88. },
  89. }),
  90. );
  91. beforeAll(async () => {
  92. await server.init({
  93. initialData,
  94. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  95. customerCount: 1,
  96. });
  97. }, TEST_SETUP_TIMEOUT_MS);
  98. afterAll(async () => {
  99. await server.destroy();
  100. });
  101. it('willAddItemToOrder', async () => {
  102. const { addItemToOrder } = await shopClient.query(addItemToOrderDocument, {
  103. productVariantId: 'T_1',
  104. quantity: 1,
  105. });
  106. orderResultGuard.assertSuccess(addItemToOrder);
  107. expect(addItemToOrder.lines.length).toBe(1);
  108. expect(interceptor1.willAddItemToOrderSpy).toHaveBeenCalled();
  109. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][0]).toBeInstanceOf(RequestContext);
  110. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][1]).toBeInstanceOf(Order);
  111. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][2].quantity).toBe(1);
  112. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][2].productVariant.id).toBe(1);
  113. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][2].customFields).toBeUndefined();
  114. });
  115. it('willAdjustOrderLine', async () => {
  116. const { adjustOrderLine } = await shopClient.query(adjustItemQuantityDocument, {
  117. orderLineId: 'T_1',
  118. quantity: 2,
  119. });
  120. orderResultGuard.assertSuccess(adjustOrderLine);
  121. expect(interceptor1.willAdjustOrderLineSpy).toHaveBeenCalled();
  122. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][0]).toBeInstanceOf(RequestContext);
  123. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][1]).toBeInstanceOf(Order);
  124. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][2].quantity).toBe(2);
  125. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][2].orderLine.id).toBe(1);
  126. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][2].customFields).toBeUndefined();
  127. });
  128. it('willRemoveItemFromOrder when removing an item', async () => {
  129. const { removeOrderLine } = await shopClient.query(removeItemFromOrderDocument, {
  130. orderLineId: 'T_1',
  131. });
  132. orderResultGuard.assertSuccess(removeOrderLine);
  133. expect(interceptor1.willRemoveItemFromOrderSpy).toHaveBeenCalled();
  134. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][0]).toBeInstanceOf(RequestContext);
  135. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][1]).toBeInstanceOf(Order);
  136. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][2].productVariant.id).toEqual(1);
  137. });
  138. it('willRemoveItemFromOrder when removing all items', async () => {
  139. const { addItemToOrder: addFirstItem } = await shopClient.query(addItemToOrderDocument, {
  140. productVariantId: 'T_1',
  141. quantity: 2,
  142. });
  143. orderResultGuard.assertSuccess(addFirstItem);
  144. const { addItemToOrder: addSecondItem } = await shopClient.query(addItemToOrderDocument, {
  145. productVariantId: 'T_2',
  146. quantity: 2,
  147. });
  148. orderResultGuard.assertSuccess(addSecondItem);
  149. interceptor1.willRemoveItemFromOrderSpy.mockClear();
  150. const { removeAllOrderLines } = await shopClient.query(removeAllOrderLinesDocument, undefined, {
  151. overridden: 1,
  152. });
  153. orderResultGuard.assertSuccess(removeAllOrderLines);
  154. expect(interceptor1.willRemoveItemFromOrderSpy).toHaveBeenCalledTimes(2);
  155. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][0]).toBeInstanceOf(RequestContext);
  156. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][1]).toBeInstanceOf(Order);
  157. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][2].productVariant.id).toEqual(1);
  158. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[1][0]).toBeInstanceOf(RequestContext);
  159. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[1][1]).toBeInstanceOf(Order);
  160. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[1][2].productVariant.id).toEqual(2);
  161. });
  162. it('willAddItemToOrder with error', async () => {
  163. const { addItemToOrder } = await shopClient.query(addItemToOrderDocument, {
  164. productVariantId: 'T_2',
  165. quantity: 1,
  166. });
  167. orderResultGuard.assertErrorResult(addItemToOrder);
  168. expect(addItemToOrder.message).toBe('An error occurred when attempting to modify the Order');
  169. expect(addItemToOrder.interceptorError).toBe('Quantity must be at least 2');
  170. });
  171. it('item was not added to order', async () => {
  172. const { activeOrder } = await shopClient.query(getActiveOrderDocument);
  173. orderResultGuard.assertSuccess(activeOrder);
  174. expect(activeOrder.lines.length).toBe(0);
  175. });
  176. it('add item that passes interceptor check', async () => {
  177. const { addItemToOrder } = await shopClient.query(addItemToOrderDocument, {
  178. productVariantId: 'T_2',
  179. quantity: 2,
  180. });
  181. orderResultGuard.assertSuccess(addItemToOrder);
  182. expect(addItemToOrder.lines.length).toBe(1);
  183. });
  184. it('willAdjustOrderLine with error', async () => {
  185. const { adjustOrderLine } = await shopClient.query(adjustItemQuantityDocument, {
  186. orderLineId: 'T_4',
  187. quantity: 1,
  188. });
  189. orderResultGuard.assertErrorResult(adjustOrderLine);
  190. expect(adjustOrderLine.message).toBe('An error occurred when attempting to modify the Order');
  191. expect(adjustOrderLine.interceptorError).toBe('Quantity must be at least 2');
  192. });
  193. it('item was not adjusted', async () => {
  194. const { activeOrder } = await shopClient.query(getActiveOrderDocument);
  195. orderResultGuard.assertSuccess(activeOrder);
  196. expect(activeOrder.lines.length).toBe(1);
  197. expect(activeOrder.lines[0].quantity).toBe(2);
  198. });
  199. it('adjust item that passes interceptor check', async () => {
  200. const { adjustOrderLine } = await shopClient.query(adjustItemQuantityDocument, {
  201. orderLineId: 'T_4',
  202. quantity: 5,
  203. });
  204. orderResultGuard.assertSuccess(adjustOrderLine);
  205. expect(adjustOrderLine.lines.length).toBe(1);
  206. expect(adjustOrderLine.lines[0].quantity).toBe(5);
  207. });
  208. it('willRemoveItemFromOrder with error', async () => {
  209. const { removeOrderLine } = await shopClient.query(removeItemFromOrderDocument, {
  210. orderLineId: 'T_4',
  211. });
  212. orderResultGuard.assertErrorResult(removeOrderLine);
  213. expect(removeOrderLine.message).toBe('An error occurred when attempting to modify the Order');
  214. expect(removeOrderLine.interceptorError).toBe('Cannot remove this item');
  215. });
  216. it('item was not removed', async () => {
  217. const { activeOrder } = await shopClient.query(getActiveOrderDocument);
  218. orderResultGuard.assertSuccess(activeOrder);
  219. expect(activeOrder.lines.length).toBe(1);
  220. expect(activeOrder.lines[0].quantity).toBe(5);
  221. });
  222. it('remove item that passes interceptor check', async () => {
  223. const { removeOrderLine } = await shopClient.query(
  224. removeItemFromOrderDocument,
  225. {
  226. orderLineId: 'T_4',
  227. },
  228. {
  229. overridden: 1,
  230. },
  231. );
  232. orderResultGuard.assertSuccess(removeOrderLine);
  233. expect(removeOrderLine.lines.length).toBe(0);
  234. });
  235. it('item was removed', async () => {
  236. const { activeOrder } = await shopClient.query(getActiveOrderDocument);
  237. orderResultGuard.assertSuccess(activeOrder);
  238. expect(activeOrder.lines.length).toBe(0);
  239. });
  240. });