order-interceptor.e2e-spec.ts 10 KB

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