order-interceptor.e2e-spec.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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 * as CodegenShop from './graphql/generated-e2e-shop-types';
  16. import {
  17. ADD_ITEM_TO_ORDER,
  18. ADJUST_ITEM_QUANTITY,
  19. GET_ACTIVE_ORDER,
  20. REMOVE_ALL_ORDER_LINES,
  21. REMOVE_ITEM_FROM_ORDER,
  22. } from './graphql/shop-definitions';
  23. class Interceptor1 implements OrderInterceptor {
  24. willAddItemToOrderSpy = vi.fn();
  25. willAdjustOrderLineSpy = vi.fn();
  26. willRemoveItemFromOrderSpy = vi.fn();
  27. willAddItemToOrder(
  28. ctx: RequestContext,
  29. order: Order,
  30. input: WillAddItemToOrderInput,
  31. ): Promise<void | string> {
  32. this.willAddItemToOrderSpy(ctx, order, input);
  33. return Promise.resolve();
  34. }
  35. willAdjustOrderLine(
  36. ctx: RequestContext,
  37. order: Order,
  38. input: WillAdjustOrderLineInput,
  39. ): Promise<void | string> {
  40. this.willAdjustOrderLineSpy(ctx, order, input);
  41. return Promise.resolve();
  42. }
  43. willRemoveItemFromOrder(ctx: RequestContext, order: Order, orderLine: OrderLine): Promise<void | string> {
  44. this.willRemoveItemFromOrderSpy(ctx, order, orderLine);
  45. return Promise.resolve();
  46. }
  47. }
  48. class Interceptor2 implements OrderInterceptor {
  49. async willAddItemToOrder(ctx: RequestContext, order: Order, input: WillAddItemToOrderInput) {
  50. if (input.productVariant.id === 2 && input.quantity < 2) {
  51. return 'Quantity must be at least 2';
  52. }
  53. }
  54. async willAdjustOrderLine(
  55. ctx: RequestContext,
  56. order: Order,
  57. input: WillAdjustOrderLineInput,
  58. ): Promise<void | string> {
  59. if (input.orderLine.productVariant.id === 2 && input.quantity < 2) {
  60. return 'Quantity must be at least 2';
  61. }
  62. }
  63. async willRemoveItemFromOrder(
  64. ctx: RequestContext,
  65. order: Order,
  66. orderLine: OrderLine,
  67. ): Promise<void | string> {
  68. const overridden = ctx.req?.query?.overridden;
  69. if (overridden) {
  70. return;
  71. }
  72. if (orderLine.productVariant.id === 2) {
  73. return 'Cannot remove this item';
  74. }
  75. }
  76. }
  77. type OrderSuccessResult =
  78. | CodegenShop.UpdatedOrderFragment
  79. | CodegenShop.TestOrderFragmentFragment
  80. | CodegenShop.TestOrderWithPaymentsFragment
  81. | CodegenShop.ActiveOrderCustomerFragment
  82. | CodegenShop.OrderWithAddressesFragment;
  83. const orderResultGuard: ErrorResultGuard<OrderSuccessResult> = createErrorResultGuard(input => !!input.lines);
  84. describe('Order interceptor', () => {
  85. const interceptor1 = new Interceptor1();
  86. const interceptor2 = new Interceptor2();
  87. const { server, adminClient, shopClient } = createTestEnvironment(
  88. mergeConfig(testConfig(), {
  89. orderOptions: {
  90. orderInterceptors: [interceptor1, interceptor2],
  91. },
  92. }),
  93. );
  94. beforeAll(async () => {
  95. await server.init({
  96. initialData,
  97. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  98. customerCount: 1,
  99. });
  100. }, TEST_SETUP_TIMEOUT_MS);
  101. afterAll(async () => {
  102. await server.destroy();
  103. });
  104. it('willAddItemToOrder', async () => {
  105. const { addItemToOrder } = await shopClient.query<
  106. CodegenShop.AddItemToOrderMutation,
  107. CodegenShop.AddItemToOrderMutationVariables
  108. >(ADD_ITEM_TO_ORDER, {
  109. productVariantId: 'T_1',
  110. quantity: 1,
  111. });
  112. orderResultGuard.assertSuccess(addItemToOrder);
  113. expect(addItemToOrder.lines.length).toBe(1);
  114. expect(interceptor1.willAddItemToOrderSpy).toHaveBeenCalled();
  115. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][0]).toBeInstanceOf(RequestContext);
  116. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][1]).toBeInstanceOf(Order);
  117. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][2].quantity).toBe(1);
  118. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][2].productVariant.id).toBe(1);
  119. expect(interceptor1.willAddItemToOrderSpy.mock.calls[0][2].customFields).toBeUndefined();
  120. });
  121. it('willAdjustOrderLine', async () => {
  122. const { adjustOrderLine } = await shopClient.query<
  123. CodegenShop.AdjustItemQuantityMutation,
  124. CodegenShop.AdjustItemQuantityMutationVariables
  125. >(ADJUST_ITEM_QUANTITY, {
  126. orderLineId: 'T_1',
  127. quantity: 2,
  128. });
  129. orderResultGuard.assertSuccess(adjustOrderLine);
  130. expect(interceptor1.willAdjustOrderLineSpy).toHaveBeenCalled();
  131. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][0]).toBeInstanceOf(RequestContext);
  132. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][1]).toBeInstanceOf(Order);
  133. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][2].quantity).toBe(2);
  134. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][2].orderLine.id).toBe(1);
  135. expect(interceptor1.willAdjustOrderLineSpy.mock.calls[0][2].customFields).toBeUndefined();
  136. });
  137. it('willRemoveItemFromOrder when removing an item', async () => {
  138. const { removeOrderLine } = await shopClient.query<
  139. CodegenShop.RemoveItemFromOrderMutation,
  140. CodegenShop.RemoveItemFromOrderMutationVariables
  141. >(REMOVE_ITEM_FROM_ORDER, {
  142. orderLineId: 'T_1',
  143. });
  144. orderResultGuard.assertSuccess(removeOrderLine);
  145. expect(interceptor1.willRemoveItemFromOrderSpy).toHaveBeenCalled();
  146. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][0]).toBeInstanceOf(RequestContext);
  147. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][1]).toBeInstanceOf(Order);
  148. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][2].productVariant.id).toEqual(1);
  149. });
  150. it('willRemoveItemFromOrder when removing all items', async () => {
  151. const { addItemToOrder: addFirstItem } = await shopClient.query<
  152. CodegenShop.AddItemToOrderMutation,
  153. CodegenShop.AddItemToOrderMutationVariables
  154. >(ADD_ITEM_TO_ORDER, {
  155. productVariantId: 'T_1',
  156. quantity: 2,
  157. });
  158. orderResultGuard.assertSuccess(addFirstItem);
  159. const { addItemToOrder: addSecondItem } = await shopClient.query<
  160. CodegenShop.AddItemToOrderMutation,
  161. CodegenShop.AddItemToOrderMutationVariables
  162. >(ADD_ITEM_TO_ORDER, {
  163. productVariantId: 'T_2',
  164. quantity: 2,
  165. });
  166. orderResultGuard.assertSuccess(addSecondItem);
  167. interceptor1.willRemoveItemFromOrderSpy.mockClear();
  168. const { removeAllOrderLines } = await shopClient.query<
  169. CodegenShop.RemoveAllOrderLinesMutation,
  170. CodegenShop.RemoveAllOrderLinesMutationVariables
  171. >(REMOVE_ALL_ORDER_LINES, undefined, {
  172. overridden: 1,
  173. });
  174. orderResultGuard.assertSuccess(removeAllOrderLines);
  175. expect(interceptor1.willRemoveItemFromOrderSpy).toHaveBeenCalledTimes(2);
  176. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][0]).toBeInstanceOf(RequestContext);
  177. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][1]).toBeInstanceOf(Order);
  178. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[0][2].productVariant.id).toEqual(1);
  179. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[1][0]).toBeInstanceOf(RequestContext);
  180. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[1][1]).toBeInstanceOf(Order);
  181. expect(interceptor1.willRemoveItemFromOrderSpy.mock.calls[1][2].productVariant.id).toEqual(2);
  182. });
  183. it('willAddItemToOrder with error', async () => {
  184. const { addItemToOrder } = await shopClient.query<
  185. CodegenShop.AddItemToOrderMutation,
  186. CodegenShop.AddItemToOrderMutationVariables
  187. >(ADD_ITEM_TO_ORDER, {
  188. productVariantId: 'T_2',
  189. quantity: 1,
  190. });
  191. orderResultGuard.assertErrorResult(addItemToOrder);
  192. expect(addItemToOrder.message).toBe('An error occurred when attempting to modify the Order');
  193. expect(addItemToOrder.interceptorError).toBe('Quantity must be at least 2');
  194. });
  195. it('item was not added to order', async () => {
  196. const { activeOrder } = await shopClient.query<CodegenShop.GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  197. orderResultGuard.assertSuccess(activeOrder);
  198. expect(activeOrder.lines.length).toBe(0);
  199. });
  200. it('add item that passes interceptor check', async () => {
  201. const { addItemToOrder } = await shopClient.query<
  202. CodegenShop.AddItemToOrderMutation,
  203. CodegenShop.AddItemToOrderMutationVariables
  204. >(ADD_ITEM_TO_ORDER, {
  205. productVariantId: 'T_2',
  206. quantity: 2,
  207. });
  208. orderResultGuard.assertSuccess(addItemToOrder);
  209. expect(addItemToOrder.lines.length).toBe(1);
  210. });
  211. it('willAdjustOrderLine with error', async () => {
  212. const { adjustOrderLine } = await shopClient.query<
  213. CodegenShop.AdjustItemQuantityMutation,
  214. CodegenShop.AdjustItemQuantityMutationVariables
  215. >(ADJUST_ITEM_QUANTITY, {
  216. orderLineId: 'T_4',
  217. quantity: 1,
  218. });
  219. orderResultGuard.assertErrorResult(adjustOrderLine);
  220. expect(adjustOrderLine.message).toBe('An error occurred when attempting to modify the Order');
  221. expect(adjustOrderLine.interceptorError).toBe('Quantity must be at least 2');
  222. });
  223. it('item was not adjusted', async () => {
  224. const { activeOrder } = await shopClient.query<CodegenShop.GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  225. orderResultGuard.assertSuccess(activeOrder);
  226. expect(activeOrder.lines.length).toBe(1);
  227. expect(activeOrder.lines[0].quantity).toBe(2);
  228. });
  229. it('adjust item that passes interceptor check', async () => {
  230. const { adjustOrderLine } = await shopClient.query<
  231. CodegenShop.AdjustItemQuantityMutation,
  232. CodegenShop.AdjustItemQuantityMutationVariables
  233. >(ADJUST_ITEM_QUANTITY, {
  234. orderLineId: 'T_4',
  235. quantity: 5,
  236. });
  237. orderResultGuard.assertSuccess(adjustOrderLine);
  238. expect(adjustOrderLine.lines.length).toBe(1);
  239. expect(adjustOrderLine.lines[0].quantity).toBe(5);
  240. });
  241. it('willRemoveItemFromOrder with error', async () => {
  242. const { removeOrderLine } = await shopClient.query<
  243. CodegenShop.RemoveItemFromOrderMutation,
  244. CodegenShop.RemoveItemFromOrderMutationVariables
  245. >(REMOVE_ITEM_FROM_ORDER, {
  246. orderLineId: 'T_4',
  247. });
  248. orderResultGuard.assertErrorResult(removeOrderLine);
  249. expect(removeOrderLine.message).toBe('An error occurred when attempting to modify the Order');
  250. expect(removeOrderLine.interceptorError).toBe('Cannot remove this item');
  251. });
  252. it('item was not removed', async () => {
  253. const { activeOrder } = await shopClient.query<CodegenShop.GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  254. orderResultGuard.assertSuccess(activeOrder);
  255. expect(activeOrder.lines.length).toBe(1);
  256. expect(activeOrder.lines[0].quantity).toBe(5);
  257. });
  258. it('remove item that passes interceptor check', async () => {
  259. const { removeOrderLine } = await shopClient.query<
  260. CodegenShop.RemoveItemFromOrderMutation,
  261. CodegenShop.RemoveItemFromOrderMutationVariables
  262. >(
  263. REMOVE_ITEM_FROM_ORDER,
  264. {
  265. orderLineId: 'T_4',
  266. },
  267. {
  268. overridden: 1,
  269. },
  270. );
  271. orderResultGuard.assertSuccess(removeOrderLine);
  272. expect(removeOrderLine.lines.length).toBe(0);
  273. });
  274. it('item was removed', async () => {
  275. const { activeOrder } = await shopClient.query<CodegenShop.GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  276. orderResultGuard.assertSuccess(activeOrder);
  277. expect(activeOrder.lines.length).toBe(0);
  278. });
  279. });