shop-order.e2e-spec.ts 35 KB

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