shop-order.e2e-spec.ts 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446
  1. /* tslint:disable:no-non-null-assertion */
  2. import { pick } from '@vendure/common/lib/pick';
  3. import { mergeConfig } from '@vendure/core';
  4. import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
  5. import gql from 'graphql-tag';
  6. import path from 'path';
  7. import { initialData } from '../../../e2e-common/e2e-initial-data';
  8. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  9. import {
  10. testErrorPaymentMethod,
  11. testFailingPaymentMethod,
  12. testSuccessfulPaymentMethod,
  13. } from './fixtures/test-payment-methods';
  14. import {
  15. AttemptLogin,
  16. CreateAddressInput,
  17. GetCountryList,
  18. GetCustomer,
  19. GetCustomerList,
  20. UpdateCountry,
  21. } from './graphql/generated-e2e-admin-types';
  22. import {
  23. ActiveOrderCustomerFragment,
  24. AddItemToOrder,
  25. AddPaymentToOrder,
  26. AdjustItemQuantity,
  27. ErrorCode,
  28. GetActiveOrder,
  29. GetActiveOrderPayments,
  30. GetActiveOrderWithPayments,
  31. GetAvailableCountries,
  32. GetCustomerAddresses,
  33. GetCustomerOrders,
  34. GetNextOrderStates,
  35. GetOrderByCode,
  36. GetShippingMethods,
  37. RemoveAllOrderLines,
  38. RemoveItemFromOrder,
  39. SetBillingAddress,
  40. SetCustomerForOrder,
  41. SetShippingAddress,
  42. SetShippingMethod,
  43. TestOrderFragmentFragment,
  44. TestOrderWithPaymentsFragment,
  45. TransitionToState,
  46. UpdatedOrderFragment,
  47. } from './graphql/generated-e2e-shop-types';
  48. import {
  49. ATTEMPT_LOGIN,
  50. GET_COUNTRY_LIST,
  51. GET_CUSTOMER,
  52. GET_CUSTOMER_LIST,
  53. UPDATE_COUNTRY,
  54. } from './graphql/shared-definitions';
  55. import {
  56. ADD_ITEM_TO_ORDER,
  57. ADD_PAYMENT,
  58. ADJUST_ITEM_QUANTITY,
  59. GET_ACTIVE_ORDER,
  60. GET_ACTIVE_ORDER_ADDRESSES,
  61. GET_ACTIVE_ORDER_ORDERS,
  62. GET_ACTIVE_ORDER_PAYMENTS,
  63. GET_ACTIVE_ORDER_WITH_PAYMENTS,
  64. GET_AVAILABLE_COUNTRIES,
  65. GET_ELIGIBLE_SHIPPING_METHODS,
  66. GET_NEXT_STATES,
  67. GET_ORDER_BY_CODE,
  68. REMOVE_ALL_ORDER_LINES,
  69. REMOVE_ITEM_FROM_ORDER,
  70. SET_BILLING_ADDRESS,
  71. SET_CUSTOMER,
  72. SET_SHIPPING_ADDRESS,
  73. SET_SHIPPING_METHOD,
  74. TRANSITION_TO_STATE,
  75. UPDATED_ORDER_FRAGMENT,
  76. } from './graphql/shop-definitions';
  77. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  78. describe('Shop orders', () => {
  79. const { server, adminClient, shopClient } = createTestEnvironment(
  80. mergeConfig(testConfig, {
  81. paymentOptions: {
  82. paymentMethodHandlers: [
  83. testSuccessfulPaymentMethod,
  84. testFailingPaymentMethod,
  85. testErrorPaymentMethod,
  86. ],
  87. },
  88. customFields: {
  89. Order: [{ name: 'giftWrap', type: 'boolean', defaultValue: false }],
  90. OrderLine: [{ name: 'notes', type: 'string' }],
  91. },
  92. orderOptions: {
  93. orderItemsLimit: 99,
  94. },
  95. }),
  96. );
  97. type OrderSuccessResult =
  98. | UpdatedOrderFragment
  99. | TestOrderFragmentFragment
  100. | TestOrderWithPaymentsFragment
  101. | ActiveOrderCustomerFragment;
  102. const orderResultGuard: ErrorResultGuard<OrderSuccessResult> = createErrorResultGuard(
  103. input => !!input.lines,
  104. );
  105. beforeAll(async () => {
  106. await server.init({
  107. initialData: {
  108. ...initialData,
  109. paymentMethods: [
  110. {
  111. name: testSuccessfulPaymentMethod.code,
  112. handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
  113. },
  114. {
  115. name: testFailingPaymentMethod.code,
  116. handler: { code: testFailingPaymentMethod.code, arguments: [] },
  117. },
  118. {
  119. name: testErrorPaymentMethod.code,
  120. handler: { code: testErrorPaymentMethod.code, arguments: [] },
  121. },
  122. ],
  123. },
  124. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  125. customerCount: 3,
  126. });
  127. await adminClient.asSuperAdmin();
  128. }, TEST_SETUP_TIMEOUT_MS);
  129. afterAll(async () => {
  130. await server.destroy();
  131. });
  132. it('availableCountries returns enabled countries', async () => {
  133. // disable Austria
  134. const { countries } = await adminClient.query<GetCountryList.Query>(GET_COUNTRY_LIST, {});
  135. const AT = countries.items.find(c => c.code === 'AT')!;
  136. await adminClient.query<UpdateCountry.Mutation, UpdateCountry.Variables>(UPDATE_COUNTRY, {
  137. input: {
  138. id: AT.id,
  139. enabled: false,
  140. },
  141. });
  142. const result = await shopClient.query<GetAvailableCountries.Query>(GET_AVAILABLE_COUNTRIES);
  143. expect(result.availableCountries.length).toBe(countries.items.length - 1);
  144. expect(result.availableCountries.find(c => c.id === AT.id)).toBeUndefined();
  145. });
  146. describe('ordering as anonymous user', () => {
  147. let firstOrderLineId: string;
  148. let createdCustomerId: string;
  149. let orderCode: string;
  150. it('addItemToOrder starts with no session token', () => {
  151. expect(shopClient.getAuthToken()).toBeFalsy();
  152. });
  153. it('activeOrder returns null before any items have been added', async () => {
  154. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  155. expect(result.activeOrder).toBeNull();
  156. });
  157. it('activeOrder creates an anonymous session', () => {
  158. expect(shopClient.getAuthToken()).not.toBe('');
  159. });
  160. it('addItemToOrder creates a new Order with an item', async () => {
  161. const { addItemToOrder } = await shopClient.query<
  162. AddItemToOrder.Mutation,
  163. AddItemToOrder.Variables
  164. >(ADD_ITEM_TO_ORDER, {
  165. productVariantId: 'T_1',
  166. quantity: 1,
  167. });
  168. orderResultGuard.assertSuccess(addItemToOrder);
  169. expect(addItemToOrder!.lines.length).toBe(1);
  170. expect(addItemToOrder!.lines[0].quantity).toBe(1);
  171. expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_1');
  172. expect(addItemToOrder!.lines[0].id).toBe('T_1');
  173. firstOrderLineId = addItemToOrder!.lines[0].id;
  174. orderCode = addItemToOrder!.code;
  175. });
  176. it(
  177. 'addItemToOrder errors with an invalid productVariantId',
  178. assertThrowsWithMessage(
  179. () =>
  180. shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  181. productVariantId: 'T_999',
  182. quantity: 1,
  183. }),
  184. `No ProductVariant with the id '999' could be found`,
  185. ),
  186. );
  187. it('addItemToOrder errors with a negative quantity', async () => {
  188. const { addItemToOrder } = await shopClient.query<
  189. AddItemToOrder.Mutation,
  190. AddItemToOrder.Variables
  191. >(ADD_ITEM_TO_ORDER, {
  192. productVariantId: 'T_999',
  193. quantity: -3,
  194. });
  195. orderResultGuard.assertErrorResult(addItemToOrder);
  196. expect(addItemToOrder.message).toEqual(`The quantity for an OrderItem cannot be negative`);
  197. expect(addItemToOrder.errorCode).toEqual(ErrorCode.NEGATIVE_QUANTITY_ERROR);
  198. });
  199. it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
  200. const { addItemToOrder } = await shopClient.query<
  201. AddItemToOrder.Mutation,
  202. AddItemToOrder.Variables
  203. >(ADD_ITEM_TO_ORDER, {
  204. productVariantId: 'T_1',
  205. quantity: 2,
  206. });
  207. orderResultGuard.assertSuccess(addItemToOrder);
  208. expect(addItemToOrder!.lines.length).toBe(1);
  209. expect(addItemToOrder!.lines[0].quantity).toBe(3);
  210. });
  211. it('addItemToOrder with equal customFields adds quantity to the existing OrderLine', async () => {
  212. const { addItemToOrder: add1 } = await shopClient.query<AddItemToOrder.Mutation>(
  213. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  214. {
  215. productVariantId: 'T_2',
  216. quantity: 1,
  217. customFields: {
  218. notes: 'note1',
  219. },
  220. },
  221. );
  222. orderResultGuard.assertSuccess(add1);
  223. expect(add1!.lines.length).toBe(2);
  224. expect(add1!.lines[1].quantity).toBe(1);
  225. const { addItemToOrder: add2 } = await shopClient.query<AddItemToOrder.Mutation>(
  226. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  227. {
  228. productVariantId: 'T_2',
  229. quantity: 1,
  230. customFields: {
  231. notes: 'note1',
  232. },
  233. },
  234. );
  235. orderResultGuard.assertSuccess(add2);
  236. expect(add2!.lines.length).toBe(2);
  237. expect(add2!.lines[1].quantity).toBe(2);
  238. await shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  239. REMOVE_ITEM_FROM_ORDER,
  240. {
  241. orderLineId: add2!.lines[1].id,
  242. },
  243. );
  244. });
  245. it('addItemToOrder with different customFields adds quantity to a new OrderLine', async () => {
  246. const { addItemToOrder: add1 } = await shopClient.query<AddItemToOrder.Mutation>(
  247. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  248. {
  249. productVariantId: 'T_3',
  250. quantity: 1,
  251. customFields: {
  252. notes: 'note2',
  253. },
  254. },
  255. );
  256. orderResultGuard.assertSuccess(add1);
  257. expect(add1!.lines.length).toBe(2);
  258. expect(add1!.lines[1].quantity).toBe(1);
  259. const { addItemToOrder: add2 } = await shopClient.query<AddItemToOrder.Mutation>(
  260. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  261. {
  262. productVariantId: 'T_3',
  263. quantity: 1,
  264. customFields: {
  265. notes: 'note3',
  266. },
  267. },
  268. );
  269. orderResultGuard.assertSuccess(add2);
  270. expect(add2!.lines.length).toBe(3);
  271. expect(add2!.lines[1].quantity).toBe(1);
  272. expect(add2!.lines[2].quantity).toBe(1);
  273. await shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  274. REMOVE_ITEM_FROM_ORDER,
  275. {
  276. orderLineId: add2!.lines[1].id,
  277. },
  278. );
  279. await shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  280. REMOVE_ITEM_FROM_ORDER,
  281. {
  282. orderLineId: add2!.lines[2].id,
  283. },
  284. );
  285. });
  286. it('addItemToOrder errors when going beyond orderItemsLimit', async () => {
  287. const { addItemToOrder } = await shopClient.query<
  288. AddItemToOrder.Mutation,
  289. AddItemToOrder.Variables
  290. >(ADD_ITEM_TO_ORDER, {
  291. productVariantId: 'T_1',
  292. quantity: 100,
  293. });
  294. orderResultGuard.assertErrorResult(addItemToOrder);
  295. expect(addItemToOrder.message).toBe(
  296. 'Cannot add items. An order may consist of a maximum of 99 items',
  297. );
  298. expect(addItemToOrder.errorCode).toBe(ErrorCode.ORDER_LIMIT_ERROR);
  299. });
  300. it('adjustOrderLine adjusts the quantity', async () => {
  301. const { adjustOrderLine } = await shopClient.query<
  302. AdjustItemQuantity.Mutation,
  303. AdjustItemQuantity.Variables
  304. >(ADJUST_ITEM_QUANTITY, {
  305. orderLineId: firstOrderLineId,
  306. quantity: 50,
  307. });
  308. orderResultGuard.assertSuccess(adjustOrderLine);
  309. expect(adjustOrderLine!.lines.length).toBe(1);
  310. expect(adjustOrderLine!.lines[0].quantity).toBe(50);
  311. });
  312. it('adjustOrderLine with quantity 0 removes the line', async () => {
  313. const { addItemToOrder } = await shopClient.query<
  314. AddItemToOrder.Mutation,
  315. AddItemToOrder.Variables
  316. >(ADD_ITEM_TO_ORDER, {
  317. productVariantId: 'T_3',
  318. quantity: 3,
  319. });
  320. orderResultGuard.assertSuccess(addItemToOrder);
  321. expect(addItemToOrder!.lines.length).toBe(2);
  322. expect(addItemToOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  323. const { adjustOrderLine } = await shopClient.query<
  324. AdjustItemQuantity.Mutation,
  325. AdjustItemQuantity.Variables
  326. >(ADJUST_ITEM_QUANTITY, {
  327. orderLineId: addItemToOrder?.lines[1].id!,
  328. quantity: 0,
  329. });
  330. orderResultGuard.assertSuccess(adjustOrderLine);
  331. expect(adjustOrderLine!.lines.length).toBe(1);
  332. expect(adjustOrderLine!.lines.map(i => i.productVariant.id)).toEqual(['T_1']);
  333. });
  334. it('adjustOrderLine errors when going beyond orderItemsLimit', async () => {
  335. const { adjustOrderLine } = await shopClient.query<
  336. AdjustItemQuantity.Mutation,
  337. AdjustItemQuantity.Variables
  338. >(ADJUST_ITEM_QUANTITY, {
  339. orderLineId: firstOrderLineId,
  340. quantity: 100,
  341. });
  342. orderResultGuard.assertErrorResult(adjustOrderLine);
  343. expect(adjustOrderLine.message).toBe(
  344. 'Cannot add items. An order may consist of a maximum of 99 items',
  345. );
  346. expect(adjustOrderLine.errorCode).toBe(ErrorCode.ORDER_LIMIT_ERROR);
  347. });
  348. it('adjustOrderLine errors with a negative quantity', async () => {
  349. const { adjustOrderLine } = await shopClient.query<
  350. AdjustItemQuantity.Mutation,
  351. AdjustItemQuantity.Variables
  352. >(ADJUST_ITEM_QUANTITY, {
  353. orderLineId: firstOrderLineId,
  354. quantity: -3,
  355. });
  356. orderResultGuard.assertErrorResult(adjustOrderLine);
  357. expect(adjustOrderLine.message).toBe('The quantity for an OrderItem cannot be negative');
  358. expect(adjustOrderLine.errorCode).toBe(ErrorCode.NEGATIVE_QUANTITY_ERROR);
  359. });
  360. it(
  361. 'adjustOrderLine errors with an invalid orderLineId',
  362. assertThrowsWithMessage(
  363. () =>
  364. shopClient.query<AdjustItemQuantity.Mutation, AdjustItemQuantity.Variables>(
  365. ADJUST_ITEM_QUANTITY,
  366. {
  367. orderLineId: 'T_999',
  368. quantity: 5,
  369. },
  370. ),
  371. `This order does not contain an OrderLine with the id 999`,
  372. ),
  373. );
  374. it('removeItemFromOrder removes the correct item', async () => {
  375. const { addItemToOrder } = await shopClient.query<
  376. AddItemToOrder.Mutation,
  377. AddItemToOrder.Variables
  378. >(ADD_ITEM_TO_ORDER, {
  379. productVariantId: 'T_3',
  380. quantity: 3,
  381. });
  382. orderResultGuard.assertSuccess(addItemToOrder);
  383. expect(addItemToOrder!.lines.length).toBe(2);
  384. expect(addItemToOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  385. const { removeOrderLine } = await shopClient.query<
  386. RemoveItemFromOrder.Mutation,
  387. RemoveItemFromOrder.Variables
  388. >(REMOVE_ITEM_FROM_ORDER, {
  389. orderLineId: firstOrderLineId,
  390. });
  391. orderResultGuard.assertSuccess(removeOrderLine);
  392. expect(removeOrderLine!.lines.length).toBe(1);
  393. expect(removeOrderLine!.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
  394. });
  395. it(
  396. 'removeItemFromOrder errors with an invalid orderItemId',
  397. assertThrowsWithMessage(
  398. () =>
  399. shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  400. REMOVE_ITEM_FROM_ORDER,
  401. {
  402. orderLineId: 'T_999',
  403. },
  404. ),
  405. `This order does not contain an OrderLine with the id 999`,
  406. ),
  407. );
  408. it('nextOrderStates returns next valid states', async () => {
  409. const result = await shopClient.query<GetNextOrderStates.Query>(GET_NEXT_STATES);
  410. expect(result.nextOrderStates).toEqual(['ArrangingPayment', 'Cancelled']);
  411. });
  412. it('transitionOrderToState returns error result for invalid state', async () => {
  413. const { transitionOrderToState } = await shopClient.query<
  414. TransitionToState.Mutation,
  415. TransitionToState.Variables
  416. >(TRANSITION_TO_STATE, { state: 'Completed' });
  417. orderResultGuard.assertErrorResult(transitionOrderToState);
  418. expect(transitionOrderToState!.message).toBe(
  419. `Cannot transition Order from "AddingItems" to "Completed"`,
  420. );
  421. expect(transitionOrderToState!.errorCode).toBe(ErrorCode.ORDER_STATE_TRANSITION_ERROR);
  422. });
  423. it('attempting to transition to ArrangingPayment returns error result when Order has no Customer', async () => {
  424. const { transitionOrderToState } = await shopClient.query<
  425. TransitionToState.Mutation,
  426. TransitionToState.Variables
  427. >(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
  428. orderResultGuard.assertErrorResult(transitionOrderToState);
  429. expect(transitionOrderToState!.transitionError).toBe(
  430. `Cannot transition Order to the "ArrangingPayment" state without Customer details`,
  431. );
  432. expect(transitionOrderToState!.errorCode).toBe(ErrorCode.ORDER_STATE_TRANSITION_ERROR);
  433. });
  434. it('setCustomerForOrder returns error result on email address conflict', async () => {
  435. const { customers } = await adminClient.query<GetCustomerList.Query>(GET_CUSTOMER_LIST);
  436. const { setCustomerForOrder } = await shopClient.query<
  437. SetCustomerForOrder.Mutation,
  438. SetCustomerForOrder.Variables
  439. >(SET_CUSTOMER, {
  440. input: {
  441. emailAddress: customers.items[0].emailAddress,
  442. firstName: 'Test',
  443. lastName: 'Person',
  444. },
  445. });
  446. orderResultGuard.assertErrorResult(setCustomerForOrder);
  447. expect(setCustomerForOrder!.message).toBe('The email address is not available.');
  448. expect(setCustomerForOrder!.errorCode).toBe(ErrorCode.EMAIL_ADDRESS_CONFLICT_ERROR);
  449. });
  450. it('setCustomerForOrder creates a new Customer and associates it with the Order', async () => {
  451. const { setCustomerForOrder } = await shopClient.query<
  452. SetCustomerForOrder.Mutation,
  453. SetCustomerForOrder.Variables
  454. >(SET_CUSTOMER, {
  455. input: {
  456. emailAddress: 'test@test.com',
  457. firstName: 'Test',
  458. lastName: 'Person',
  459. },
  460. });
  461. orderResultGuard.assertSuccess(setCustomerForOrder);
  462. const customer = setCustomerForOrder!.customer!;
  463. expect(customer.firstName).toBe('Test');
  464. expect(customer.lastName).toBe('Person');
  465. expect(customer.emailAddress).toBe('test@test.com');
  466. createdCustomerId = customer.id;
  467. });
  468. it('setCustomerForOrder updates the existing customer if Customer already set', async () => {
  469. const { setCustomerForOrder } = await shopClient.query<
  470. SetCustomerForOrder.Mutation,
  471. SetCustomerForOrder.Variables
  472. >(SET_CUSTOMER, {
  473. input: {
  474. emailAddress: 'test@test.com',
  475. firstName: 'Changed',
  476. lastName: 'Person',
  477. },
  478. });
  479. orderResultGuard.assertSuccess(setCustomerForOrder);
  480. const customer = setCustomerForOrder!.customer!;
  481. expect(customer.firstName).toBe('Changed');
  482. expect(customer.lastName).toBe('Person');
  483. expect(customer.emailAddress).toBe('test@test.com');
  484. expect(customer.id).toBe(createdCustomerId);
  485. });
  486. it('setOrderShippingAddress sets shipping address', async () => {
  487. const address: CreateAddressInput = {
  488. fullName: 'name',
  489. company: 'company',
  490. streetLine1: '12 the street',
  491. streetLine2: null,
  492. city: 'foo',
  493. province: 'bar',
  494. postalCode: '123456',
  495. countryCode: 'US',
  496. phoneNumber: '4444444',
  497. };
  498. const { setOrderShippingAddress } = await shopClient.query<
  499. SetShippingAddress.Mutation,
  500. SetShippingAddress.Variables
  501. >(SET_SHIPPING_ADDRESS, {
  502. input: address,
  503. });
  504. expect(setOrderShippingAddress!.shippingAddress).toEqual({
  505. fullName: 'name',
  506. company: 'company',
  507. streetLine1: '12 the street',
  508. streetLine2: null,
  509. city: 'foo',
  510. province: 'bar',
  511. postalCode: '123456',
  512. country: 'United States of America',
  513. phoneNumber: '4444444',
  514. });
  515. });
  516. it('setOrderBillingAddress sets billing address', async () => {
  517. const address: CreateAddressInput = {
  518. fullName: 'name',
  519. company: 'company',
  520. streetLine1: '12 the street',
  521. streetLine2: null,
  522. city: 'foo',
  523. province: 'bar',
  524. postalCode: '123456',
  525. countryCode: 'US',
  526. phoneNumber: '4444444',
  527. };
  528. const { setOrderBillingAddress } = await shopClient.query<
  529. SetBillingAddress.Mutation,
  530. SetBillingAddress.Variables
  531. >(SET_BILLING_ADDRESS, {
  532. input: address,
  533. });
  534. expect(setOrderBillingAddress!.billingAddress).toEqual({
  535. fullName: 'name',
  536. company: 'company',
  537. streetLine1: '12 the street',
  538. streetLine2: null,
  539. city: 'foo',
  540. province: 'bar',
  541. postalCode: '123456',
  542. country: 'United States of America',
  543. phoneNumber: '4444444',
  544. });
  545. });
  546. it('customer default Addresses are not updated before payment', async () => {
  547. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  548. const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
  549. GET_CUSTOMER,
  550. { id: activeOrder!.customer!.id },
  551. );
  552. expect(customer!.addresses).toEqual([]);
  553. });
  554. it('can transition to ArrangingPayment once Customer has been set', async () => {
  555. const { transitionOrderToState } = await shopClient.query<
  556. TransitionToState.Mutation,
  557. TransitionToState.Variables
  558. >(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
  559. orderResultGuard.assertSuccess(transitionOrderToState);
  560. expect(pick(transitionOrderToState, ['id', 'state'])).toEqual({
  561. id: 'T_1',
  562. state: 'ArrangingPayment',
  563. });
  564. });
  565. it('adds a successful payment and transitions Order state', async () => {
  566. const { addPaymentToOrder } = await shopClient.query<
  567. AddPaymentToOrder.Mutation,
  568. AddPaymentToOrder.Variables
  569. >(ADD_PAYMENT, {
  570. input: {
  571. method: testSuccessfulPaymentMethod.code,
  572. metadata: {},
  573. },
  574. });
  575. orderResultGuard.assertSuccess(addPaymentToOrder);
  576. const payment = addPaymentToOrder!.payments![0];
  577. expect(addPaymentToOrder!.state).toBe('PaymentSettled');
  578. expect(addPaymentToOrder!.active).toBe(false);
  579. expect(addPaymentToOrder!.payments!.length).toBe(1);
  580. expect(payment.method).toBe(testSuccessfulPaymentMethod.code);
  581. expect(payment.state).toBe('Settled');
  582. });
  583. it('activeOrder is null after payment', async () => {
  584. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  585. expect(result.activeOrder).toBeNull();
  586. });
  587. it('customer default Addresses are updated after payment', async () => {
  588. const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
  589. id: createdCustomerId,
  590. });
  591. // tslint:disable-next-line:no-non-null-assertion
  592. const address = result.customer!.addresses![0];
  593. expect(address.streetLine1).toBe('12 the street');
  594. expect(address.postalCode).toBe('123456');
  595. expect(address.defaultBillingAddress).toBe(true);
  596. expect(address.defaultShippingAddress).toBe(true);
  597. });
  598. });
  599. describe('ordering as authenticated user', () => {
  600. let firstOrderLineId: string;
  601. let activeOrder: UpdatedOrderFragment;
  602. let authenticatedUserEmailAddress: string;
  603. let customers: GetCustomerList.Items[];
  604. const password = 'test';
  605. beforeAll(async () => {
  606. await adminClient.asSuperAdmin();
  607. const result = await adminClient.query<GetCustomerList.Query, GetCustomerList.Variables>(
  608. GET_CUSTOMER_LIST,
  609. {
  610. options: {
  611. take: 2,
  612. },
  613. },
  614. );
  615. customers = result.customers.items;
  616. authenticatedUserEmailAddress = customers[0].emailAddress;
  617. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  618. });
  619. it('activeOrder returns null before any items have been added', async () => {
  620. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  621. expect(result.activeOrder).toBeNull();
  622. });
  623. it('addItemToOrder creates a new Order with an item', async () => {
  624. const { addItemToOrder } = await shopClient.query<
  625. AddItemToOrder.Mutation,
  626. AddItemToOrder.Variables
  627. >(ADD_ITEM_TO_ORDER, {
  628. productVariantId: 'T_1',
  629. quantity: 1,
  630. });
  631. orderResultGuard.assertSuccess(addItemToOrder);
  632. expect(addItemToOrder!.lines.length).toBe(1);
  633. expect(addItemToOrder!.lines[0].quantity).toBe(1);
  634. expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_1');
  635. activeOrder = addItemToOrder!;
  636. firstOrderLineId = addItemToOrder!.lines[0].id;
  637. });
  638. it('activeOrder returns order after item has been added', async () => {
  639. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  640. expect(result.activeOrder!.id).toBe(activeOrder.id);
  641. expect(result.activeOrder!.state).toBe('AddingItems');
  642. });
  643. it('activeOrder resolves customer user', async () => {
  644. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  645. expect(result.activeOrder!.customer!.user).toEqual({
  646. id: 'T_2',
  647. identifier: 'hayden.zieme12@hotmail.com',
  648. });
  649. });
  650. it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
  651. const { addItemToOrder } = await shopClient.query<
  652. AddItemToOrder.Mutation,
  653. AddItemToOrder.Variables
  654. >(ADD_ITEM_TO_ORDER, {
  655. productVariantId: 'T_1',
  656. quantity: 2,
  657. });
  658. orderResultGuard.assertSuccess(addItemToOrder);
  659. expect(addItemToOrder!.lines.length).toBe(1);
  660. expect(addItemToOrder!.lines[0].quantity).toBe(3);
  661. });
  662. it('adjustOrderLine adjusts the quantity', async () => {
  663. const { adjustOrderLine } = await shopClient.query<
  664. AdjustItemQuantity.Mutation,
  665. AdjustItemQuantity.Variables
  666. >(ADJUST_ITEM_QUANTITY, {
  667. orderLineId: firstOrderLineId,
  668. quantity: 50,
  669. });
  670. orderResultGuard.assertSuccess(adjustOrderLine);
  671. expect(adjustOrderLine!.lines.length).toBe(1);
  672. expect(adjustOrderLine!.lines[0].quantity).toBe(50);
  673. });
  674. it('removeItemFromOrder removes the correct item', async () => {
  675. const { addItemToOrder } = await shopClient.query<
  676. AddItemToOrder.Mutation,
  677. AddItemToOrder.Variables
  678. >(ADD_ITEM_TO_ORDER, {
  679. productVariantId: 'T_3',
  680. quantity: 3,
  681. });
  682. orderResultGuard.assertSuccess(addItemToOrder);
  683. expect(addItemToOrder!.lines.length).toBe(2);
  684. expect(addItemToOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  685. const { removeOrderLine } = await shopClient.query<
  686. RemoveItemFromOrder.Mutation,
  687. RemoveItemFromOrder.Variables
  688. >(REMOVE_ITEM_FROM_ORDER, {
  689. orderLineId: firstOrderLineId,
  690. });
  691. orderResultGuard.assertSuccess(removeOrderLine);
  692. expect(removeOrderLine!.lines.length).toBe(1);
  693. expect(removeOrderLine!.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
  694. });
  695. it('nextOrderStates returns next valid states', async () => {
  696. const result = await shopClient.query<GetNextOrderStates.Query>(GET_NEXT_STATES);
  697. expect(result.nextOrderStates).toEqual(['ArrangingPayment', 'Cancelled']);
  698. });
  699. it('logging out and back in again resumes the last active order', async () => {
  700. await shopClient.asAnonymousUser();
  701. const result1 = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  702. expect(result1.activeOrder).toBeNull();
  703. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  704. const result2 = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  705. expect(result2.activeOrder!.id).toBe(activeOrder.id);
  706. });
  707. it('cannot setCustomerForOrder when already logged in', async () => {
  708. const { setCustomerForOrder } = await shopClient.query<
  709. SetCustomerForOrder.Mutation,
  710. SetCustomerForOrder.Variables
  711. >(SET_CUSTOMER, {
  712. input: {
  713. emailAddress: 'newperson@email.com',
  714. firstName: 'New',
  715. lastName: 'Person',
  716. },
  717. });
  718. orderResultGuard.assertErrorResult(setCustomerForOrder);
  719. expect(setCustomerForOrder!.message).toBe(
  720. 'Cannot set a Customer for the Order when already logged in',
  721. );
  722. expect(setCustomerForOrder!.errorCode).toBe(ErrorCode.ALREADY_LOGGED_IN_ERROR);
  723. });
  724. describe('shipping', () => {
  725. let shippingMethods: GetShippingMethods.EligibleShippingMethods[];
  726. it(
  727. 'setOrderShippingAddress throws with invalid countryCode',
  728. assertThrowsWithMessage(() => {
  729. const address: CreateAddressInput = {
  730. streetLine1: '12 the street',
  731. countryCode: 'INVALID',
  732. };
  733. return shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
  734. SET_SHIPPING_ADDRESS,
  735. {
  736. input: address,
  737. },
  738. );
  739. }, `The countryCode "INVALID" was not recognized`),
  740. );
  741. it('setOrderShippingAddress sets shipping address', async () => {
  742. const address: CreateAddressInput = {
  743. fullName: 'name',
  744. company: 'company',
  745. streetLine1: '12 the street',
  746. streetLine2: null,
  747. city: 'foo',
  748. province: 'bar',
  749. postalCode: '123456',
  750. countryCode: 'US',
  751. phoneNumber: '4444444',
  752. };
  753. const { setOrderShippingAddress } = await shopClient.query<
  754. SetShippingAddress.Mutation,
  755. SetShippingAddress.Variables
  756. >(SET_SHIPPING_ADDRESS, {
  757. input: address,
  758. });
  759. expect(setOrderShippingAddress!.shippingAddress).toEqual({
  760. fullName: 'name',
  761. company: 'company',
  762. streetLine1: '12 the street',
  763. streetLine2: null,
  764. city: 'foo',
  765. province: 'bar',
  766. postalCode: '123456',
  767. country: 'United States of America',
  768. phoneNumber: '4444444',
  769. });
  770. });
  771. it('eligibleShippingMethods lists shipping methods', async () => {
  772. const result = await shopClient.query<GetShippingMethods.Query>(
  773. GET_ELIGIBLE_SHIPPING_METHODS,
  774. );
  775. shippingMethods = result.eligibleShippingMethods;
  776. expect(shippingMethods).toEqual([
  777. { id: 'T_1', price: 500, name: 'Standard Shipping', description: '' },
  778. { id: 'T_2', price: 1000, name: 'Express Shipping', description: '' },
  779. ]);
  780. });
  781. it('shipping is initially unset', async () => {
  782. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  783. expect(result.activeOrder!.shipping).toEqual(0);
  784. expect(result.activeOrder!.shippingLines).toEqual([]);
  785. });
  786. it('setOrderShippingMethod sets the shipping method', async () => {
  787. const result = await shopClient.query<
  788. SetShippingMethod.Mutation,
  789. SetShippingMethod.Variables
  790. >(SET_SHIPPING_METHOD, {
  791. id: shippingMethods[1].id,
  792. });
  793. const activeOrderResult = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  794. const order = activeOrderResult.activeOrder!;
  795. expect(order.shipping).toBe(shippingMethods[1].price);
  796. expect(order.shippingLines[0].shippingMethod!.id).toBe(shippingMethods[1].id);
  797. expect(order.shippingLines[0].shippingMethod!.description).toBe(
  798. shippingMethods[1].description,
  799. );
  800. });
  801. it('shipping method is preserved after adjustOrderLine', async () => {
  802. const activeOrderResult = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  803. activeOrder = activeOrderResult.activeOrder!;
  804. const { adjustOrderLine } = await shopClient.query<
  805. AdjustItemQuantity.Mutation,
  806. AdjustItemQuantity.Variables
  807. >(ADJUST_ITEM_QUANTITY, {
  808. orderLineId: activeOrder.lines[0].id,
  809. quantity: 10,
  810. });
  811. orderResultGuard.assertSuccess(adjustOrderLine);
  812. expect(adjustOrderLine!.shipping).toBe(shippingMethods[1].price);
  813. expect(adjustOrderLine!.shippingLines[0].shippingMethod!.id).toBe(shippingMethods[1].id);
  814. expect(adjustOrderLine!.shippingLines[0].shippingMethod!.description).toBe(
  815. shippingMethods[1].description,
  816. );
  817. });
  818. });
  819. describe('payment', () => {
  820. it('attempting add a Payment returns error result when in AddingItems state', async () => {
  821. const { addPaymentToOrder } = await shopClient.query<
  822. AddPaymentToOrder.Mutation,
  823. AddPaymentToOrder.Variables
  824. >(ADD_PAYMENT, {
  825. input: {
  826. method: testSuccessfulPaymentMethod.code,
  827. metadata: {},
  828. },
  829. });
  830. orderResultGuard.assertErrorResult(addPaymentToOrder);
  831. expect(addPaymentToOrder!.message).toBe(
  832. `A Payment may only be added when Order is in "ArrangingPayment" state`,
  833. );
  834. expect(addPaymentToOrder!.errorCode).toBe(ErrorCode.ORDER_PAYMENT_STATE_ERROR);
  835. });
  836. it('transitions to the ArrangingPayment state', async () => {
  837. const { transitionOrderToState } = await shopClient.query<
  838. TransitionToState.Mutation,
  839. TransitionToState.Variables
  840. >(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
  841. orderResultGuard.assertSuccess(transitionOrderToState);
  842. expect(pick(transitionOrderToState, ['id', 'state'])).toEqual({
  843. id: activeOrder.id,
  844. state: 'ArrangingPayment',
  845. });
  846. });
  847. it('attempting to add an item returns error result when in ArrangingPayment state', async () => {
  848. const { addItemToOrder } = await shopClient.query<
  849. AddItemToOrder.Mutation,
  850. AddItemToOrder.Variables
  851. >(ADD_ITEM_TO_ORDER, {
  852. productVariantId: 'T_4',
  853. quantity: 1,
  854. });
  855. orderResultGuard.assertErrorResult(addItemToOrder);
  856. expect(addItemToOrder.message).toBe(
  857. `Order contents may only be modified when in the "AddingItems" state`,
  858. );
  859. expect(addItemToOrder.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  860. });
  861. it('attempting to modify item quantity returns error result when in ArrangingPayment state', async () => {
  862. const { adjustOrderLine } = await shopClient.query<
  863. AdjustItemQuantity.Mutation,
  864. AdjustItemQuantity.Variables
  865. >(ADJUST_ITEM_QUANTITY, {
  866. orderLineId: activeOrder.lines[0].id,
  867. quantity: 12,
  868. });
  869. orderResultGuard.assertErrorResult(adjustOrderLine);
  870. expect(adjustOrderLine.message).toBe(
  871. `Order contents may only be modified when in the "AddingItems" state`,
  872. );
  873. expect(adjustOrderLine.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  874. });
  875. it('attempting to remove an item returns error result when in ArrangingPayment state', async () => {
  876. const { removeOrderLine } = await shopClient.query<
  877. RemoveItemFromOrder.Mutation,
  878. RemoveItemFromOrder.Variables
  879. >(REMOVE_ITEM_FROM_ORDER, {
  880. orderLineId: activeOrder.lines[0].id,
  881. });
  882. orderResultGuard.assertErrorResult(removeOrderLine);
  883. expect(removeOrderLine.message).toBe(
  884. `Order contents may only be modified when in the "AddingItems" state`,
  885. );
  886. expect(removeOrderLine.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  887. });
  888. it('attempting to remove all items returns error result when in ArrangingPayment state', async () => {
  889. const { removeAllOrderLines } = await shopClient.query<RemoveAllOrderLines.Mutation>(
  890. REMOVE_ALL_ORDER_LINES,
  891. );
  892. orderResultGuard.assertErrorResult(removeAllOrderLines);
  893. expect(removeAllOrderLines.message).toBe(
  894. `Order contents may only be modified when in the "AddingItems" state`,
  895. );
  896. expect(removeAllOrderLines.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  897. });
  898. it('attempting to setOrderShippingMethod returns error result when in ArrangingPayment state', async () => {
  899. const shippingMethodsResult = await shopClient.query<GetShippingMethods.Query>(
  900. GET_ELIGIBLE_SHIPPING_METHODS,
  901. );
  902. const shippingMethods = shippingMethodsResult.eligibleShippingMethods;
  903. const { setOrderShippingMethod } = await shopClient.query<
  904. SetShippingMethod.Mutation,
  905. SetShippingMethod.Variables
  906. >(SET_SHIPPING_METHOD, {
  907. id: shippingMethods[0].id,
  908. });
  909. orderResultGuard.assertErrorResult(setOrderShippingMethod);
  910. expect(setOrderShippingMethod.message).toBe(
  911. `Order contents may only be modified when in the "AddingItems" state`,
  912. );
  913. expect(setOrderShippingMethod.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  914. });
  915. it('adds a declined payment', async () => {
  916. const { addPaymentToOrder } = await shopClient.query<
  917. AddPaymentToOrder.Mutation,
  918. AddPaymentToOrder.Variables
  919. >(ADD_PAYMENT, {
  920. input: {
  921. method: testFailingPaymentMethod.code,
  922. metadata: {
  923. foo: 'bar',
  924. },
  925. },
  926. });
  927. orderResultGuard.assertErrorResult(addPaymentToOrder);
  928. expect(addPaymentToOrder!.message).toBe('The payment was declined');
  929. expect(addPaymentToOrder!.errorCode).toBe(ErrorCode.PAYMENT_DECLINED_ERROR);
  930. expect((addPaymentToOrder as any).paymentErrorMessage).toBe('Insufficient funds');
  931. const { activeOrder: order } = await shopClient.query<GetActiveOrderWithPayments.Query>(
  932. GET_ACTIVE_ORDER_WITH_PAYMENTS,
  933. );
  934. const payment = order!.payments![0];
  935. expect(order!.payments!.length).toBe(1);
  936. expect(payment.method).toBe(testFailingPaymentMethod.code);
  937. expect(payment.state).toBe('Declined');
  938. expect(payment.transactionId).toBe(null);
  939. expect(payment.metadata).toEqual({
  940. public: { foo: 'bar' },
  941. });
  942. });
  943. it('adds an error payment and returns error result', async () => {
  944. const { addPaymentToOrder } = await shopClient.query<
  945. AddPaymentToOrder.Mutation,
  946. AddPaymentToOrder.Variables
  947. >(ADD_PAYMENT, {
  948. input: {
  949. method: testErrorPaymentMethod.code,
  950. metadata: {
  951. foo: 'bar',
  952. },
  953. },
  954. });
  955. orderResultGuard.assertErrorResult(addPaymentToOrder);
  956. expect(addPaymentToOrder!.message).toBe('The payment failed');
  957. expect(addPaymentToOrder!.errorCode).toBe(ErrorCode.PAYMENT_FAILED_ERROR);
  958. expect((addPaymentToOrder as any).paymentErrorMessage).toBe('Something went horribly wrong');
  959. const result = await shopClient.query<GetActiveOrderPayments.Query>(
  960. GET_ACTIVE_ORDER_PAYMENTS,
  961. );
  962. const payment = result.activeOrder!.payments![1];
  963. expect(result.activeOrder!.payments!.length).toBe(2);
  964. expect(payment.method).toBe(testErrorPaymentMethod.code);
  965. expect(payment.state).toBe('Error');
  966. expect(payment.errorMessage).toBe('Something went horribly wrong');
  967. });
  968. it('adds a successful payment and transitions Order state', async () => {
  969. const { addPaymentToOrder } = await shopClient.query<
  970. AddPaymentToOrder.Mutation,
  971. AddPaymentToOrder.Variables
  972. >(ADD_PAYMENT, {
  973. input: {
  974. method: testSuccessfulPaymentMethod.code,
  975. metadata: {
  976. baz: 'quux',
  977. },
  978. },
  979. });
  980. orderResultGuard.assertSuccess(addPaymentToOrder);
  981. const payment = addPaymentToOrder!.payments!.find(p => p.transactionId === '12345')!;
  982. expect(addPaymentToOrder!.state).toBe('PaymentSettled');
  983. expect(addPaymentToOrder!.active).toBe(false);
  984. expect(addPaymentToOrder!.payments!.length).toBe(3);
  985. expect(payment.method).toBe(testSuccessfulPaymentMethod.code);
  986. expect(payment.state).toBe('Settled');
  987. expect(payment.transactionId).toBe('12345');
  988. expect(payment.metadata).toEqual({
  989. public: { baz: 'quux' },
  990. });
  991. });
  992. it('does not create new address when Customer already has address', async () => {
  993. const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
  994. GET_CUSTOMER,
  995. { id: customers[0].id },
  996. );
  997. expect(customer!.addresses!.length).toBe(1);
  998. });
  999. });
  1000. describe('orderByCode', () => {
  1001. describe('immediately after Order is placed', () => {
  1002. it('works when authenticated', async () => {
  1003. const result = await shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
  1004. GET_ORDER_BY_CODE,
  1005. {
  1006. code: activeOrder.code,
  1007. },
  1008. );
  1009. expect(result.orderByCode!.id).toBe(activeOrder.id);
  1010. });
  1011. it('works when anonymous', async () => {
  1012. await shopClient.asAnonymousUser();
  1013. const result = await shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
  1014. GET_ORDER_BY_CODE,
  1015. {
  1016. code: activeOrder.code,
  1017. },
  1018. );
  1019. expect(result.orderByCode!.id).toBe(activeOrder.id);
  1020. });
  1021. it(
  1022. `throws error for another user's Order`,
  1023. assertThrowsWithMessage(async () => {
  1024. authenticatedUserEmailAddress = customers[1].emailAddress;
  1025. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  1026. return shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
  1027. GET_ORDER_BY_CODE,
  1028. {
  1029. code: activeOrder.code,
  1030. },
  1031. );
  1032. }, `You are not currently authorized to perform this action`),
  1033. );
  1034. });
  1035. });
  1036. });
  1037. describe('order merging', () => {
  1038. let customers: GetCustomerList.Items[];
  1039. beforeAll(async () => {
  1040. const result = await adminClient.query<GetCustomerList.Query>(GET_CUSTOMER_LIST);
  1041. customers = result.customers.items;
  1042. });
  1043. it('merges guest order with no existing order', async () => {
  1044. await shopClient.asAnonymousUser();
  1045. const { addItemToOrder } = await shopClient.query<
  1046. AddItemToOrder.Mutation,
  1047. AddItemToOrder.Variables
  1048. >(ADD_ITEM_TO_ORDER, {
  1049. productVariantId: 'T_1',
  1050. quantity: 1,
  1051. });
  1052. orderResultGuard.assertSuccess(addItemToOrder);
  1053. expect(addItemToOrder!.lines.length).toBe(1);
  1054. expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_1');
  1055. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  1056. username: customers[1].emailAddress,
  1057. password: 'test',
  1058. });
  1059. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1060. expect(activeOrder!.lines.length).toBe(1);
  1061. expect(activeOrder!.lines[0].productVariant.id).toBe('T_1');
  1062. });
  1063. it('merges guest order with existing order', async () => {
  1064. await shopClient.asAnonymousUser();
  1065. const { addItemToOrder } = await shopClient.query<
  1066. AddItemToOrder.Mutation,
  1067. AddItemToOrder.Variables
  1068. >(ADD_ITEM_TO_ORDER, {
  1069. productVariantId: 'T_2',
  1070. quantity: 1,
  1071. });
  1072. orderResultGuard.assertSuccess(addItemToOrder);
  1073. expect(addItemToOrder!.lines.length).toBe(1);
  1074. expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_2');
  1075. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  1076. username: customers[1].emailAddress,
  1077. password: 'test',
  1078. });
  1079. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1080. expect(activeOrder!.lines.length).toBe(2);
  1081. expect(activeOrder!.lines[0].productVariant.id).toBe('T_1');
  1082. expect(activeOrder!.lines[1].productVariant.id).toBe('T_2');
  1083. });
  1084. /**
  1085. * See https://github.com/vendure-ecommerce/vendure/issues/263
  1086. */
  1087. it('does not merge when logging in to a different account (issue #263)', async () => {
  1088. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  1089. username: customers[2].emailAddress,
  1090. password: 'test',
  1091. });
  1092. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1093. expect(activeOrder).toBeNull();
  1094. });
  1095. it('does not merge when logging back to other account (issue #263)', async () => {
  1096. const { addItemToOrder } = await shopClient.query<
  1097. AddItemToOrder.Mutation,
  1098. AddItemToOrder.Variables
  1099. >(ADD_ITEM_TO_ORDER, {
  1100. productVariantId: 'T_3',
  1101. quantity: 1,
  1102. });
  1103. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  1104. username: customers[1].emailAddress,
  1105. password: 'test',
  1106. });
  1107. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1108. expect(activeOrder!.lines.length).toBe(2);
  1109. expect(activeOrder!.lines[0].productVariant.id).toBe('T_1');
  1110. expect(activeOrder!.lines[1].productVariant.id).toBe('T_2');
  1111. });
  1112. });
  1113. describe('security of customer data', () => {
  1114. let customers: GetCustomerList.Items[];
  1115. beforeAll(async () => {
  1116. const result = await adminClient.query<GetCustomerList.Query>(GET_CUSTOMER_LIST);
  1117. customers = result.customers.items;
  1118. });
  1119. it('cannot setCustomOrder to existing non-guest Customer', async () => {
  1120. await shopClient.asAnonymousUser();
  1121. const { addItemToOrder } = await shopClient.query<
  1122. AddItemToOrder.Mutation,
  1123. AddItemToOrder.Variables
  1124. >(ADD_ITEM_TO_ORDER, {
  1125. productVariantId: 'T_1',
  1126. quantity: 1,
  1127. });
  1128. const { setCustomerForOrder } = await shopClient.query<
  1129. SetCustomerForOrder.Mutation,
  1130. SetCustomerForOrder.Variables
  1131. >(SET_CUSTOMER, {
  1132. input: {
  1133. emailAddress: customers[0].emailAddress,
  1134. firstName: 'Evil',
  1135. lastName: 'Hacker',
  1136. },
  1137. });
  1138. orderResultGuard.assertErrorResult(setCustomerForOrder);
  1139. expect(setCustomerForOrder!.message).toBe('The email address is not available.');
  1140. expect(setCustomerForOrder!.errorCode).toBe(ErrorCode.EMAIL_ADDRESS_CONFLICT_ERROR);
  1141. const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
  1142. GET_CUSTOMER,
  1143. {
  1144. id: customers[0].id,
  1145. },
  1146. );
  1147. expect(customer!.firstName).not.toBe('Evil');
  1148. expect(customer!.lastName).not.toBe('Hacker');
  1149. });
  1150. it('guest cannot access Addresses of guest customer', async () => {
  1151. await shopClient.asAnonymousUser();
  1152. const { addItemToOrder } = await shopClient.query<
  1153. AddItemToOrder.Mutation,
  1154. AddItemToOrder.Variables
  1155. >(ADD_ITEM_TO_ORDER, {
  1156. productVariantId: 'T_1',
  1157. quantity: 1,
  1158. });
  1159. await shopClient.query<SetCustomerForOrder.Mutation, SetCustomerForOrder.Variables>(
  1160. SET_CUSTOMER,
  1161. {
  1162. input: {
  1163. emailAddress: 'test@test.com',
  1164. firstName: 'Evil',
  1165. lastName: 'Hacker',
  1166. },
  1167. },
  1168. );
  1169. const { activeOrder } = await shopClient.query<GetCustomerAddresses.Query>(
  1170. GET_ACTIVE_ORDER_ADDRESSES,
  1171. );
  1172. expect(activeOrder!.customer!.addresses).toEqual([]);
  1173. });
  1174. it('guest cannot access Orders of guest customer', async () => {
  1175. await shopClient.asAnonymousUser();
  1176. const { addItemToOrder } = await shopClient.query<
  1177. AddItemToOrder.Mutation,
  1178. AddItemToOrder.Variables
  1179. >(ADD_ITEM_TO_ORDER, {
  1180. productVariantId: 'T_1',
  1181. quantity: 1,
  1182. });
  1183. await shopClient.query<SetCustomerForOrder.Mutation, SetCustomerForOrder.Variables>(
  1184. SET_CUSTOMER,
  1185. {
  1186. input: {
  1187. emailAddress: 'test@test.com',
  1188. firstName: 'Evil',
  1189. lastName: 'Hacker',
  1190. },
  1191. },
  1192. );
  1193. const { activeOrder } = await shopClient.query<GetCustomerOrders.Query>(GET_ACTIVE_ORDER_ORDERS);
  1194. expect(activeOrder!.customer!.orders.items).toEqual([]);
  1195. });
  1196. });
  1197. describe('order custom fields', () => {
  1198. it('custom fields added to type', async () => {
  1199. await shopClient.asAnonymousUser();
  1200. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1201. productVariantId: 'T_1',
  1202. quantity: 1,
  1203. });
  1204. const { activeOrder } = await shopClient.query(GET_ORDER_CUSTOM_FIELDS);
  1205. expect(activeOrder?.customFields).toEqual({
  1206. giftWrap: false,
  1207. });
  1208. });
  1209. it('setting order custom fields', async () => {
  1210. const { setOrderCustomFields } = await shopClient.query(SET_ORDER_CUSTOM_FIELDS, {
  1211. input: {
  1212. customFields: { giftWrap: true },
  1213. },
  1214. });
  1215. expect(setOrderCustomFields?.customFields).toEqual({
  1216. giftWrap: true,
  1217. });
  1218. const { activeOrder } = await shopClient.query(GET_ORDER_CUSTOM_FIELDS);
  1219. expect(activeOrder?.customFields).toEqual({
  1220. giftWrap: true,
  1221. });
  1222. });
  1223. });
  1224. describe('remove all order lines', () => {
  1225. beforeAll(async () => {
  1226. await shopClient.asAnonymousUser();
  1227. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1228. productVariantId: 'T_1',
  1229. quantity: 1,
  1230. });
  1231. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1232. productVariantId: 'T_2',
  1233. quantity: 3,
  1234. });
  1235. });
  1236. it('should remove all order lines', async () => {
  1237. const { removeAllOrderLines } = await shopClient.query<
  1238. RemoveAllOrderLines.Mutation,
  1239. RemoveAllOrderLines.Variables
  1240. >(REMOVE_ALL_ORDER_LINES);
  1241. orderResultGuard.assertSuccess(removeAllOrderLines);
  1242. expect(removeAllOrderLines?.total).toBe(0);
  1243. expect(removeAllOrderLines?.lines.length).toBe(0);
  1244. });
  1245. });
  1246. });
  1247. const GET_ORDER_CUSTOM_FIELDS = gql`
  1248. query GetOrderCustomFields {
  1249. activeOrder {
  1250. id
  1251. customFields {
  1252. giftWrap
  1253. }
  1254. }
  1255. }
  1256. `;
  1257. const SET_ORDER_CUSTOM_FIELDS = gql`
  1258. mutation SetOrderCustomFields($input: UpdateOrderInput!) {
  1259. setOrderCustomFields(input: $input) {
  1260. ... on Order {
  1261. id
  1262. customFields {
  1263. giftWrap
  1264. }
  1265. }
  1266. ... on ErrorResult {
  1267. errorCode
  1268. message
  1269. }
  1270. }
  1271. }
  1272. `;
  1273. export const ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS = gql`
  1274. mutation AddItemToOrderWithCustomFields(
  1275. $productVariantId: ID!
  1276. $quantity: Int!
  1277. $customFields: OrderLineCustomFieldsInput
  1278. ) {
  1279. addItemToOrder(
  1280. productVariantId: $productVariantId
  1281. quantity: $quantity
  1282. customFields: $customFields
  1283. ) {
  1284. ...UpdatedOrder
  1285. ... on ErrorResult {
  1286. errorCode
  1287. message
  1288. }
  1289. }
  1290. }
  1291. ${UPDATED_ORDER_FRAGMENT}
  1292. `;