active-order-strategy.e2e-spec.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. import { LanguageCode } from '@vendure/common/lib/generated-types';
  2. import { mergeConfig, orderPercentageDiscount } from '@vendure/core';
  3. import { createTestEnvironment } from '@vendure/testing';
  4. import gql from 'graphql-tag';
  5. import path from 'path';
  6. import { afterAll, beforeAll, describe, expect, it } from 'vitest';
  7. import { initialData } from '../../../e2e-common/e2e-initial-data';
  8. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  9. import { testSuccessfulPaymentMethod } from './fixtures/test-payment-methods';
  10. import { TokenActiveOrderPlugin } from './fixtures/test-plugins/token-active-order-plugin';
  11. import {
  12. CreatePromotionMutation,
  13. CreatePromotionMutationVariables,
  14. GetCustomerListQuery,
  15. } from './graphql/generated-e2e-admin-types';
  16. import {
  17. AddItemToOrderMutation,
  18. AddItemToOrderMutationVariables,
  19. GetActiveOrderQuery,
  20. } from './graphql/generated-e2e-shop-types';
  21. import { CREATE_PROMOTION, GET_CUSTOMER_LIST } from './graphql/shared-definitions';
  22. import { ADD_ITEM_TO_ORDER, GET_ACTIVE_ORDER } from './graphql/shop-definitions';
  23. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  24. describe('custom ActiveOrderStrategy', () => {
  25. const { server, adminClient, shopClient } = createTestEnvironment(
  26. mergeConfig(testConfig(), {
  27. plugins: [TokenActiveOrderPlugin],
  28. paymentOptions: {
  29. paymentMethodHandlers: [testSuccessfulPaymentMethod],
  30. },
  31. customFields: {
  32. Order: [
  33. {
  34. name: 'message',
  35. type: 'string',
  36. nullable: true,
  37. },
  38. ],
  39. },
  40. }),
  41. );
  42. let customers: GetCustomerListQuery['customers']['items'];
  43. beforeAll(async () => {
  44. await server.init({
  45. initialData: {
  46. ...initialData,
  47. paymentMethods: [
  48. {
  49. name: testSuccessfulPaymentMethod.code,
  50. handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
  51. },
  52. ],
  53. },
  54. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  55. customerCount: 3,
  56. });
  57. await adminClient.asSuperAdmin();
  58. const result = await adminClient.query<GetCustomerListQuery>(GET_CUSTOMER_LIST);
  59. customers = result.customers.items;
  60. }, TEST_SETUP_TIMEOUT_MS);
  61. afterAll(async () => {
  62. await server.destroy();
  63. });
  64. it('activeOrder with no createActiveOrder defined returns null', async () => {
  65. const { activeOrder } = await shopClient.query<GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  66. expect(activeOrder).toBeNull();
  67. });
  68. it(
  69. 'addItemToOrder with no createActiveOrder throws',
  70. assertThrowsWithMessage(async () => {
  71. await shopClient.query<AddItemToOrderMutation, AddItemToOrderMutationVariables>(
  72. ADD_ITEM_TO_ORDER,
  73. {
  74. productVariantId: 'T_1',
  75. quantity: 1,
  76. },
  77. );
  78. }, 'No active Order could be determined nor created'),
  79. );
  80. it('activeOrder with valid input', async () => {
  81. const { createOrder } = await shopClient.query(gql`
  82. mutation CreateCustomOrder {
  83. createOrder(customerId: "${customers[1].id}") {
  84. id
  85. orderToken
  86. }
  87. }
  88. `);
  89. expect(createOrder).toEqual({
  90. id: 'T_1',
  91. orderToken: 'token-2',
  92. });
  93. await shopClient.asUserWithCredentials(customers[1].emailAddress, 'test');
  94. const { activeOrder } = await shopClient.query(ACTIVE_ORDER_BY_TOKEN, {
  95. input: {
  96. orderToken: { token: 'token-2' },
  97. },
  98. });
  99. expect(activeOrder).toEqual({
  100. id: 'T_1',
  101. orderToken: 'token-2',
  102. });
  103. });
  104. it('activeOrder with invalid input', async () => {
  105. await shopClient.asUserWithCredentials(customers[1].emailAddress, 'test');
  106. const { activeOrder } = await shopClient.query(ACTIVE_ORDER_BY_TOKEN, {
  107. input: {
  108. orderToken: { token: 'invalid' },
  109. },
  110. });
  111. expect(activeOrder).toBeNull();
  112. });
  113. it('activeOrder with invalid condition', async () => {
  114. // wrong customer logged in
  115. await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
  116. const { activeOrder } = await shopClient.query(ACTIVE_ORDER_BY_TOKEN, {
  117. input: {
  118. orderToken: { token: 'token-2' },
  119. },
  120. });
  121. expect(activeOrder).toBeNull();
  122. });
  123. describe('happy path', () => {
  124. const activeOrderInput = 'activeOrderInput: { orderToken: { token: "token-2" } }';
  125. const TEST_COUPON_CODE = 'TESTCOUPON';
  126. let firstOrderLineId: string;
  127. beforeAll(async () => {
  128. await shopClient.asUserWithCredentials(customers[1].emailAddress, 'test');
  129. const result = await adminClient.query<CreatePromotionMutation, CreatePromotionMutationVariables>(
  130. CREATE_PROMOTION,
  131. {
  132. input: {
  133. enabled: true,
  134. couponCode: TEST_COUPON_CODE,
  135. conditions: [],
  136. actions: [
  137. {
  138. code: orderPercentageDiscount.code,
  139. arguments: [{ name: 'discount', value: '100' }],
  140. },
  141. ],
  142. translations: [{ languageCode: LanguageCode.en, name: 'Free with test coupon' }],
  143. },
  144. },
  145. );
  146. });
  147. it('addItemToOrder', async () => {
  148. const { addItemToOrder } = await shopClient.query(gql`
  149. mutation {
  150. addItemToOrder(productVariantId: "T_1", quantity: 1, ${activeOrderInput}) {
  151. ...on Order {
  152. id
  153. orderToken
  154. lines {
  155. id
  156. productVariant { id }
  157. }
  158. }
  159. }
  160. }
  161. `);
  162. expect(addItemToOrder).toEqual({
  163. id: 'T_1',
  164. orderToken: 'token-2',
  165. lines: [
  166. {
  167. id: 'T_1',
  168. productVariant: { id: 'T_1' },
  169. },
  170. ],
  171. });
  172. firstOrderLineId = addItemToOrder.lines[0].id;
  173. });
  174. it('adjustOrderLine', async () => {
  175. const { adjustOrderLine } = await shopClient.query(gql`
  176. mutation {
  177. adjustOrderLine(orderLineId: "${firstOrderLineId}", quantity: 2, ${activeOrderInput}) {
  178. ...on Order {
  179. id
  180. orderToken
  181. lines {
  182. quantity
  183. productVariant { id }
  184. }
  185. }
  186. }
  187. }
  188. `);
  189. expect(adjustOrderLine).toEqual({
  190. id: 'T_1',
  191. orderToken: 'token-2',
  192. lines: [
  193. {
  194. quantity: 2,
  195. productVariant: { id: 'T_1' },
  196. },
  197. ],
  198. });
  199. });
  200. it('removeOrderLine', async () => {
  201. const { removeOrderLine } = await shopClient.query(gql`
  202. mutation {
  203. removeOrderLine(orderLineId: "${firstOrderLineId}", ${activeOrderInput}) {
  204. ...on Order {
  205. id
  206. orderToken
  207. lines {
  208. id
  209. }
  210. }
  211. }
  212. }
  213. `);
  214. expect(removeOrderLine).toEqual({
  215. id: 'T_1',
  216. orderToken: 'token-2',
  217. lines: [],
  218. });
  219. });
  220. it('removeAllOrderLines', async () => {
  221. const { addItemToOrder } = await shopClient.query(gql`
  222. mutation {
  223. addItemToOrder(productVariantId: "T_1", quantity: 1, ${activeOrderInput}) {
  224. ...on Order { lines { id } }
  225. }
  226. }
  227. `);
  228. expect(addItemToOrder.lines.length).toBe(1);
  229. const { removeAllOrderLines } = await shopClient.query(gql`
  230. mutation {
  231. removeAllOrderLines(${activeOrderInput}) {
  232. ...on Order {
  233. id
  234. orderToken
  235. lines { id }
  236. }
  237. }
  238. }
  239. `);
  240. expect(removeAllOrderLines.lines.length).toBe(0);
  241. });
  242. it('applyCouponCode', async () => {
  243. await shopClient.query(gql`
  244. mutation {
  245. addItemToOrder(productVariantId: "T_1", quantity: 1, ${activeOrderInput}) {
  246. ...on Order { lines { id } }
  247. }
  248. }
  249. `);
  250. const { applyCouponCode } = await shopClient.query(gql`
  251. mutation {
  252. applyCouponCode(couponCode: "${TEST_COUPON_CODE}", ${activeOrderInput}) {
  253. ...on Order {
  254. id
  255. orderToken
  256. couponCodes
  257. discounts {
  258. description
  259. }
  260. }
  261. }
  262. }
  263. `);
  264. expect(applyCouponCode).toEqual({
  265. id: 'T_1',
  266. orderToken: 'token-2',
  267. couponCodes: [TEST_COUPON_CODE],
  268. discounts: [{ description: 'Free with test coupon' }],
  269. });
  270. });
  271. it('removeCouponCode', async () => {
  272. const { removeCouponCode } = await shopClient.query(gql`
  273. mutation {
  274. removeCouponCode(couponCode: "${TEST_COUPON_CODE}", ${activeOrderInput}) {
  275. ...on Order {
  276. id
  277. orderToken
  278. couponCodes
  279. discounts {
  280. description
  281. }
  282. }
  283. }
  284. }
  285. `);
  286. expect(removeCouponCode).toEqual({
  287. id: 'T_1',
  288. orderToken: 'token-2',
  289. couponCodes: [],
  290. discounts: [],
  291. });
  292. });
  293. it('setOrderShippingAddress', async () => {
  294. const { setOrderShippingAddress } = await shopClient.query(gql`
  295. mutation {
  296. setOrderShippingAddress(input: {
  297. streetLine1: "Shipping Street"
  298. countryCode: "AT"
  299. }, ${activeOrderInput}) {
  300. ...on Order {
  301. id
  302. orderToken
  303. shippingAddress {
  304. streetLine1
  305. country
  306. }
  307. }
  308. }
  309. }
  310. `);
  311. expect(setOrderShippingAddress).toEqual({
  312. id: 'T_1',
  313. orderToken: 'token-2',
  314. shippingAddress: {
  315. streetLine1: 'Shipping Street',
  316. country: 'Austria',
  317. },
  318. });
  319. });
  320. it('setOrderBillingAddress', async () => {
  321. const { setOrderBillingAddress } = await shopClient.query(gql`
  322. mutation {
  323. setOrderBillingAddress(input: {
  324. streetLine1: "Billing Street"
  325. countryCode: "AT"
  326. }, ${activeOrderInput}) {
  327. ...on Order {
  328. id
  329. orderToken
  330. billingAddress {
  331. streetLine1
  332. country
  333. }
  334. }
  335. }
  336. }
  337. `);
  338. expect(setOrderBillingAddress).toEqual({
  339. id: 'T_1',
  340. orderToken: 'token-2',
  341. billingAddress: {
  342. streetLine1: 'Billing Street',
  343. country: 'Austria',
  344. },
  345. });
  346. });
  347. it('eligibleShippingMethods', async () => {
  348. const { eligibleShippingMethods } = await shopClient.query(gql`
  349. query {
  350. eligibleShippingMethods(${activeOrderInput}) {
  351. id
  352. name
  353. priceWithTax
  354. }
  355. }
  356. `);
  357. expect(eligibleShippingMethods).toEqual([
  358. {
  359. id: 'T_1',
  360. name: 'Standard Shipping',
  361. priceWithTax: 500,
  362. },
  363. {
  364. id: 'T_2',
  365. name: 'Express Shipping',
  366. priceWithTax: 1000,
  367. },
  368. ]);
  369. });
  370. it('setOrderShippingMethod', async () => {
  371. const { setOrderShippingMethod } = await shopClient.query(gql`
  372. mutation {
  373. setOrderShippingMethod(shippingMethodId: "T_1", ${activeOrderInput}) {
  374. ...on Order {
  375. id
  376. orderToken
  377. shippingLines {
  378. price
  379. }
  380. }
  381. }
  382. }
  383. `);
  384. expect(setOrderShippingMethod).toEqual({
  385. id: 'T_1',
  386. orderToken: 'token-2',
  387. shippingLines: [{ price: 500 }],
  388. });
  389. });
  390. it('setOrderCustomFields', async () => {
  391. const { setOrderCustomFields } = await shopClient.query(gql`
  392. mutation {
  393. setOrderCustomFields(input: { customFields: { message: "foo" } }, ${activeOrderInput}) {
  394. ...on Order {
  395. id
  396. orderToken
  397. customFields { message }
  398. }
  399. }
  400. }
  401. `);
  402. expect(setOrderCustomFields).toEqual({
  403. id: 'T_1',
  404. orderToken: 'token-2',
  405. customFields: { message: 'foo' },
  406. });
  407. });
  408. it('eligiblePaymentMethods', async () => {
  409. const { eligiblePaymentMethods } = await shopClient.query(gql`
  410. query {
  411. eligiblePaymentMethods(${activeOrderInput}) {
  412. id
  413. name
  414. code
  415. }
  416. }
  417. `);
  418. expect(eligiblePaymentMethods).toEqual([
  419. {
  420. id: 'T_1',
  421. name: 'test-payment-method',
  422. code: 'test-payment-method',
  423. },
  424. ]);
  425. });
  426. it('nextOrderStates', async () => {
  427. const { nextOrderStates } = await shopClient.query(gql`
  428. query {
  429. nextOrderStates(${activeOrderInput})
  430. }
  431. `);
  432. expect(nextOrderStates).toEqual(['ArrangingPayment', 'Cancelled']);
  433. });
  434. it('transitionOrderToState', async () => {
  435. const { transitionOrderToState } = await shopClient.query(gql`
  436. mutation {
  437. transitionOrderToState(state: "ArrangingPayment", ${activeOrderInput}) {
  438. ...on Order {
  439. id
  440. orderToken
  441. state
  442. }
  443. }
  444. }
  445. `);
  446. expect(transitionOrderToState).toEqual({
  447. id: 'T_1',
  448. orderToken: 'token-2',
  449. state: 'ArrangingPayment',
  450. });
  451. });
  452. it('addPaymentToOrder', async () => {
  453. const { addPaymentToOrder } = await shopClient.query(gql`
  454. mutation {
  455. addPaymentToOrder(input: { method: "test-payment-method", metadata: {}}, ${activeOrderInput}) {
  456. ...on Order {
  457. id
  458. orderToken
  459. state
  460. payments {
  461. state
  462. }
  463. }
  464. }
  465. }
  466. `);
  467. expect(addPaymentToOrder).toEqual({
  468. id: 'T_1',
  469. orderToken: 'token-2',
  470. payments: [
  471. {
  472. state: 'Settled',
  473. },
  474. ],
  475. state: 'PaymentSettled',
  476. });
  477. });
  478. });
  479. });
  480. export const ACTIVE_ORDER_BY_TOKEN = gql`
  481. query ActiveOrderByToken($input: ActiveOrderInput) {
  482. activeOrder(activeOrderInput: $input) {
  483. id
  484. orderToken
  485. }
  486. }
  487. `;