order-merge.e2e-spec.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /* tslint:disable:no-non-null-assertion */
  2. import {
  3. mergeConfig,
  4. MergedOrderLine,
  5. MergeOrdersStrategy,
  6. Order,
  7. OrderMergeStrategy,
  8. RequestContext,
  9. UseExistingStrategy,
  10. UseGuestIfExistingEmptyStrategy,
  11. UseGuestStrategy,
  12. } from '@vendure/core';
  13. import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
  14. import gql from 'graphql-tag';
  15. import path from 'path';
  16. import { initialData } from '../../../e2e-common/e2e-initial-data';
  17. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  18. import { AttemptLogin, GetCustomerList } from './graphql/generated-e2e-admin-types';
  19. import {
  20. AddItemToOrder,
  21. TestOrderFragmentFragment,
  22. UpdatedOrderFragment,
  23. } from './graphql/generated-e2e-shop-types';
  24. import { ATTEMPT_LOGIN, GET_CUSTOMER_LIST } from './graphql/shared-definitions';
  25. import { TEST_ORDER_FRAGMENT } from './graphql/shop-definitions';
  26. import { sortById } from './utils/test-order-utils';
  27. /**
  28. * Allows us to change the active OrderMergeStrategy per-test and delegates to the current
  29. * activeStrategy.
  30. */
  31. class DelegateMergeStrategy implements OrderMergeStrategy {
  32. static activeStrategy: OrderMergeStrategy = new MergeOrdersStrategy();
  33. merge(ctx: RequestContext, guestOrder: Order, existingOrder: Order): MergedOrderLine[] {
  34. return DelegateMergeStrategy.activeStrategy.merge(ctx, guestOrder, existingOrder);
  35. }
  36. }
  37. type AddItemToOrderWithCustomFields = AddItemToOrder.Variables & {
  38. customFields?: { inscription?: string };
  39. };
  40. describe('Order merging', () => {
  41. type OrderSuccessResult = UpdatedOrderFragment | TestOrderFragmentFragment;
  42. const orderResultGuard: ErrorResultGuard<OrderSuccessResult> = createErrorResultGuard(
  43. input => !!input.lines,
  44. );
  45. let customers: GetCustomerList.Items[];
  46. const { server, shopClient, adminClient } = createTestEnvironment(
  47. mergeConfig(testConfig(), {
  48. orderOptions: {
  49. mergeStrategy: new DelegateMergeStrategy(),
  50. },
  51. customFields: {
  52. OrderLine: [{ name: 'inscription', type: 'string' }],
  53. },
  54. }),
  55. );
  56. beforeAll(async () => {
  57. await server.init({
  58. initialData,
  59. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  60. customerCount: 10,
  61. });
  62. await adminClient.asSuperAdmin();
  63. const result = await adminClient.query<GetCustomerList.Query>(GET_CUSTOMER_LIST);
  64. customers = result.customers.items;
  65. }, TEST_SETUP_TIMEOUT_MS);
  66. afterAll(async () => {
  67. await server.destroy();
  68. });
  69. async function testMerge(options: {
  70. strategy: OrderMergeStrategy;
  71. customerEmailAddress: string;
  72. existingOrderLines: AddItemToOrderWithCustomFields[];
  73. guestOrderLines: AddItemToOrderWithCustomFields[];
  74. }): Promise<{ lines: any[] }> {
  75. const { strategy, customerEmailAddress, existingOrderLines, guestOrderLines } = options;
  76. DelegateMergeStrategy.activeStrategy = strategy;
  77. await shopClient.asUserWithCredentials(customerEmailAddress, 'test');
  78. for (const line of existingOrderLines) {
  79. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrderWithCustomFields>(
  80. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  81. line,
  82. );
  83. }
  84. await shopClient.asAnonymousUser();
  85. for (const line of guestOrderLines) {
  86. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrderWithCustomFields>(
  87. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  88. line,
  89. );
  90. }
  91. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  92. username: customerEmailAddress,
  93. password: 'test',
  94. });
  95. const { activeOrder } = await shopClient.query(GET_ACTIVE_ORDER_WITH_CUSTOM_FIELDS);
  96. return activeOrder;
  97. }
  98. it('MergeOrdersStrategy adds new line', async () => {
  99. const result = await testMerge({
  100. strategy: new MergeOrdersStrategy(),
  101. customerEmailAddress: customers[0].emailAddress,
  102. existingOrderLines: [{ productVariantId: 'T_1', quantity: 1 }],
  103. guestOrderLines: [{ productVariantId: 'T_2', quantity: 1 }],
  104. });
  105. expect(
  106. result.lines.map(line => ({ productVariantId: line.productVariant.id, quantity: line.quantity })),
  107. ).toEqual([
  108. { productVariantId: 'T_1', quantity: 1 },
  109. { productVariantId: 'T_2', quantity: 1 },
  110. ]);
  111. });
  112. it('MergeOrdersStrategy uses guest quantity', async () => {
  113. const result = await testMerge({
  114. strategy: new MergeOrdersStrategy(),
  115. customerEmailAddress: customers[1].emailAddress,
  116. existingOrderLines: [{ productVariantId: 'T_1', quantity: 1 }],
  117. guestOrderLines: [{ productVariantId: 'T_1', quantity: 3 }],
  118. });
  119. expect(
  120. result.lines.map(line => ({ productVariantId: line.productVariant.id, quantity: line.quantity })),
  121. ).toEqual([{ productVariantId: 'T_1', quantity: 3 }]);
  122. });
  123. it('MergeOrdersStrategy accounts for customFields', async () => {
  124. const result = await testMerge({
  125. strategy: new MergeOrdersStrategy(),
  126. customerEmailAddress: customers[2].emailAddress,
  127. existingOrderLines: [
  128. { productVariantId: 'T_1', quantity: 1, customFields: { inscription: 'foo' } },
  129. ],
  130. guestOrderLines: [{ productVariantId: 'T_1', quantity: 3, customFields: { inscription: 'bar' } }],
  131. });
  132. expect(
  133. result.lines.sort(sortById).map(line => ({
  134. productVariantId: line.productVariant.id,
  135. quantity: line.quantity,
  136. customFields: line.customFields,
  137. })),
  138. ).toEqual([
  139. { productVariantId: 'T_1', quantity: 1, customFields: { inscription: 'foo' } },
  140. { productVariantId: 'T_1', quantity: 3, customFields: { inscription: 'bar' } },
  141. ]);
  142. });
  143. it('UseGuestStrategy', async () => {
  144. const result = await testMerge({
  145. strategy: new UseGuestStrategy(),
  146. customerEmailAddress: customers[3].emailAddress,
  147. existingOrderLines: [
  148. { productVariantId: 'T_1', quantity: 1 },
  149. { productVariantId: 'T_3', quantity: 1 },
  150. ],
  151. guestOrderLines: [{ productVariantId: 'T_5', quantity: 3 }],
  152. });
  153. expect(
  154. result.lines.sort(sortById).map(line => ({
  155. productVariantId: line.productVariant.id,
  156. quantity: line.quantity,
  157. })),
  158. ).toEqual([{ productVariantId: 'T_5', quantity: 3 }]);
  159. });
  160. it('UseGuestIfExistingEmptyStrategy with empty existing', async () => {
  161. const result = await testMerge({
  162. strategy: new UseGuestIfExistingEmptyStrategy(),
  163. customerEmailAddress: customers[4].emailAddress,
  164. existingOrderLines: [],
  165. guestOrderLines: [{ productVariantId: 'T_2', quantity: 3 }],
  166. });
  167. expect(
  168. result.lines.sort(sortById).map(line => ({
  169. productVariantId: line.productVariant.id,
  170. quantity: line.quantity,
  171. })),
  172. ).toEqual([{ productVariantId: 'T_2', quantity: 3 }]);
  173. });
  174. it('UseGuestIfExistingEmptyStrategy with non-empty existing', async () => {
  175. const result = await testMerge({
  176. strategy: new UseGuestIfExistingEmptyStrategy(),
  177. customerEmailAddress: customers[5].emailAddress,
  178. existingOrderLines: [{ productVariantId: 'T_5', quantity: 5 }],
  179. guestOrderLines: [{ productVariantId: 'T_2', quantity: 3 }],
  180. });
  181. expect(
  182. result.lines.sort(sortById).map(line => ({
  183. productVariantId: line.productVariant.id,
  184. quantity: line.quantity,
  185. })),
  186. ).toEqual([{ productVariantId: 'T_5', quantity: 5 }]);
  187. });
  188. it('UseExistingStrategy', async () => {
  189. const result = await testMerge({
  190. strategy: new UseExistingStrategy(),
  191. customerEmailAddress: customers[6].emailAddress,
  192. existingOrderLines: [{ productVariantId: 'T_8', quantity: 1 }],
  193. guestOrderLines: [{ productVariantId: 'T_2', quantity: 3 }],
  194. });
  195. expect(
  196. result.lines.sort(sortById).map(line => ({
  197. productVariantId: line.productVariant.id,
  198. quantity: line.quantity,
  199. })),
  200. ).toEqual([{ productVariantId: 'T_8', quantity: 1 }]);
  201. });
  202. });
  203. export const ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS = gql`
  204. mutation AddItemToOrder(
  205. $productVariantId: ID!
  206. $quantity: Int!
  207. $customFields: OrderLineCustomFieldsInput
  208. ) {
  209. addItemToOrder(
  210. productVariantId: $productVariantId
  211. quantity: $quantity
  212. customFields: $customFields
  213. ) {
  214. ... on Order {
  215. id
  216. }
  217. ... on ErrorResult {
  218. errorCode
  219. message
  220. }
  221. }
  222. }
  223. `;
  224. export const GET_ACTIVE_ORDER_WITH_CUSTOM_FIELDS = gql`
  225. query GetActiveOrder {
  226. activeOrder {
  227. ...TestOrderFragment
  228. ... on Order {
  229. lines {
  230. customFields {
  231. inscription
  232. }
  233. }
  234. }
  235. }
  236. }
  237. ${TEST_ORDER_FRAGMENT}
  238. `;