guest-checkout-strategy.e2e-spec.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import { CreateCustomerInput, SetCustomerForOrderResult } from '@vendure/common/lib/generated-shop-types';
  2. import {
  3. GuestCheckoutStrategy,
  4. Order,
  5. RequestContext,
  6. ErrorResultUnion,
  7. Customer,
  8. CustomerService,
  9. GuestCheckoutError,
  10. Injector,
  11. TransactionalConnection,
  12. ChannelService,
  13. } from '@vendure/core';
  14. import {
  15. createErrorResultGuard,
  16. createTestEnvironment,
  17. ErrorResultGuard,
  18. SimpleGraphQLClient,
  19. } from '@vendure/testing';
  20. import path from 'path';
  21. import { IsNull } from 'typeorm';
  22. import { it, afterAll, beforeAll, describe, expect } from 'vitest';
  23. import { initialData } from '../../../e2e-common/e2e-initial-data';
  24. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  25. import { AlreadyLoggedInError } from '../src/common/error/generated-graphql-shop-errors';
  26. import { CustomerEvent } from '../src/index';
  27. import { testSuccessfulPaymentMethod } from './fixtures/test-payment-methods';
  28. import * as Codegen from './graphql/generated-e2e-admin-types';
  29. import * as CodegenShop from './graphql/generated-e2e-shop-types';
  30. import { GET_CUSTOMER_LIST, GET_PRODUCTS_WITH_VARIANT_PRICES } from './graphql/shared-definitions';
  31. import { ADD_ITEM_TO_ORDER, SET_CUSTOMER } from './graphql/shop-definitions';
  32. class TestGuestCheckoutStrategy implements GuestCheckoutStrategy {
  33. static allowGuestCheckout = true;
  34. static allowGuestCheckoutForRegisteredCustomers = true;
  35. static createNewCustomerOnEmailAddressConflict = false;
  36. private customerService: CustomerService;
  37. private connection: TransactionalConnection;
  38. private channelService: ChannelService;
  39. init(injector: Injector) {
  40. this.customerService = injector.get(CustomerService);
  41. this.connection = injector.get(TransactionalConnection);
  42. this.channelService = injector.get(ChannelService);
  43. }
  44. async setCustomerForOrder(
  45. ctx: RequestContext,
  46. order: Order,
  47. input: CreateCustomerInput,
  48. ): Promise<ErrorResultUnion<SetCustomerForOrderResult, Customer>> {
  49. if (TestGuestCheckoutStrategy.allowGuestCheckout === false) {
  50. return new GuestCheckoutError({ errorDetail: 'Guest orders are disabled' });
  51. }
  52. if (ctx.activeUserId) {
  53. return new AlreadyLoggedInError();
  54. }
  55. if (TestGuestCheckoutStrategy.createNewCustomerOnEmailAddressConflict === true) {
  56. const existing = await this.connection.getRepository(ctx, Customer).findOne({
  57. relations: ['channels'],
  58. where: {
  59. emailAddress: input.emailAddress,
  60. deletedAt: IsNull(),
  61. },
  62. });
  63. if (existing) {
  64. const newCustomer = await this.connection
  65. .getRepository(ctx, Customer)
  66. .save(new Customer(input));
  67. await this.channelService.assignToCurrentChannel(newCustomer, ctx);
  68. return newCustomer;
  69. }
  70. }
  71. const errorOnExistingUser = !TestGuestCheckoutStrategy.allowGuestCheckoutForRegisteredCustomers;
  72. const customer = await this.customerService.createOrUpdate(ctx, input, errorOnExistingUser);
  73. return customer;
  74. }
  75. }
  76. describe('Order taxes', () => {
  77. const { server, adminClient, shopClient } = createTestEnvironment({
  78. ...testConfig(),
  79. orderOptions: {
  80. guestCheckoutStrategy: new TestGuestCheckoutStrategy(),
  81. },
  82. paymentOptions: {
  83. paymentMethodHandlers: [testSuccessfulPaymentMethod],
  84. },
  85. });
  86. let customers: Codegen.GetCustomerListQuery['customers']['items'];
  87. const orderResultGuard: ErrorResultGuard<CodegenShop.ActiveOrderCustomerFragment> =
  88. createErrorResultGuard(input => !!input.lines);
  89. beforeAll(async () => {
  90. await server.init({
  91. initialData: {
  92. ...initialData,
  93. paymentMethods: [
  94. {
  95. name: testSuccessfulPaymentMethod.code,
  96. handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
  97. },
  98. ],
  99. },
  100. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  101. customerCount: 2,
  102. });
  103. await adminClient.asSuperAdmin();
  104. const result = await adminClient.query<Codegen.GetCustomerListQuery>(GET_CUSTOMER_LIST);
  105. customers = result.customers.items;
  106. }, TEST_SETUP_TIMEOUT_MS);
  107. afterAll(async () => {
  108. await server.destroy();
  109. });
  110. it('with guest checkout disabled', async () => {
  111. TestGuestCheckoutStrategy.allowGuestCheckout = false;
  112. await shopClient.asAnonymousUser();
  113. await addItemToOrder(shopClient);
  114. const { setCustomerForOrder } = await shopClient.query<
  115. CodegenShop.SetCustomerForOrderMutation,
  116. CodegenShop.SetCustomerForOrderMutationVariables
  117. >(SET_CUSTOMER, {
  118. input: {
  119. emailAddress: 'guest@test.com',
  120. firstName: 'Guest',
  121. lastName: 'User',
  122. },
  123. });
  124. orderResultGuard.assertErrorResult(setCustomerForOrder);
  125. expect(setCustomerForOrder.errorCode).toBe('GUEST_CHECKOUT_ERROR');
  126. expect((setCustomerForOrder as any).errorDetail).toBe('Guest orders are disabled');
  127. });
  128. it('with guest checkout enabled', async () => {
  129. TestGuestCheckoutStrategy.allowGuestCheckout = true;
  130. await shopClient.asAnonymousUser();
  131. await addItemToOrder(shopClient);
  132. const { setCustomerForOrder } = await shopClient.query<
  133. CodegenShop.SetCustomerForOrderMutation,
  134. CodegenShop.SetCustomerForOrderMutationVariables
  135. >(SET_CUSTOMER, {
  136. input: {
  137. emailAddress: 'guest@test.com',
  138. firstName: 'Guest',
  139. lastName: 'User',
  140. },
  141. });
  142. orderResultGuard.assertSuccess(setCustomerForOrder);
  143. expect(setCustomerForOrder.customer?.emailAddress).toBe('guest@test.com');
  144. });
  145. it('with guest checkout for registered customers disabled', async () => {
  146. TestGuestCheckoutStrategy.allowGuestCheckoutForRegisteredCustomers = false;
  147. await shopClient.asAnonymousUser();
  148. await addItemToOrder(shopClient);
  149. const { setCustomerForOrder } = await shopClient.query<
  150. CodegenShop.SetCustomerForOrderMutation,
  151. CodegenShop.SetCustomerForOrderMutationVariables
  152. >(SET_CUSTOMER, {
  153. input: {
  154. emailAddress: customers[0].emailAddress,
  155. firstName: customers[0].firstName,
  156. lastName: customers[0].lastName,
  157. },
  158. });
  159. orderResultGuard.assertErrorResult(setCustomerForOrder);
  160. expect(setCustomerForOrder.errorCode).toBe('EMAIL_ADDRESS_CONFLICT_ERROR');
  161. });
  162. it('with guest checkout for registered customers enabled', async () => {
  163. TestGuestCheckoutStrategy.allowGuestCheckoutForRegisteredCustomers = true;
  164. await shopClient.asAnonymousUser();
  165. await addItemToOrder(shopClient);
  166. const { setCustomerForOrder } = await shopClient.query<
  167. CodegenShop.SetCustomerForOrderMutation,
  168. CodegenShop.SetCustomerForOrderMutationVariables
  169. >(SET_CUSTOMER, {
  170. input: {
  171. emailAddress: customers[0].emailAddress,
  172. firstName: customers[0].firstName,
  173. lastName: customers[0].lastName,
  174. },
  175. });
  176. orderResultGuard.assertSuccess(setCustomerForOrder);
  177. expect(setCustomerForOrder.customer?.emailAddress).toBe(customers[0].emailAddress);
  178. expect(setCustomerForOrder.customer?.id).toBe(customers[0].id);
  179. });
  180. it('create new customer on email address conflict', async () => {
  181. TestGuestCheckoutStrategy.createNewCustomerOnEmailAddressConflict = true;
  182. await shopClient.asAnonymousUser();
  183. await addItemToOrder(shopClient);
  184. const { setCustomerForOrder } = await shopClient.query<
  185. CodegenShop.SetCustomerForOrderMutation,
  186. CodegenShop.SetCustomerForOrderMutationVariables
  187. >(SET_CUSTOMER, {
  188. input: {
  189. emailAddress: customers[0].emailAddress,
  190. firstName: customers[0].firstName,
  191. lastName: customers[0].lastName,
  192. },
  193. });
  194. orderResultGuard.assertSuccess(setCustomerForOrder);
  195. expect(setCustomerForOrder.customer?.emailAddress).toBe(customers[0].emailAddress);
  196. expect(setCustomerForOrder.customer?.id).not.toBe(customers[0].id);
  197. });
  198. it('when already logged in', async () => {
  199. await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
  200. await addItemToOrder(shopClient);
  201. const { setCustomerForOrder } = await shopClient.query<
  202. CodegenShop.SetCustomerForOrderMutation,
  203. CodegenShop.SetCustomerForOrderMutationVariables
  204. >(SET_CUSTOMER, {
  205. input: {
  206. emailAddress: customers[0].emailAddress,
  207. firstName: customers[0].firstName,
  208. lastName: customers[0].lastName,
  209. },
  210. });
  211. orderResultGuard.assertErrorResult(setCustomerForOrder);
  212. expect(setCustomerForOrder.errorCode).toBe('ALREADY_LOGGED_IN_ERROR');
  213. });
  214. });
  215. async function addItemToOrder(shopClient: SimpleGraphQLClient) {
  216. await shopClient.query<CodegenShop.AddItemToOrderMutation, CodegenShop.AddItemToOrderMutationVariables>(
  217. ADD_ITEM_TO_ORDER,
  218. {
  219. productVariantId: 'T_1',
  220. quantity: 1,
  221. },
  222. );
  223. }