shop-order.e2e-spec.ts 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. /* tslint:disable:no-non-null-assertion */
  2. import { CreateAddressInput, GetCountryList, GetCustomer, GetCustomerList, UpdateCountry } from '@vendure/common/lib/generated-types';
  3. import gql from 'graphql-tag';
  4. import path from 'path';
  5. import { GET_CUSTOMER, GET_CUSTOMER_LIST } from '../../../admin-ui/src/app/data/definitions/customer-definitions';
  6. import { GET_COUNTRY_LIST, UPDATE_COUNTRY } from '../../../admin-ui/src/app/data/definitions/settings-definitions';
  7. import { PaymentMethodHandler } from '../src/config/payment-method/payment-method-handler';
  8. import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
  9. import { TestAdminClient, TestShopClient } from './test-client';
  10. import { TestServer } from './test-server';
  11. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  12. describe('Shop orders', () => {
  13. const adminClient = new TestAdminClient();
  14. const shopClient = new TestShopClient();
  15. const server = new TestServer();
  16. beforeAll(async () => {
  17. const token = await server.init(
  18. {
  19. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  20. customerCount: 2,
  21. },
  22. {
  23. paymentOptions: {
  24. paymentMethodHandlers: [testPaymentMethod, testFailingPaymentMethod],
  25. },
  26. orderOptions: {
  27. orderItemsLimit: 99,
  28. },
  29. },
  30. );
  31. await shopClient.init();
  32. await adminClient.init();
  33. }, TEST_SETUP_TIMEOUT_MS);
  34. afterAll(async () => {
  35. await server.destroy();
  36. });
  37. it('availableCountries returns enabled countries', async () => {
  38. // disable Austria
  39. const { countries } = await adminClient.query<GetCountryList.Query>(GET_COUNTRY_LIST, {});
  40. const AT = countries.items.find(c => c.code === 'AT')!;
  41. await adminClient.query<UpdateCountry.Mutation, UpdateCountry.Variables>(UPDATE_COUNTRY, {
  42. input: {
  43. id: AT.id,
  44. enabled: false,
  45. },
  46. });
  47. const result = await shopClient.query(GET_AVAILABLE_COUNTRIES);
  48. expect(result.availableCountries.length).toBe(countries.items.length - 1);
  49. expect(result.availableCountries.find((c: GetCountryList.Items) => c.id === AT.id)).toBeUndefined();
  50. });
  51. describe('ordering as anonymous user', () => {
  52. let firstOrderItemId: string;
  53. let createdCustomerId: string;
  54. let orderCode: string;
  55. it('addItemToOrder starts with no session token', () => {
  56. expect(shopClient.getAuthToken()).toBeFalsy();
  57. });
  58. it('activeOrder returns null before any items have been added', async () => {
  59. const result = await shopClient.query(GET_ACTIVE_ORDER);
  60. expect(result.activeOrder).toBeNull();
  61. });
  62. it('activeOrder creates an anonymous session', () => {
  63. expect(shopClient.getAuthToken()).not.toBe('');
  64. });
  65. it('addItemToOrder creates a new Order with an item', async () => {
  66. const result = await shopClient.query(ADD_ITEM_TO_ORDER, {
  67. productVariantId: 'T_1',
  68. quantity: 1,
  69. });
  70. expect(result.addItemToOrder.lines.length).toBe(1);
  71. expect(result.addItemToOrder.lines[0].quantity).toBe(1);
  72. expect(result.addItemToOrder.lines[0].productVariant.id).toBe('T_1');
  73. expect(result.addItemToOrder.lines[0].id).toBe('T_1');
  74. firstOrderItemId = result.addItemToOrder.lines[0].id;
  75. orderCode = result.addItemToOrder.code;
  76. });
  77. it(
  78. 'addItemToOrder errors with an invalid productVariantId',
  79. assertThrowsWithMessage(
  80. () =>
  81. shopClient.query(ADD_ITEM_TO_ORDER, {
  82. productVariantId: 'T_999',
  83. quantity: 1,
  84. }),
  85. `No ProductVariant with the id '999' could be found`,
  86. ),
  87. );
  88. it(
  89. 'addItemToOrder errors with a negative quantity',
  90. assertThrowsWithMessage(
  91. () =>
  92. shopClient.query(ADD_ITEM_TO_ORDER, {
  93. productVariantId: 'T_999',
  94. quantity: -3,
  95. }),
  96. `-3 is not a valid quantity for an OrderItem`,
  97. ),
  98. );
  99. it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
  100. const result = await shopClient.query(ADD_ITEM_TO_ORDER, {
  101. productVariantId: 'T_1',
  102. quantity: 2,
  103. });
  104. expect(result.addItemToOrder.lines.length).toBe(1);
  105. expect(result.addItemToOrder.lines[0].quantity).toBe(3);
  106. });
  107. it(
  108. 'addItemToOrder errors when going beyond orderItemsLimit',
  109. assertThrowsWithMessage(async () => {
  110. await shopClient.query(ADD_ITEM_TO_ORDER, {
  111. productVariantId: 'T_1',
  112. quantity: 100,
  113. });
  114. }, 'Cannot add items. An order may consist of a maximum of 99 items'),
  115. );
  116. it('adjustItemQuantity adjusts the quantity', async () => {
  117. const result = await shopClient.query(ADJUST_ITEM_QUENTITY, {
  118. orderItemId: firstOrderItemId,
  119. quantity: 50,
  120. });
  121. expect(result.adjustItemQuantity.lines.length).toBe(1);
  122. expect(result.adjustItemQuantity.lines[0].quantity).toBe(50);
  123. });
  124. it(
  125. 'adjustItemQuantity errors when going beyond orderItemsLimit',
  126. assertThrowsWithMessage(async () => {
  127. await shopClient.query(ADJUST_ITEM_QUENTITY, {
  128. orderItemId: firstOrderItemId,
  129. quantity: 100,
  130. });
  131. }, 'Cannot add items. An order may consist of a maximum of 99 items'),
  132. );
  133. it(
  134. 'adjustItemQuantity errors with a negative quantity',
  135. assertThrowsWithMessage(
  136. () =>
  137. shopClient.query(ADJUST_ITEM_QUENTITY, {
  138. orderItemId: firstOrderItemId,
  139. quantity: -3,
  140. }),
  141. `-3 is not a valid quantity for an OrderItem`,
  142. ),
  143. );
  144. it(
  145. 'adjustItemQuantity errors with an invalid orderItemId',
  146. assertThrowsWithMessage(
  147. () =>
  148. shopClient.query(ADJUST_ITEM_QUENTITY, {
  149. orderItemId: 'T_999',
  150. quantity: 5,
  151. }),
  152. `This order does not contain an OrderLine with the id 999`,
  153. ),
  154. );
  155. it('removeItemFromOrder removes the correct item', async () => {
  156. const result1 = await shopClient.query(ADD_ITEM_TO_ORDER, {
  157. productVariantId: 'T_3',
  158. quantity: 3,
  159. });
  160. expect(result1.addItemToOrder.lines.length).toBe(2);
  161. expect(result1.addItemToOrder.lines.map((i: any) => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  162. const result2 = await shopClient.query(REMOVE_ITEM_FROM_ORDER, {
  163. orderItemId: firstOrderItemId,
  164. });
  165. expect(result2.removeItemFromOrder.lines.length).toBe(1);
  166. expect(result2.removeItemFromOrder.lines.map((i: any) => i.productVariant.id)).toEqual(['T_3']);
  167. });
  168. it(
  169. 'removeItemFromOrder errors with an invalid orderItemId',
  170. assertThrowsWithMessage(
  171. () =>
  172. shopClient.query(REMOVE_ITEM_FROM_ORDER, {
  173. orderItemId: 'T_999',
  174. }),
  175. `This order does not contain an OrderLine with the id 999`,
  176. ),
  177. );
  178. it('nextOrderStates returns next valid states', async () => {
  179. const result = await shopClient.query(gql`
  180. query {
  181. nextOrderStates
  182. }
  183. `);
  184. expect(result.nextOrderStates).toEqual(['ArrangingPayment']);
  185. });
  186. it(
  187. 'transitionOrderToState throws for an invalid state',
  188. assertThrowsWithMessage(
  189. () => shopClient.query(TRANSITION_TO_STATE, { state: 'Completed' }),
  190. `Cannot transition Order from "AddingItems" to "Completed"`,
  191. ),
  192. );
  193. it(
  194. 'attempting to transition to ArrangingPayment throws when Order has no Customer',
  195. assertThrowsWithMessage(
  196. () => shopClient.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' }),
  197. `Cannot transition Order to the "ArrangingPayment" state without Customer details`,
  198. ),
  199. );
  200. it('setCustomerForOrder creates a new Customer and associates it with the Order', async () => {
  201. const result = await shopClient.query(SET_CUSTOMER, {
  202. input: {
  203. emailAddress: 'test@test.com',
  204. firstName: 'Test',
  205. lastName: 'Person',
  206. },
  207. });
  208. const customer = result.setCustomerForOrder.customer;
  209. expect(customer.firstName).toBe('Test');
  210. expect(customer.lastName).toBe('Person');
  211. expect(customer.emailAddress).toBe('test@test.com');
  212. createdCustomerId = customer.id;
  213. });
  214. it('setCustomerForOrder updates the existing customer if Customer already set', async () => {
  215. const result = await shopClient.query(SET_CUSTOMER, {
  216. input: {
  217. emailAddress: 'test@test.com',
  218. firstName: 'Changed',
  219. lastName: 'Person',
  220. },
  221. });
  222. const customer = result.setCustomerForOrder.customer;
  223. expect(customer.firstName).toBe('Changed');
  224. expect(customer.lastName).toBe('Person');
  225. expect(customer.emailAddress).toBe('test@test.com');
  226. expect(customer.id).toBe(createdCustomerId);
  227. });
  228. it('setOrderShippingAddress sets shipping address', async () => {
  229. const address: CreateAddressInput = {
  230. fullName: 'name',
  231. company: 'company',
  232. streetLine1: '12 the street',
  233. streetLine2: null,
  234. city: 'foo',
  235. province: 'bar',
  236. postalCode: '123456',
  237. countryCode: 'US',
  238. phoneNumber: '4444444',
  239. };
  240. const result = await shopClient.query(SET_SHIPPING_ADDRESS, {
  241. input: address,
  242. });
  243. expect(result.setOrderShippingAddress.shippingAddress).toEqual({
  244. fullName: 'name',
  245. company: 'company',
  246. streetLine1: '12 the street',
  247. streetLine2: null,
  248. city: 'foo',
  249. province: 'bar',
  250. postalCode: '123456',
  251. country: 'United States of America',
  252. phoneNumber: '4444444',
  253. });
  254. });
  255. it('customer default Addresses are not updated before payment', async () => {
  256. const result = await shopClient.query(gql`
  257. query {
  258. activeOrder {
  259. customer {
  260. addresses {
  261. id
  262. }
  263. }
  264. }
  265. }
  266. `);
  267. expect(result.activeOrder.customer.addresses).toEqual([]);
  268. });
  269. it('can transition to ArrangingPayment once Customer has been set', async () => {
  270. const result = await shopClient.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
  271. expect(result.transitionOrderToState).toEqual({ id: 'T_1', state: 'ArrangingPayment' });
  272. });
  273. it('adds a successful payment and transitions Order state', async () => {
  274. const result = await shopClient.query(ADD_PAYMENT, {
  275. input: {
  276. method: testPaymentMethod.code,
  277. metadata: {},
  278. },
  279. });
  280. const payment = result.addPaymentToOrder.payments[0];
  281. expect(result.addPaymentToOrder.state).toBe('PaymentSettled');
  282. expect(result.addPaymentToOrder.active).toBe(false);
  283. expect(result.addPaymentToOrder.payments.length).toBe(1);
  284. expect(payment.method).toBe(testPaymentMethod.code);
  285. expect(payment.state).toBe('Settled');
  286. });
  287. it('activeOrder is null after payment', async () => {
  288. const result = await shopClient.query(GET_ACTIVE_ORDER);
  289. expect(result.activeOrder).toBeNull();
  290. });
  291. it('customer default Addresses are updated after payment', async () => {
  292. const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
  293. id: createdCustomerId,
  294. });
  295. // tslint:disable-next-line:no-non-null-assertion
  296. const address = result.customer!.addresses![0];
  297. expect(address.streetLine1).toBe('12 the street');
  298. expect(address.postalCode).toBe('123456');
  299. expect(address.defaultBillingAddress).toBe(true);
  300. expect(address.defaultShippingAddress).toBe(true);
  301. });
  302. });
  303. describe('ordering as authenticated user', () => {
  304. let firstOrderItemId: string;
  305. let activeOrder: any;
  306. let authenticatedUserEmailAddress: string;
  307. let customers: GetCustomerList.Items[];
  308. const password = 'test';
  309. beforeAll(async () => {
  310. await adminClient.asSuperAdmin();
  311. const result = await adminClient.query<GetCustomerList.Query, GetCustomerList.Variables>(
  312. GET_CUSTOMER_LIST,
  313. {
  314. options: {
  315. take: 2,
  316. },
  317. },
  318. );
  319. customers = result.customers.items;
  320. authenticatedUserEmailAddress = customers[0].emailAddress;
  321. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  322. });
  323. it('activeOrder returns null before any items have been added', async () => {
  324. const result = await shopClient.query(GET_ACTIVE_ORDER);
  325. expect(result.activeOrder).toBeNull();
  326. });
  327. it('addItemToOrder creates a new Order with an item', async () => {
  328. const result = await shopClient.query(ADD_ITEM_TO_ORDER, {
  329. productVariantId: 'T_1',
  330. quantity: 1,
  331. });
  332. expect(result.addItemToOrder.lines.length).toBe(1);
  333. expect(result.addItemToOrder.lines[0].quantity).toBe(1);
  334. expect(result.addItemToOrder.lines[0].productVariant.id).toBe('T_1');
  335. activeOrder = result.addItemToOrder;
  336. firstOrderItemId = result.addItemToOrder.lines[0].id;
  337. });
  338. it('activeOrder returns order after item has been added', async () => {
  339. const result = await shopClient.query(GET_ACTIVE_ORDER);
  340. expect(result.activeOrder.id).toBe(activeOrder.id);
  341. expect(result.activeOrder.state).toBe('AddingItems');
  342. });
  343. it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
  344. const result = await shopClient.query(ADD_ITEM_TO_ORDER, {
  345. productVariantId: 'T_1',
  346. quantity: 2,
  347. });
  348. expect(result.addItemToOrder.lines.length).toBe(1);
  349. expect(result.addItemToOrder.lines[0].quantity).toBe(3);
  350. });
  351. it('adjustItemQuantity adjusts the quantity', async () => {
  352. const result = await shopClient.query(ADJUST_ITEM_QUENTITY, {
  353. orderItemId: firstOrderItemId,
  354. quantity: 50,
  355. });
  356. expect(result.adjustItemQuantity.lines.length).toBe(1);
  357. expect(result.adjustItemQuantity.lines[0].quantity).toBe(50);
  358. });
  359. it('removeItemFromOrder removes the correct item', async () => {
  360. const result1 = await shopClient.query(ADD_ITEM_TO_ORDER, {
  361. productVariantId: 'T_3',
  362. quantity: 3,
  363. });
  364. expect(result1.addItemToOrder.lines.length).toBe(2);
  365. expect(result1.addItemToOrder.lines.map((i: any) => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  366. const result2 = await shopClient.query(REMOVE_ITEM_FROM_ORDER, {
  367. orderItemId: firstOrderItemId,
  368. });
  369. expect(result2.removeItemFromOrder.lines.length).toBe(1);
  370. expect(result2.removeItemFromOrder.lines.map((i: any) => i.productVariant.id)).toEqual(['T_3']);
  371. });
  372. it('nextOrderStates returns next valid states', async () => {
  373. const result = await shopClient.query(GET_NEXT_STATES);
  374. expect(result.nextOrderStates).toEqual(['ArrangingPayment']);
  375. });
  376. it('logging out and back in again resumes the last active order', async () => {
  377. await shopClient.asAnonymousUser();
  378. const result1 = await shopClient.query(GET_ACTIVE_ORDER);
  379. expect(result1.activeOrder).toBeNull();
  380. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  381. const result2 = await shopClient.query(GET_ACTIVE_ORDER);
  382. expect(result2.activeOrder.id).toBe(activeOrder.id);
  383. });
  384. describe('shipping', () => {
  385. let shippingMethods: any;
  386. it(
  387. 'setOrderShippingAddress throws with invalid countryCode',
  388. assertThrowsWithMessage(() => {
  389. const address: CreateAddressInput = {
  390. streetLine1: '12 the street',
  391. countryCode: 'INVALID',
  392. };
  393. return shopClient.query(SET_SHIPPING_ADDRESS, {
  394. input: address,
  395. });
  396. }, `The countryCode "INVALID" was not recognized`),
  397. );
  398. it('setOrderShippingAddress sets shipping address', async () => {
  399. const address: CreateAddressInput = {
  400. fullName: 'name',
  401. company: 'company',
  402. streetLine1: '12 the street',
  403. streetLine2: null,
  404. city: 'foo',
  405. province: 'bar',
  406. postalCode: '123456',
  407. countryCode: 'US',
  408. phoneNumber: '4444444',
  409. };
  410. const result = await shopClient.query(SET_SHIPPING_ADDRESS, {
  411. input: address,
  412. });
  413. expect(result.setOrderShippingAddress.shippingAddress).toEqual({
  414. fullName: 'name',
  415. company: 'company',
  416. streetLine1: '12 the street',
  417. streetLine2: null,
  418. city: 'foo',
  419. province: 'bar',
  420. postalCode: '123456',
  421. country: 'United States of America',
  422. phoneNumber: '4444444',
  423. });
  424. });
  425. it('eligibleShippingMethods lists shipping methods', async () => {
  426. const result = await shopClient.query(GET_ELIGIBLE_SHIPPING_METHODS);
  427. shippingMethods = result.eligibleShippingMethods;
  428. expect(shippingMethods).toEqual([
  429. { id: 'T_1', price: 500, description: 'Standard Shipping' },
  430. { id: 'T_2', price: 1000, description: 'Express Shipping' },
  431. ]);
  432. });
  433. it('shipping is initially unset', async () => {
  434. const result = await shopClient.query(GET_ACTIVE_ORDER);
  435. expect(result.activeOrder.shipping).toEqual(0);
  436. expect(result.activeOrder.shippingMethod).toEqual(null);
  437. });
  438. it('setOrderShippingMethod sets the shipping method', async () => {
  439. const result = await shopClient.query(SET_SHIPPING_METHOD, {
  440. id: shippingMethods[1].id,
  441. });
  442. const activeOrderResult = await shopClient.query(GET_ACTIVE_ORDER);
  443. const order = activeOrderResult.activeOrder;
  444. expect(order.shipping).toBe(shippingMethods[1].price);
  445. expect(order.shippingMethod.id).toBe(shippingMethods[1].id);
  446. expect(order.shippingMethod.description).toBe(shippingMethods[1].description);
  447. });
  448. it('shipping method is preserved after adjustItemQuantity', async () => {
  449. const activeOrderResult = await shopClient.query(GET_ACTIVE_ORDER);
  450. activeOrder = activeOrderResult.activeOrder;
  451. const result = await shopClient.query(ADJUST_ITEM_QUENTITY, {
  452. orderItemId: activeOrder.lines[0].id,
  453. quantity: 10,
  454. });
  455. expect(result.adjustItemQuantity.shipping).toBe(shippingMethods[1].price);
  456. expect(result.adjustItemQuantity.shippingMethod.id).toBe(shippingMethods[1].id);
  457. expect(result.adjustItemQuantity.shippingMethod.description).toBe(
  458. shippingMethods[1].description,
  459. );
  460. });
  461. });
  462. describe('payment', () => {
  463. it(
  464. 'attempting add a Payment throws error when in AddingItems state',
  465. assertThrowsWithMessage(
  466. () =>
  467. shopClient.query(ADD_PAYMENT, {
  468. input: {
  469. method: testPaymentMethod.code,
  470. metadata: {},
  471. },
  472. }),
  473. `A Payment may only be added when Order is in "ArrangingPayment" state`,
  474. ),
  475. );
  476. it('transitions to the ArrangingPayment state', async () => {
  477. const result = await shopClient.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
  478. expect(result.transitionOrderToState).toEqual({
  479. id: activeOrder.id,
  480. state: 'ArrangingPayment',
  481. });
  482. });
  483. it(
  484. 'attempting to add an item throws error when in ArrangingPayment state',
  485. assertThrowsWithMessage(
  486. () =>
  487. shopClient.query(ADD_ITEM_TO_ORDER, {
  488. productVariantId: 'T_4',
  489. quantity: 1,
  490. }),
  491. `Order contents may only be modified when in the "AddingItems" state`,
  492. ),
  493. );
  494. it(
  495. 'attempting to modify item quantity throws error when in ArrangingPayment state',
  496. assertThrowsWithMessage(
  497. () =>
  498. shopClient.query(ADJUST_ITEM_QUENTITY, {
  499. orderItemId: activeOrder.lines[0].id,
  500. quantity: 12,
  501. }),
  502. `Order contents may only be modified when in the "AddingItems" state`,
  503. ),
  504. );
  505. it(
  506. 'attempting to remove an item throws error when in ArrangingPayment state',
  507. assertThrowsWithMessage(
  508. () =>
  509. shopClient.query(REMOVE_ITEM_FROM_ORDER, {
  510. orderItemId: activeOrder.lines[0].id,
  511. }),
  512. `Order contents may only be modified when in the "AddingItems" state`,
  513. ),
  514. );
  515. it(
  516. 'attempting to setOrderShippingMethod throws error when in ArrangingPayment state',
  517. assertThrowsWithMessage(async () => {
  518. const shippingMethodsResult = await shopClient.query(GET_ELIGIBLE_SHIPPING_METHODS);
  519. const shippingMethods = shippingMethodsResult.eligibleShippingMethods;
  520. return shopClient.query(SET_SHIPPING_METHOD, {
  521. id: shippingMethods[0].id,
  522. });
  523. }, `Order contents may only be modified when in the "AddingItems" state`),
  524. );
  525. it('adds a declined payment', async () => {
  526. const result = await shopClient.query(ADD_PAYMENT, {
  527. input: {
  528. method: testFailingPaymentMethod.code,
  529. metadata: {
  530. foo: 'bar',
  531. },
  532. },
  533. });
  534. const payment = result.addPaymentToOrder.payments[0];
  535. expect(result.addPaymentToOrder.payments.length).toBe(1);
  536. expect(payment.method).toBe(testFailingPaymentMethod.code);
  537. expect(payment.state).toBe('Declined');
  538. expect(payment.transactionId).toBe(null);
  539. expect(payment.metadata).toEqual({
  540. foo: 'bar',
  541. });
  542. });
  543. it('adds a successful payment and transitions Order state', async () => {
  544. const result = await shopClient.query(ADD_PAYMENT, {
  545. input: {
  546. method: testPaymentMethod.code,
  547. metadata: {
  548. baz: 'quux',
  549. },
  550. },
  551. });
  552. const payment = result.addPaymentToOrder.payments[0];
  553. expect(result.addPaymentToOrder.state).toBe('PaymentSettled');
  554. expect(result.addPaymentToOrder.active).toBe(false);
  555. expect(result.addPaymentToOrder.payments.length).toBe(1);
  556. expect(payment.method).toBe(testPaymentMethod.code);
  557. expect(payment.state).toBe('Settled');
  558. expect(payment.transactionId).toBe('12345');
  559. expect(payment.metadata).toEqual({
  560. baz: 'quux',
  561. });
  562. });
  563. });
  564. describe('orderByCode', () => {
  565. describe('immediately after Order is placed', () => {
  566. it('works when authenticated', async () => {
  567. const result = await shopClient.query(GET_ORDER_BY_CODE, {
  568. code: activeOrder.code,
  569. });
  570. expect(result.orderByCode.id).toBe(activeOrder.id);
  571. });
  572. it('works when anonymous', async () => {
  573. await shopClient.asAnonymousUser();
  574. const result = await shopClient.query(GET_ORDER_BY_CODE, {
  575. code: activeOrder.code,
  576. });
  577. expect(result.orderByCode.id).toBe(activeOrder.id);
  578. });
  579. it(
  580. `throws error for another user's Order`,
  581. assertThrowsWithMessage(async () => {
  582. authenticatedUserEmailAddress = customers[1].emailAddress;
  583. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  584. return shopClient.query(GET_ORDER_BY_CODE, {
  585. code: activeOrder.code,
  586. });
  587. }, `You are not currently authorized to perform this action`),
  588. );
  589. });
  590. });
  591. });
  592. });
  593. const testPaymentMethod = new PaymentMethodHandler({
  594. code: 'test-payment-method',
  595. description: 'Test Payment Method',
  596. args: {},
  597. createPayment: (order, args, metadata) => {
  598. return {
  599. amount: order.total,
  600. state: 'Settled',
  601. transactionId: '12345',
  602. metadata,
  603. };
  604. },
  605. });
  606. const testFailingPaymentMethod = new PaymentMethodHandler({
  607. code: 'test-failing-payment-method',
  608. description: 'Test Failing Payment Method',
  609. args: {},
  610. createPayment: (order, args, metadata) => {
  611. return {
  612. amount: order.total,
  613. state: 'Declined',
  614. metadata,
  615. };
  616. },
  617. });
  618. const TEST_ORDER_FRAGMENT = gql`
  619. fragment TestOrderFragment on Order {
  620. id
  621. code
  622. state
  623. active
  624. lines {
  625. id
  626. quantity
  627. productVariant {
  628. id
  629. }
  630. }
  631. shipping
  632. shippingMethod {
  633. id
  634. code
  635. description
  636. }
  637. customer {
  638. id
  639. }
  640. }
  641. `;
  642. const GET_ACTIVE_ORDER = gql`
  643. query {
  644. activeOrder {
  645. ...TestOrderFragment
  646. }
  647. }
  648. ${TEST_ORDER_FRAGMENT}
  649. `;
  650. const ADD_ITEM_TO_ORDER = gql`
  651. mutation AddItemToOrder($productVariantId: ID!, $quantity: Int!) {
  652. addItemToOrder(productVariantId: $productVariantId, quantity: $quantity) {
  653. ...TestOrderFragment
  654. }
  655. }
  656. ${TEST_ORDER_FRAGMENT}
  657. `;
  658. const ADJUST_ITEM_QUENTITY = gql`
  659. mutation AdjustItemQuantity($orderItemId: ID!, $quantity: Int!) {
  660. adjustItemQuantity(orderItemId: $orderItemId, quantity: $quantity) {
  661. ...TestOrderFragment
  662. }
  663. }
  664. ${TEST_ORDER_FRAGMENT}
  665. `;
  666. const REMOVE_ITEM_FROM_ORDER = gql`
  667. mutation RemoveItemFromOrder($orderItemId: ID!) {
  668. removeItemFromOrder(orderItemId: $orderItemId) {
  669. ...TestOrderFragment
  670. }
  671. }
  672. ${TEST_ORDER_FRAGMENT}
  673. `;
  674. const GET_NEXT_STATES = gql`
  675. query {
  676. nextOrderStates
  677. }
  678. `;
  679. const TRANSITION_TO_STATE = gql`
  680. mutation TransitionToState($state: String!) {
  681. transitionOrderToState(state: $state) {
  682. id
  683. state
  684. }
  685. }
  686. `;
  687. const GET_ELIGIBLE_SHIPPING_METHODS = gql`
  688. query {
  689. eligibleShippingMethods {
  690. id
  691. price
  692. description
  693. }
  694. }
  695. `;
  696. const SET_SHIPPING_ADDRESS = gql`
  697. mutation SetShippingAddress($input: CreateAddressInput!) {
  698. setOrderShippingAddress(input: $input) {
  699. shippingAddress {
  700. fullName
  701. company
  702. streetLine1
  703. streetLine2
  704. city
  705. province
  706. postalCode
  707. country
  708. phoneNumber
  709. }
  710. }
  711. }
  712. `;
  713. const SET_SHIPPING_METHOD = gql`
  714. mutation SetShippingMethod($id: ID!) {
  715. setOrderShippingMethod(shippingMethodId: $id) {
  716. shipping
  717. shippingMethod {
  718. id
  719. code
  720. description
  721. }
  722. }
  723. }
  724. `;
  725. const ADD_PAYMENT = gql`
  726. mutation AddPaymentToOrder($input: PaymentInput!) {
  727. addPaymentToOrder(input: $input) {
  728. ...TestOrderFragment
  729. payments {
  730. id
  731. transactionId
  732. method
  733. amount
  734. state
  735. metadata
  736. }
  737. }
  738. }
  739. ${TEST_ORDER_FRAGMENT}
  740. `;
  741. const SET_CUSTOMER = gql`
  742. mutation SetCustomerForOrder($input: CreateCustomerInput!) {
  743. setCustomerForOrder(input: $input) {
  744. id
  745. customer {
  746. id
  747. emailAddress
  748. firstName
  749. lastName
  750. }
  751. }
  752. }
  753. `;
  754. const GET_ORDER_BY_CODE = gql`
  755. query GetOrderByCode($code: String!) {
  756. orderByCode(code: $code) {
  757. ...TestOrderFragment
  758. }
  759. }
  760. ${TEST_ORDER_FRAGMENT}
  761. `;
  762. const GET_AVAILABLE_COUNTRIES = gql`
  763. query {
  764. availableCountries {
  765. id
  766. code
  767. }
  768. }
  769. `;