order-multi-vendor.e2e-spec.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /* eslint-disable @typescript-eslint/no-non-null-assertion */
  2. import { mergeConfig } from '@vendure/core';
  3. import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
  4. import { CONNECTED_PAYMENT_METHOD_CODE } from 'dev-server/example-plugins/multivendor-plugin/constants';
  5. import { MultivendorPlugin } from 'dev-server/example-plugins/multivendor-plugin/multivendor.plugin';
  6. import path from 'path';
  7. import { afterAll, beforeAll, describe, expect, it } from 'vitest';
  8. import { initialData } from '../../../e2e-common/e2e-initial-data';
  9. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  10. import { getOrderWithSellerOrdersDocument } from './graphql/admin-definitions';
  11. import { FragmentOf } from './graphql/graphql-shop';
  12. import { assignProductToChannelDocument } from './graphql/shared-definitions';
  13. import {
  14. activeOrderCustomerDocument,
  15. addItemToOrderDocument,
  16. addPaymentDocument,
  17. getEligibleShippingMethodsDocument,
  18. registerSellerDocument,
  19. setShippingAddressDocument,
  20. setShippingMethodDocument,
  21. testOrderFragment,
  22. testOrderWithPaymentsFragment,
  23. transitionToStateDocument,
  24. updatedOrderFragment,
  25. } from './graphql/shop-definitions';
  26. declare module '@vendure/core/dist/entity/custom-entity-fields' {
  27. interface CustomShippingMethodFields {
  28. minPrice: number;
  29. maxPrice: number;
  30. }
  31. }
  32. describe('Multi-vendor orders', () => {
  33. const { server, adminClient, shopClient } = createTestEnvironment(
  34. mergeConfig(testConfig(), {
  35. plugins: [
  36. MultivendorPlugin.init({
  37. platformFeePercent: 10,
  38. platformFeeSKU: 'FEE',
  39. }),
  40. ],
  41. }),
  42. );
  43. let bobsPartsChannel: { id: string; token: string; variantIds: string[] };
  44. let alicesWaresChannel: { id: string; token: string; variantIds: string[] };
  45. let orderId: string;
  46. type OrderSuccessResult =
  47. | FragmentOf<typeof updatedOrderFragment>
  48. | FragmentOf<typeof testOrderFragment>
  49. | FragmentOf<typeof testOrderWithPaymentsFragment>
  50. | FragmentOf<typeof activeOrderCustomerDocument>;
  51. const orderResultGuard: ErrorResultGuard<OrderSuccessResult> = createErrorResultGuard(
  52. input => !!input.lines,
  53. );
  54. beforeAll(async () => {
  55. await server.init({
  56. initialData,
  57. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  58. customerCount: 3,
  59. });
  60. await adminClient.asSuperAdmin();
  61. }, TEST_SETUP_TIMEOUT_MS);
  62. afterAll(async () => {
  63. await server.destroy();
  64. });
  65. it('setup sellers', async () => {
  66. const result1 = await shopClient.query(registerSellerDocument, {
  67. input: {
  68. shopName: "Bob's Parts",
  69. seller: {
  70. firstName: 'Bob',
  71. lastName: 'Dobalina',
  72. emailAddress: 'bob@bobs-parts.com',
  73. password: 'test',
  74. },
  75. },
  76. });
  77. bobsPartsChannel = result1.registerNewSeller;
  78. expect(bobsPartsChannel.token).toBe('bobs-parts-token');
  79. const result2 = await shopClient.query(registerSellerDocument, {
  80. input: {
  81. shopName: "Alice's Wares",
  82. seller: {
  83. firstName: 'Alice',
  84. lastName: 'Smith',
  85. emailAddress: 'alice@alices-wares.com',
  86. password: 'test',
  87. },
  88. },
  89. });
  90. alicesWaresChannel = result2.registerNewSeller;
  91. expect(alicesWaresChannel.token).toBe('alices-wares-token');
  92. });
  93. it('assign products to sellers', async () => {
  94. const { assignProductsToChannel } = await adminClient.query(assignProductToChannelDocument, {
  95. input: {
  96. channelId: bobsPartsChannel.id,
  97. productIds: ['T_1'],
  98. priceFactor: 1,
  99. },
  100. });
  101. expect(assignProductsToChannel[0].channels.map(c => c.code)).toEqual([
  102. '__default_channel__',
  103. 'bobs-parts',
  104. ]);
  105. bobsPartsChannel.variantIds = assignProductsToChannel[0].variants.map(v => v.id);
  106. expect(bobsPartsChannel.variantIds).toEqual(['T_1', 'T_2', 'T_3', 'T_4']);
  107. const { assignProductsToChannel: result2 } = await adminClient.query(assignProductToChannelDocument, {
  108. input: {
  109. channelId: alicesWaresChannel.id,
  110. productIds: ['T_11'],
  111. priceFactor: 1,
  112. },
  113. });
  114. expect(result2[0].channels.map(c => c.code)).toEqual(['__default_channel__', 'alices-wares']);
  115. alicesWaresChannel.variantIds = result2[0].variants.map(v => v.id);
  116. expect(alicesWaresChannel.variantIds).toEqual(['T_22']);
  117. });
  118. it('adds items and sets shipping methods', async () => {
  119. await shopClient.asUserWithCredentials('hayden.zieme12@hotmail.com', 'test');
  120. await shopClient.query(addItemToOrderDocument, {
  121. productVariantId: bobsPartsChannel.variantIds[0],
  122. quantity: 1,
  123. });
  124. await shopClient.query(addItemToOrderDocument, {
  125. productVariantId: alicesWaresChannel.variantIds[0],
  126. quantity: 1,
  127. });
  128. await shopClient.query(setShippingAddressDocument, {
  129. input: {
  130. streetLine1: '12 the street',
  131. postalCode: '123456',
  132. countryCode: 'US',
  133. },
  134. });
  135. const { eligibleShippingMethods } = await shopClient.query(getEligibleShippingMethodsDocument);
  136. expect(eligibleShippingMethods.map(m => m.code).sort()).toEqual([
  137. 'alices-wares-shipping',
  138. 'bobs-parts-shipping',
  139. 'express-shipping',
  140. 'express-shipping-taxed',
  141. 'standard-shipping',
  142. ]);
  143. const { setOrderShippingMethod } = await shopClient.query(setShippingMethodDocument, {
  144. id: [
  145. eligibleShippingMethods.find(m => m.code === 'bobs-parts-shipping')!.id,
  146. eligibleShippingMethods.find(m => m.code === 'alices-wares-shipping')!.id,
  147. ],
  148. });
  149. orderResultGuard.assertSuccess(setOrderShippingMethod);
  150. expect(setOrderShippingMethod.shippingLines.map(l => l.shippingMethod.code).sort()).toEqual([
  151. 'alices-wares-shipping',
  152. 'bobs-parts-shipping',
  153. ]);
  154. });
  155. it('completing checkout splits order', async () => {
  156. const { transitionOrderToState } = await shopClient.query(transitionToStateDocument, {
  157. state: 'ArrangingPayment',
  158. });
  159. orderResultGuard.assertSuccess(transitionOrderToState);
  160. const { addPaymentToOrder } = await shopClient.query(addPaymentDocument, {
  161. input: {
  162. method: CONNECTED_PAYMENT_METHOD_CODE,
  163. metadata: {},
  164. },
  165. });
  166. orderResultGuard.assertSuccess(addPaymentToOrder);
  167. expect(addPaymentToOrder.state).toBe('PaymentSettled');
  168. const { order } = await adminClient.query(getOrderWithSellerOrdersDocument, {
  169. id: addPaymentToOrder.id,
  170. });
  171. orderId = order.id;
  172. expect(order?.sellerOrders?.length).toBe(2);
  173. });
  174. it('order lines get split', async () => {
  175. const { order } = await adminClient.query(getOrderWithSellerOrdersDocument, {
  176. id: orderId,
  177. });
  178. expect(order?.sellerOrders?.[0].lines.map(l => l.productVariant.name)).toEqual([
  179. 'Laptop 13 inch 8GB',
  180. ]);
  181. expect(order?.sellerOrders?.[1].lines.map(l => l.productVariant.name)).toEqual(['Road Bike']);
  182. });
  183. it('shippingLines get split', async () => {
  184. const { order } = await adminClient.query(getOrderWithSellerOrdersDocument, {
  185. id: orderId,
  186. });
  187. expect(order?.sellerOrders?.[0]?.shippingLines.length).toBe(1);
  188. expect(order?.sellerOrders?.[1]?.shippingLines.length).toBe(1);
  189. expect(order?.sellerOrders?.[0]?.shippingLines[0].shippingMethod.code).toBe('bobs-parts-shipping');
  190. expect(order?.sellerOrders?.[1]?.shippingLines[0].shippingMethod.code).toBe('alices-wares-shipping');
  191. });
  192. });