shop-order.e2e-spec.ts 96 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359
  1. /* tslint:disable:no-non-null-assertion */
  2. import { pick } from '@vendure/common/lib/pick';
  3. import {
  4. Asset,
  5. defaultShippingCalculator,
  6. defaultShippingEligibilityChecker,
  7. mergeConfig,
  8. } from '@vendure/core';
  9. import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
  10. import gql from 'graphql-tag';
  11. import path from 'path';
  12. import { initialData } from '../../../e2e-common/e2e-initial-data';
  13. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  14. import { manualFulfillmentHandler } from '../src/index';
  15. import {
  16. testErrorPaymentMethod,
  17. testFailingPaymentMethod,
  18. testSuccessfulPaymentMethod,
  19. } from './fixtures/test-payment-methods';
  20. import { countryCodeShippingEligibilityChecker } from './fixtures/test-shipping-eligibility-checkers';
  21. import {
  22. AttemptLogin,
  23. CanceledOrderFragment,
  24. CancelOrderMutation,
  25. CancelOrderMutationVariables,
  26. CreateAddressInput,
  27. CreateShippingMethod,
  28. CreateShippingMethodInput,
  29. DeleteProduct,
  30. DeleteProductVariant,
  31. DeleteShippingMethod,
  32. GetCountryList,
  33. GetCustomer,
  34. GetCustomerList,
  35. GetShippingMethodList,
  36. LanguageCode,
  37. UpdateCountry,
  38. UpdateProduct,
  39. UpdateProductVariants,
  40. } from './graphql/generated-e2e-admin-types';
  41. import {
  42. ActiveOrderCustomerFragment,
  43. AddItemToOrder,
  44. AddPaymentToOrder,
  45. AdjustItemQuantity,
  46. ErrorCode,
  47. GetActiveOrder,
  48. GetActiveOrderPayments,
  49. GetActiveOrderWithPayments,
  50. GetAvailableCountries,
  51. GetCustomerAddresses,
  52. GetCustomerOrders,
  53. GetNextOrderStates,
  54. GetOrderByCode,
  55. GetShippingMethods,
  56. RemoveAllOrderLines,
  57. RemoveItemFromOrder,
  58. SetBillingAddress,
  59. SetCustomerForOrder,
  60. SetShippingAddress,
  61. SetShippingMethod,
  62. TestOrderFragmentFragment,
  63. TestOrderWithPaymentsFragment,
  64. TransitionToState,
  65. UpdatedOrderFragment,
  66. } from './graphql/generated-e2e-shop-types';
  67. import {
  68. ATTEMPT_LOGIN,
  69. CANCEL_ORDER,
  70. CREATE_SHIPPING_METHOD,
  71. DELETE_PRODUCT,
  72. DELETE_PRODUCT_VARIANT,
  73. DELETE_SHIPPING_METHOD,
  74. GET_COUNTRY_LIST,
  75. GET_CUSTOMER,
  76. GET_CUSTOMER_LIST,
  77. GET_SHIPPING_METHOD_LIST,
  78. UPDATE_COUNTRY,
  79. UPDATE_PRODUCT,
  80. UPDATE_PRODUCT_VARIANTS,
  81. } from './graphql/shared-definitions';
  82. import {
  83. ADD_ITEM_TO_ORDER,
  84. ADD_PAYMENT,
  85. ADJUST_ITEM_QUANTITY,
  86. GET_ACTIVE_ORDER,
  87. GET_ACTIVE_ORDER_ADDRESSES,
  88. GET_ACTIVE_ORDER_ORDERS,
  89. GET_ACTIVE_ORDER_PAYMENTS,
  90. GET_ACTIVE_ORDER_WITH_PAYMENTS,
  91. GET_AVAILABLE_COUNTRIES,
  92. GET_ELIGIBLE_SHIPPING_METHODS,
  93. GET_NEXT_STATES,
  94. GET_ORDER_BY_CODE,
  95. REMOVE_ALL_ORDER_LINES,
  96. REMOVE_ITEM_FROM_ORDER,
  97. SET_BILLING_ADDRESS,
  98. SET_CUSTOMER,
  99. SET_SHIPPING_ADDRESS,
  100. SET_SHIPPING_METHOD,
  101. TRANSITION_TO_STATE,
  102. UPDATED_ORDER_FRAGMENT,
  103. } from './graphql/shop-definitions';
  104. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  105. describe('Shop orders', () => {
  106. const { server, adminClient, shopClient } = createTestEnvironment(
  107. mergeConfig(testConfig(), {
  108. paymentOptions: {
  109. paymentMethodHandlers: [
  110. testSuccessfulPaymentMethod,
  111. testFailingPaymentMethod,
  112. testErrorPaymentMethod,
  113. ],
  114. },
  115. shippingOptions: {
  116. shippingEligibilityCheckers: [
  117. defaultShippingEligibilityChecker,
  118. countryCodeShippingEligibilityChecker,
  119. ],
  120. },
  121. customFields: {
  122. Order: [
  123. { name: 'giftWrap', type: 'boolean', defaultValue: false },
  124. { name: 'orderImage', type: 'relation', entity: Asset },
  125. ],
  126. OrderLine: [
  127. { name: 'notes', type: 'string' },
  128. { name: 'privateField', type: 'string', public: false },
  129. { name: 'lineImage', type: 'relation', entity: Asset },
  130. { name: 'lineImages', type: 'relation', list: true, entity: Asset },
  131. { name: 'dropShip', type: 'boolean', defaultValue: false },
  132. ],
  133. },
  134. orderOptions: {
  135. orderItemsLimit: 199,
  136. },
  137. }),
  138. );
  139. type OrderSuccessResult =
  140. | UpdatedOrderFragment
  141. | TestOrderFragmentFragment
  142. | TestOrderWithPaymentsFragment
  143. | CanceledOrderFragment
  144. | ActiveOrderCustomerFragment;
  145. const orderResultGuard: ErrorResultGuard<OrderSuccessResult> = createErrorResultGuard(
  146. input => !!input.lines,
  147. );
  148. beforeAll(async () => {
  149. await server.init({
  150. initialData: {
  151. ...initialData,
  152. paymentMethods: [
  153. {
  154. name: testSuccessfulPaymentMethod.code,
  155. handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
  156. },
  157. {
  158. name: testFailingPaymentMethod.code,
  159. handler: { code: testFailingPaymentMethod.code, arguments: [] },
  160. },
  161. {
  162. name: testErrorPaymentMethod.code,
  163. handler: { code: testErrorPaymentMethod.code, arguments: [] },
  164. },
  165. ],
  166. },
  167. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  168. customerCount: 3,
  169. });
  170. await adminClient.asSuperAdmin();
  171. }, TEST_SETUP_TIMEOUT_MS);
  172. afterAll(async () => {
  173. await server.destroy();
  174. });
  175. it('availableCountries returns enabled countries', async () => {
  176. // disable Austria
  177. const { countries } = await adminClient.query<GetCountryList.Query>(GET_COUNTRY_LIST, {});
  178. const AT = countries.items.find(c => c.code === 'AT')!;
  179. await adminClient.query<UpdateCountry.Mutation, UpdateCountry.Variables>(UPDATE_COUNTRY, {
  180. input: {
  181. id: AT.id,
  182. enabled: false,
  183. },
  184. });
  185. const result = await shopClient.query<GetAvailableCountries.Query>(GET_AVAILABLE_COUNTRIES);
  186. expect(result.availableCountries.length).toBe(countries.items.length - 1);
  187. expect(result.availableCountries.find(c => c.id === AT.id)).toBeUndefined();
  188. });
  189. describe('ordering as anonymous user', () => {
  190. let firstOrderLineId: string;
  191. let createdCustomerId: string;
  192. let orderCode: string;
  193. it('addItemToOrder starts with no session token', () => {
  194. expect(shopClient.getAuthToken()).toBeFalsy();
  195. });
  196. it('activeOrder returns null before any items have been added', async () => {
  197. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  198. expect(result.activeOrder).toBeNull();
  199. });
  200. it('activeOrder creates an anonymous session', () => {
  201. expect(shopClient.getAuthToken()).not.toBe('');
  202. });
  203. it('addItemToOrder creates a new Order with an item', async () => {
  204. const { addItemToOrder } = await shopClient.query<
  205. AddItemToOrder.Mutation,
  206. AddItemToOrder.Variables
  207. >(ADD_ITEM_TO_ORDER, {
  208. productVariantId: 'T_1',
  209. quantity: 1,
  210. });
  211. orderResultGuard.assertSuccess(addItemToOrder);
  212. expect(addItemToOrder!.lines.length).toBe(1);
  213. expect(addItemToOrder!.lines[0].quantity).toBe(1);
  214. expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_1');
  215. expect(addItemToOrder!.lines[0].id).toBe('T_1');
  216. firstOrderLineId = addItemToOrder!.lines[0].id;
  217. orderCode = addItemToOrder!.code;
  218. });
  219. it(
  220. 'addItemToOrder errors with an invalid productVariantId',
  221. assertThrowsWithMessage(
  222. () =>
  223. shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  224. productVariantId: 'T_999',
  225. quantity: 1,
  226. }),
  227. `No ProductVariant with the id '999' could be found`,
  228. ),
  229. );
  230. it('addItemToOrder errors with a negative quantity', async () => {
  231. const { addItemToOrder } = await shopClient.query<
  232. AddItemToOrder.Mutation,
  233. AddItemToOrder.Variables
  234. >(ADD_ITEM_TO_ORDER, {
  235. productVariantId: 'T_999',
  236. quantity: -3,
  237. });
  238. orderResultGuard.assertErrorResult(addItemToOrder);
  239. expect(addItemToOrder.message).toEqual(`The quantity for an OrderItem cannot be negative`);
  240. expect(addItemToOrder.errorCode).toEqual(ErrorCode.NEGATIVE_QUANTITY_ERROR);
  241. });
  242. it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
  243. const { addItemToOrder } = await shopClient.query<
  244. AddItemToOrder.Mutation,
  245. AddItemToOrder.Variables
  246. >(ADD_ITEM_TO_ORDER, {
  247. productVariantId: 'T_1',
  248. quantity: 2,
  249. });
  250. orderResultGuard.assertSuccess(addItemToOrder);
  251. expect(addItemToOrder!.lines.length).toBe(1);
  252. expect(addItemToOrder!.lines[0].quantity).toBe(3);
  253. });
  254. describe('OrderLine customFields', () => {
  255. const GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS = gql`
  256. query {
  257. activeOrder {
  258. lines {
  259. id
  260. customFields {
  261. notes
  262. lineImage {
  263. id
  264. }
  265. lineImages {
  266. id
  267. }
  268. }
  269. }
  270. }
  271. }
  272. `;
  273. it('addItemToOrder with private customFields errors', async () => {
  274. try {
  275. await shopClient.query<AddItemToOrder.Mutation>(ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS, {
  276. productVariantId: 'T_2',
  277. quantity: 1,
  278. customFields: {
  279. privateField: 'oh no!',
  280. },
  281. });
  282. fail('Should have thrown');
  283. } catch (e) {
  284. expect(e.response.errors[0].extensions.code).toBe('BAD_USER_INPUT');
  285. }
  286. });
  287. it('addItemToOrder with equal customFields adds quantity to the existing OrderLine', async () => {
  288. const { addItemToOrder: add1 } = await shopClient.query<AddItemToOrder.Mutation>(
  289. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  290. {
  291. productVariantId: 'T_2',
  292. quantity: 1,
  293. customFields: {
  294. notes: 'note1',
  295. },
  296. },
  297. );
  298. orderResultGuard.assertSuccess(add1);
  299. expect(add1!.lines.length).toBe(2);
  300. expect(add1!.lines[1].quantity).toBe(1);
  301. const { addItemToOrder: add2 } = await shopClient.query<AddItemToOrder.Mutation>(
  302. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  303. {
  304. productVariantId: 'T_2',
  305. quantity: 1,
  306. customFields: {
  307. notes: 'note1',
  308. },
  309. },
  310. );
  311. orderResultGuard.assertSuccess(add2);
  312. expect(add2!.lines.length).toBe(2);
  313. expect(add2!.lines[1].quantity).toBe(2);
  314. await shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  315. REMOVE_ITEM_FROM_ORDER,
  316. {
  317. orderLineId: add2!.lines[1].id,
  318. },
  319. );
  320. });
  321. it('addItemToOrder with different customFields adds quantity to a new OrderLine', async () => {
  322. const { addItemToOrder: add1 } = await shopClient.query<AddItemToOrder.Mutation>(
  323. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  324. {
  325. productVariantId: 'T_3',
  326. quantity: 1,
  327. customFields: {
  328. notes: 'note2',
  329. },
  330. },
  331. );
  332. orderResultGuard.assertSuccess(add1);
  333. expect(add1!.lines.length).toBe(2);
  334. expect(add1!.lines[1].quantity).toBe(1);
  335. const { addItemToOrder: add2 } = await shopClient.query<AddItemToOrder.Mutation>(
  336. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  337. {
  338. productVariantId: 'T_3',
  339. quantity: 1,
  340. customFields: {
  341. notes: 'note3',
  342. },
  343. },
  344. );
  345. orderResultGuard.assertSuccess(add2);
  346. expect(add2!.lines.length).toBe(3);
  347. expect(add2!.lines[1].quantity).toBe(1);
  348. expect(add2!.lines[2].quantity).toBe(1);
  349. await shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  350. REMOVE_ITEM_FROM_ORDER,
  351. {
  352. orderLineId: add2!.lines[1].id,
  353. },
  354. );
  355. await shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  356. REMOVE_ITEM_FROM_ORDER,
  357. {
  358. orderLineId: add2!.lines[2].id,
  359. },
  360. );
  361. });
  362. // https://github.com/vendure-ecommerce/vendure/issues/1670
  363. it('adding a second item after adjusting custom field adds new OrderLine', async () => {
  364. const { addItemToOrder: add1 } = await shopClient.query<AddItemToOrder.Mutation>(
  365. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  366. {
  367. productVariantId: 'T_3',
  368. quantity: 1,
  369. },
  370. );
  371. orderResultGuard.assertSuccess(add1);
  372. expect(add1!.lines.length).toBe(2);
  373. expect(add1!.lines[1].quantity).toBe(1);
  374. const { adjustOrderLine } = await shopClient.query(ADJUST_ORDER_LINE_WITH_CUSTOM_FIELDS, {
  375. orderLineId: add1.lines[1].id,
  376. quantity: 1,
  377. customFields: {
  378. notes: 'updated notes',
  379. },
  380. });
  381. expect(adjustOrderLine.lines[1].customFields).toEqual({
  382. lineImage: null,
  383. lineImages: [],
  384. notes: 'updated notes',
  385. });
  386. const { activeOrder: ao1 } = await shopClient.query(GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS);
  387. expect(ao1.lines[1].customFields).toEqual({
  388. lineImage: null,
  389. lineImages: [],
  390. notes: 'updated notes',
  391. });
  392. const updatedNotesLineId = ao1.lines[1].id;
  393. const { addItemToOrder: add2 } = await shopClient.query<AddItemToOrder.Mutation>(
  394. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  395. {
  396. productVariantId: 'T_3',
  397. quantity: 1,
  398. },
  399. );
  400. orderResultGuard.assertSuccess(add2);
  401. expect(add2!.lines.length).toBe(3);
  402. expect(add2!.lines[1].quantity).toBe(1);
  403. expect(add2!.lines[2].quantity).toBe(1);
  404. const { activeOrder } = await shopClient.query(GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS);
  405. expect(activeOrder.lines.find((l: any) => l.id === updatedNotesLineId)?.customFields).toEqual(
  406. {
  407. lineImage: null,
  408. lineImages: [],
  409. notes: 'updated notes',
  410. },
  411. );
  412. // clean up
  413. await shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  414. REMOVE_ITEM_FROM_ORDER,
  415. {
  416. orderLineId: add2!.lines[1].id,
  417. },
  418. );
  419. const { removeOrderLine } = await shopClient.query<
  420. RemoveItemFromOrder.Mutation,
  421. RemoveItemFromOrder.Variables
  422. >(REMOVE_ITEM_FROM_ORDER, {
  423. orderLineId: add2!.lines[2].id,
  424. });
  425. orderResultGuard.assertSuccess(removeOrderLine);
  426. expect(removeOrderLine.lines.length).toBe(1);
  427. });
  428. it('addItemToOrder with relation customField', async () => {
  429. const { addItemToOrder } = await shopClient.query<AddItemToOrder.Mutation>(
  430. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  431. {
  432. productVariantId: 'T_3',
  433. quantity: 1,
  434. customFields: {
  435. lineImageId: 'T_1',
  436. },
  437. },
  438. );
  439. orderResultGuard.assertSuccess(addItemToOrder);
  440. expect(addItemToOrder!.lines.length).toBe(2);
  441. expect(addItemToOrder!.lines[1].quantity).toBe(1);
  442. const { activeOrder } = await shopClient.query(GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS);
  443. expect(activeOrder.lines[1].customFields.lineImage).toEqual({ id: 'T_1' });
  444. });
  445. it('addItemToOrder with equal relation customField adds to quantity', async () => {
  446. const { addItemToOrder } = await shopClient.query<AddItemToOrder.Mutation>(
  447. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  448. {
  449. productVariantId: 'T_3',
  450. quantity: 1,
  451. customFields: {
  452. lineImageId: 'T_1',
  453. },
  454. },
  455. );
  456. orderResultGuard.assertSuccess(addItemToOrder);
  457. expect(addItemToOrder!.lines.length).toBe(2);
  458. expect(addItemToOrder!.lines[1].quantity).toBe(2);
  459. const { activeOrder } = await shopClient.query(GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS);
  460. expect(activeOrder.lines[1].customFields.lineImage).toEqual({ id: 'T_1' });
  461. });
  462. it('addItemToOrder with different relation customField adds new line', async () => {
  463. const { addItemToOrder } = await shopClient.query<AddItemToOrder.Mutation>(
  464. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  465. {
  466. productVariantId: 'T_3',
  467. quantity: 1,
  468. customFields: {
  469. lineImageId: 'T_2',
  470. },
  471. },
  472. );
  473. orderResultGuard.assertSuccess(addItemToOrder);
  474. expect(addItemToOrder!.lines.length).toBe(3);
  475. expect(addItemToOrder!.lines[2].quantity).toBe(1);
  476. const { activeOrder } = await shopClient.query(GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS);
  477. expect(activeOrder.lines[2].customFields.lineImage).toEqual({ id: 'T_2' });
  478. });
  479. it('adjustOrderLine updates relation reference', async () => {
  480. const { activeOrder } = await shopClient.query(GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS);
  481. const { adjustOrderLine } = await shopClient.query(ADJUST_ORDER_LINE_WITH_CUSTOM_FIELDS, {
  482. orderLineId: activeOrder.lines[2].id,
  483. quantity: 1,
  484. customFields: {
  485. lineImageId: 'T_1',
  486. },
  487. });
  488. expect(adjustOrderLine.lines[2].customFields.lineImage).toEqual({ id: 'T_1' });
  489. await shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  490. REMOVE_ITEM_FROM_ORDER,
  491. {
  492. orderLineId: activeOrder!.lines[2].id,
  493. },
  494. );
  495. const { removeOrderLine } = await shopClient.query<
  496. RemoveItemFromOrder.Mutation,
  497. RemoveItemFromOrder.Variables
  498. >(REMOVE_ITEM_FROM_ORDER, {
  499. orderLineId: activeOrder!.lines[1].id,
  500. });
  501. orderResultGuard.assertSuccess(removeOrderLine);
  502. expect(removeOrderLine.lines.length).toBe(1);
  503. });
  504. it('addItemToOrder with list relation customField', async () => {
  505. const { addItemToOrder } = await shopClient.query<AddItemToOrder.Mutation>(
  506. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  507. {
  508. productVariantId: 'T_3',
  509. quantity: 1,
  510. customFields: {
  511. lineImagesIds: ['T_1', 'T_2'],
  512. },
  513. },
  514. );
  515. orderResultGuard.assertSuccess(addItemToOrder);
  516. expect(addItemToOrder!.lines.length).toBe(2);
  517. expect(addItemToOrder!.lines[1].quantity).toBe(1);
  518. const { activeOrder } = await shopClient.query(GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS);
  519. expect(activeOrder.lines[1].customFields.lineImages.length).toBe(2);
  520. expect(activeOrder.lines[1].customFields.lineImages).toContainEqual({ id: 'T_1' });
  521. expect(activeOrder.lines[1].customFields.lineImages).toContainEqual({ id: 'T_2' });
  522. });
  523. it('addItemToOrder with equal list relation customField adds to quantity', async () => {
  524. const { addItemToOrder } = await shopClient.query<AddItemToOrder.Mutation>(
  525. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  526. {
  527. productVariantId: 'T_3',
  528. quantity: 1,
  529. customFields: {
  530. lineImagesIds: ['T_1', 'T_2'],
  531. },
  532. },
  533. );
  534. orderResultGuard.assertSuccess(addItemToOrder);
  535. expect(addItemToOrder!.lines.length).toBe(2);
  536. expect(addItemToOrder!.lines[1].quantity).toBe(2);
  537. const { activeOrder } = await shopClient.query(GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS);
  538. expect(activeOrder.lines[1].customFields.lineImages.length).toBe(2);
  539. expect(activeOrder.lines[1].customFields.lineImages).toContainEqual({ id: 'T_1' });
  540. expect(activeOrder.lines[1].customFields.lineImages).toContainEqual({ id: 'T_2' });
  541. });
  542. it('addItemToOrder with different list relation customField adds new line', async () => {
  543. const { addItemToOrder } = await shopClient.query<AddItemToOrder.Mutation>(
  544. ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS,
  545. {
  546. productVariantId: 'T_3',
  547. quantity: 1,
  548. customFields: {
  549. lineImagesIds: ['T_1'],
  550. },
  551. },
  552. );
  553. orderResultGuard.assertSuccess(addItemToOrder);
  554. expect(addItemToOrder!.lines.length).toBe(3);
  555. expect(addItemToOrder!.lines[2].quantity).toBe(1);
  556. const { activeOrder } = await shopClient.query(GET_ORDER_WITH_ORDER_LINE_CUSTOM_FIELDS);
  557. expect(activeOrder.lines[2].customFields.lineImages).toEqual([{ id: 'T_1' }]);
  558. await shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  559. REMOVE_ITEM_FROM_ORDER,
  560. {
  561. orderLineId: activeOrder!.lines[2].id,
  562. },
  563. );
  564. const { removeOrderLine } = await shopClient.query<
  565. RemoveItemFromOrder.Mutation,
  566. RemoveItemFromOrder.Variables
  567. >(REMOVE_ITEM_FROM_ORDER, {
  568. orderLineId: activeOrder!.lines[1].id,
  569. });
  570. orderResultGuard.assertSuccess(removeOrderLine);
  571. expect(removeOrderLine.lines.length).toBe(1);
  572. });
  573. });
  574. it('addItemToOrder errors when going beyond orderItemsLimit', async () => {
  575. const { addItemToOrder } = await shopClient.query<
  576. AddItemToOrder.Mutation,
  577. AddItemToOrder.Variables
  578. >(ADD_ITEM_TO_ORDER, {
  579. productVariantId: 'T_1',
  580. quantity: 200,
  581. });
  582. orderResultGuard.assertErrorResult(addItemToOrder);
  583. expect(addItemToOrder.message).toBe(
  584. 'Cannot add items. An order may consist of a maximum of 199 items',
  585. );
  586. expect(addItemToOrder.errorCode).toBe(ErrorCode.ORDER_LIMIT_ERROR);
  587. });
  588. it('adjustOrderLine adjusts the quantity', async () => {
  589. const { adjustOrderLine } = await shopClient.query<
  590. AdjustItemQuantity.Mutation,
  591. AdjustItemQuantity.Variables
  592. >(ADJUST_ITEM_QUANTITY, {
  593. orderLineId: firstOrderLineId,
  594. quantity: 50,
  595. });
  596. orderResultGuard.assertSuccess(adjustOrderLine);
  597. expect(adjustOrderLine!.lines.length).toBe(1);
  598. expect(adjustOrderLine!.lines[0].quantity).toBe(50);
  599. });
  600. it('adjustOrderLine with quantity 0 removes the line', async () => {
  601. const { addItemToOrder } = await shopClient.query<
  602. AddItemToOrder.Mutation,
  603. AddItemToOrder.Variables
  604. >(ADD_ITEM_TO_ORDER, {
  605. productVariantId: 'T_3',
  606. quantity: 3,
  607. });
  608. orderResultGuard.assertSuccess(addItemToOrder);
  609. expect(addItemToOrder!.lines.length).toBe(2);
  610. expect(addItemToOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  611. const { adjustOrderLine } = await shopClient.query<
  612. AdjustItemQuantity.Mutation,
  613. AdjustItemQuantity.Variables
  614. >(ADJUST_ITEM_QUANTITY, {
  615. orderLineId: addItemToOrder?.lines[1].id!,
  616. quantity: 0,
  617. });
  618. orderResultGuard.assertSuccess(adjustOrderLine);
  619. expect(adjustOrderLine!.lines.length).toBe(1);
  620. expect(adjustOrderLine!.lines.map(i => i.productVariant.id)).toEqual(['T_1']);
  621. });
  622. it('adjustOrderLine with quantity > stockOnHand only allows user to have stock on hand', async () => {
  623. const { addItemToOrder } = await shopClient.query<
  624. AddItemToOrder.Mutation,
  625. AddItemToOrder.Variables
  626. >(ADD_ITEM_TO_ORDER, {
  627. productVariantId: 'T_3',
  628. quantity: 111,
  629. });
  630. orderResultGuard.assertErrorResult(addItemToOrder);
  631. // Insufficient stock error should return because there are only 100 available
  632. expect(addItemToOrder.errorCode).toBe('INSUFFICIENT_STOCK_ERROR');
  633. // But it should still add the item to the order
  634. expect(addItemToOrder!.order.lines[1].quantity).toBe(100);
  635. const { adjustOrderLine } = await shopClient.query<
  636. AdjustItemQuantity.Mutation,
  637. AdjustItemQuantity.Variables
  638. >(ADJUST_ITEM_QUANTITY, {
  639. orderLineId: addItemToOrder!.order.lines[1].id,
  640. quantity: 101,
  641. });
  642. orderResultGuard.assertErrorResult(adjustOrderLine);
  643. expect(adjustOrderLine.errorCode).toBe('INSUFFICIENT_STOCK_ERROR');
  644. expect(adjustOrderLine.message).toBe(
  645. 'Only 100 items were added to the order due to insufficient stock',
  646. );
  647. const order = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  648. expect(order.activeOrder?.lines[1].quantity).toBe(100);
  649. const { adjustOrderLine: adjustLine2 } = await shopClient.query<
  650. AdjustItemQuantity.Mutation,
  651. AdjustItemQuantity.Variables
  652. >(ADJUST_ITEM_QUANTITY, {
  653. orderLineId: addItemToOrder!.order.lines[1].id,
  654. quantity: 0,
  655. });
  656. orderResultGuard.assertSuccess(adjustLine2);
  657. expect(adjustLine2!.lines.length).toBe(1);
  658. expect(adjustLine2!.lines.map(i => i.productVariant.id)).toEqual(['T_1']);
  659. });
  660. it('adjustOrderLine errors when going beyond orderItemsLimit', async () => {
  661. const { adjustOrderLine } = await shopClient.query<
  662. AdjustItemQuantity.Mutation,
  663. AdjustItemQuantity.Variables
  664. >(ADJUST_ITEM_QUANTITY, {
  665. orderLineId: firstOrderLineId,
  666. quantity: 200,
  667. });
  668. orderResultGuard.assertErrorResult(adjustOrderLine);
  669. expect(adjustOrderLine.message).toBe(
  670. 'Cannot add items. An order may consist of a maximum of 199 items',
  671. );
  672. expect(adjustOrderLine.errorCode).toBe(ErrorCode.ORDER_LIMIT_ERROR);
  673. });
  674. it('adjustOrderLine errors with a negative quantity', async () => {
  675. const { adjustOrderLine } = await shopClient.query<
  676. AdjustItemQuantity.Mutation,
  677. AdjustItemQuantity.Variables
  678. >(ADJUST_ITEM_QUANTITY, {
  679. orderLineId: firstOrderLineId,
  680. quantity: -3,
  681. });
  682. orderResultGuard.assertErrorResult(adjustOrderLine);
  683. expect(adjustOrderLine.message).toBe('The quantity for an OrderItem cannot be negative');
  684. expect(adjustOrderLine.errorCode).toBe(ErrorCode.NEGATIVE_QUANTITY_ERROR);
  685. });
  686. it(
  687. 'adjustOrderLine errors with an invalid orderLineId',
  688. assertThrowsWithMessage(
  689. () =>
  690. shopClient.query<AdjustItemQuantity.Mutation, AdjustItemQuantity.Variables>(
  691. ADJUST_ITEM_QUANTITY,
  692. {
  693. orderLineId: 'T_999',
  694. quantity: 5,
  695. },
  696. ),
  697. `This order does not contain an OrderLine with the id 999`,
  698. ),
  699. );
  700. it('removeItemFromOrder removes the correct item', async () => {
  701. const { addItemToOrder } = await shopClient.query<
  702. AddItemToOrder.Mutation,
  703. AddItemToOrder.Variables
  704. >(ADD_ITEM_TO_ORDER, {
  705. productVariantId: 'T_3',
  706. quantity: 3,
  707. });
  708. orderResultGuard.assertSuccess(addItemToOrder);
  709. expect(addItemToOrder!.lines.length).toBe(2);
  710. expect(addItemToOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  711. const { removeOrderLine } = await shopClient.query<
  712. RemoveItemFromOrder.Mutation,
  713. RemoveItemFromOrder.Variables
  714. >(REMOVE_ITEM_FROM_ORDER, {
  715. orderLineId: firstOrderLineId,
  716. });
  717. orderResultGuard.assertSuccess(removeOrderLine);
  718. expect(removeOrderLine!.lines.length).toBe(1);
  719. expect(removeOrderLine!.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
  720. });
  721. it(
  722. 'removeItemFromOrder errors with an invalid orderItemId',
  723. assertThrowsWithMessage(
  724. () =>
  725. shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
  726. REMOVE_ITEM_FROM_ORDER,
  727. {
  728. orderLineId: 'T_999',
  729. },
  730. ),
  731. `This order does not contain an OrderLine with the id 999`,
  732. ),
  733. );
  734. it('nextOrderStates returns next valid states', async () => {
  735. const result = await shopClient.query<GetNextOrderStates.Query>(GET_NEXT_STATES);
  736. expect(result.nextOrderStates).toEqual(['ArrangingPayment', 'Cancelled']);
  737. });
  738. it('transitionOrderToState returns error result for invalid state', async () => {
  739. const { transitionOrderToState } = await shopClient.query<
  740. TransitionToState.Mutation,
  741. TransitionToState.Variables
  742. >(TRANSITION_TO_STATE, { state: 'Completed' });
  743. orderResultGuard.assertErrorResult(transitionOrderToState);
  744. expect(transitionOrderToState!.message).toBe(
  745. `Cannot transition Order from "AddingItems" to "Completed"`,
  746. );
  747. expect(transitionOrderToState!.errorCode).toBe(ErrorCode.ORDER_STATE_TRANSITION_ERROR);
  748. });
  749. it('attempting to transition to ArrangingPayment returns error result when Order has no Customer', async () => {
  750. const { transitionOrderToState } = await shopClient.query<
  751. TransitionToState.Mutation,
  752. TransitionToState.Variables
  753. >(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
  754. orderResultGuard.assertErrorResult(transitionOrderToState);
  755. expect(transitionOrderToState!.transitionError).toBe(
  756. `Cannot transition Order to the "ArrangingPayment" state without Customer details`,
  757. );
  758. expect(transitionOrderToState!.errorCode).toBe(ErrorCode.ORDER_STATE_TRANSITION_ERROR);
  759. });
  760. it('setCustomerForOrder returns error result on email address conflict', async () => {
  761. const { customers } = await adminClient.query<GetCustomerList.Query>(GET_CUSTOMER_LIST);
  762. const { setCustomerForOrder } = await shopClient.query<
  763. SetCustomerForOrder.Mutation,
  764. SetCustomerForOrder.Variables
  765. >(SET_CUSTOMER, {
  766. input: {
  767. emailAddress: customers.items[0].emailAddress,
  768. firstName: 'Test',
  769. lastName: 'Person',
  770. },
  771. });
  772. orderResultGuard.assertErrorResult(setCustomerForOrder);
  773. expect(setCustomerForOrder!.message).toBe('The email address is not available.');
  774. expect(setCustomerForOrder!.errorCode).toBe(ErrorCode.EMAIL_ADDRESS_CONFLICT_ERROR);
  775. });
  776. it('setCustomerForOrder creates a new Customer and associates it with the Order', async () => {
  777. const { setCustomerForOrder } = await shopClient.query<
  778. SetCustomerForOrder.Mutation,
  779. SetCustomerForOrder.Variables
  780. >(SET_CUSTOMER, {
  781. input: {
  782. emailAddress: 'test@test.com',
  783. firstName: 'Test',
  784. lastName: 'Person',
  785. },
  786. });
  787. orderResultGuard.assertSuccess(setCustomerForOrder);
  788. const customer = setCustomerForOrder!.customer!;
  789. expect(customer.firstName).toBe('Test');
  790. expect(customer.lastName).toBe('Person');
  791. expect(customer.emailAddress).toBe('test@test.com');
  792. createdCustomerId = customer.id;
  793. });
  794. it('setCustomerForOrder updates the existing customer if Customer already set', async () => {
  795. const { setCustomerForOrder } = await shopClient.query<
  796. SetCustomerForOrder.Mutation,
  797. SetCustomerForOrder.Variables
  798. >(SET_CUSTOMER, {
  799. input: {
  800. emailAddress: 'test@test.com',
  801. firstName: 'Changed',
  802. lastName: 'Person',
  803. },
  804. });
  805. orderResultGuard.assertSuccess(setCustomerForOrder);
  806. const customer = setCustomerForOrder!.customer!;
  807. expect(customer.firstName).toBe('Changed');
  808. expect(customer.lastName).toBe('Person');
  809. expect(customer.emailAddress).toBe('test@test.com');
  810. expect(customer.id).toBe(createdCustomerId);
  811. });
  812. it('setOrderShippingAddress sets shipping address', async () => {
  813. const address: CreateAddressInput = {
  814. fullName: 'name',
  815. company: 'company',
  816. streetLine1: '12 Shipping Street',
  817. streetLine2: null,
  818. city: 'foo',
  819. province: 'bar',
  820. postalCode: '123456',
  821. countryCode: 'US',
  822. phoneNumber: '4444444',
  823. };
  824. const { setOrderShippingAddress } = await shopClient.query<
  825. SetShippingAddress.Mutation,
  826. SetShippingAddress.Variables
  827. >(SET_SHIPPING_ADDRESS, {
  828. input: address,
  829. });
  830. expect(setOrderShippingAddress!.shippingAddress).toEqual({
  831. fullName: 'name',
  832. company: 'company',
  833. streetLine1: '12 Shipping Street',
  834. streetLine2: null,
  835. city: 'foo',
  836. province: 'bar',
  837. postalCode: '123456',
  838. country: 'United States of America',
  839. phoneNumber: '4444444',
  840. });
  841. });
  842. it('setOrderBillingAddress sets billing address', async () => {
  843. const address: CreateAddressInput = {
  844. fullName: 'name',
  845. company: 'company',
  846. streetLine1: '22 Billing Avenue',
  847. streetLine2: null,
  848. city: 'foo',
  849. province: 'bar',
  850. postalCode: '123456',
  851. countryCode: 'US',
  852. phoneNumber: '4444444',
  853. };
  854. const { setOrderBillingAddress } = await shopClient.query<
  855. SetBillingAddress.Mutation,
  856. SetBillingAddress.Variables
  857. >(SET_BILLING_ADDRESS, {
  858. input: address,
  859. });
  860. expect(setOrderBillingAddress!.billingAddress).toEqual({
  861. fullName: 'name',
  862. company: 'company',
  863. streetLine1: '22 Billing Avenue',
  864. streetLine2: null,
  865. city: 'foo',
  866. province: 'bar',
  867. postalCode: '123456',
  868. country: 'United States of America',
  869. phoneNumber: '4444444',
  870. });
  871. });
  872. it('customer default Addresses are not updated before payment', async () => {
  873. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  874. const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
  875. GET_CUSTOMER,
  876. { id: activeOrder!.customer!.id },
  877. );
  878. expect(customer!.addresses).toEqual([]);
  879. });
  880. it('attempting to transition to ArrangingPayment returns error result when Order has no ShippingMethod', async () => {
  881. const { transitionOrderToState } = await shopClient.query<
  882. TransitionToState.Mutation,
  883. TransitionToState.Variables
  884. >(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
  885. orderResultGuard.assertErrorResult(transitionOrderToState);
  886. expect(transitionOrderToState!.transitionError).toBe(
  887. `Cannot transition Order to the "ArrangingPayment" state without a ShippingMethod`,
  888. );
  889. expect(transitionOrderToState!.errorCode).toBe(ErrorCode.ORDER_STATE_TRANSITION_ERROR);
  890. });
  891. it('can transition to ArrangingPayment once Customer and ShippingMethod has been set', async () => {
  892. const { eligibleShippingMethods } = await shopClient.query<GetShippingMethods.Query>(
  893. GET_ELIGIBLE_SHIPPING_METHODS,
  894. );
  895. const { setOrderShippingMethod } = await shopClient.query<
  896. SetShippingMethod.Mutation,
  897. SetShippingMethod.Variables
  898. >(SET_SHIPPING_METHOD, {
  899. id: eligibleShippingMethods[0].id,
  900. });
  901. orderResultGuard.assertSuccess(setOrderShippingMethod);
  902. const { transitionOrderToState } = await shopClient.query<
  903. TransitionToState.Mutation,
  904. TransitionToState.Variables
  905. >(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
  906. orderResultGuard.assertSuccess(transitionOrderToState);
  907. expect(pick(transitionOrderToState, ['id', 'state'])).toEqual({
  908. id: 'T_1',
  909. state: 'ArrangingPayment',
  910. });
  911. });
  912. it('adds a successful payment and transitions Order state', async () => {
  913. const { addPaymentToOrder } = await shopClient.query<
  914. AddPaymentToOrder.Mutation,
  915. AddPaymentToOrder.Variables
  916. >(ADD_PAYMENT, {
  917. input: {
  918. method: testSuccessfulPaymentMethod.code,
  919. metadata: {},
  920. },
  921. });
  922. orderResultGuard.assertSuccess(addPaymentToOrder);
  923. const payment = addPaymentToOrder!.payments![0];
  924. expect(addPaymentToOrder!.state).toBe('PaymentSettled');
  925. expect(addPaymentToOrder!.active).toBe(false);
  926. expect(addPaymentToOrder!.payments!.length).toBe(1);
  927. expect(payment.method).toBe(testSuccessfulPaymentMethod.code);
  928. expect(payment.state).toBe('Settled');
  929. });
  930. it('activeOrder is null after payment', async () => {
  931. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  932. expect(result.activeOrder).toBeNull();
  933. });
  934. it('customer default Addresses are updated after payment', async () => {
  935. const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
  936. id: createdCustomerId,
  937. });
  938. // tslint:disable-next-line:no-non-null-assertion
  939. const shippingAddress = result.customer!.addresses!.find(a => a.defaultShippingAddress)!;
  940. expect(shippingAddress.streetLine1).toBe('12 Shipping Street');
  941. expect(shippingAddress.postalCode).toBe('123456');
  942. expect(shippingAddress.defaultBillingAddress).toBe(false);
  943. expect(shippingAddress.defaultShippingAddress).toBe(true);
  944. // tslint:disable-next-line:no-non-null-assertion
  945. const billingAddress = result.customer!.addresses!.find(a => a.defaultBillingAddress)!;
  946. expect(billingAddress.streetLine1).toBe('22 Billing Avenue');
  947. expect(billingAddress.postalCode).toBe('123456');
  948. expect(billingAddress.defaultBillingAddress).toBe(true);
  949. expect(billingAddress.defaultShippingAddress).toBe(false);
  950. });
  951. });
  952. describe('ordering as authenticated user', () => {
  953. let firstOrderLineId: string;
  954. let activeOrder: UpdatedOrderFragment;
  955. let authenticatedUserEmailAddress: string;
  956. let customers: GetCustomerList.Items[];
  957. const password = 'test';
  958. beforeAll(async () => {
  959. await adminClient.asSuperAdmin();
  960. const result = await adminClient.query<GetCustomerList.Query, GetCustomerList.Variables>(
  961. GET_CUSTOMER_LIST,
  962. {
  963. options: {
  964. take: 2,
  965. },
  966. },
  967. );
  968. customers = result.customers.items;
  969. authenticatedUserEmailAddress = customers[0].emailAddress;
  970. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  971. });
  972. it('activeOrder returns null before any items have been added', async () => {
  973. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  974. expect(result.activeOrder).toBeNull();
  975. });
  976. it('addItemToOrder creates a new Order with an item', async () => {
  977. const { addItemToOrder } = await shopClient.query<
  978. AddItemToOrder.Mutation,
  979. AddItemToOrder.Variables
  980. >(ADD_ITEM_TO_ORDER, {
  981. productVariantId: 'T_1',
  982. quantity: 1,
  983. });
  984. orderResultGuard.assertSuccess(addItemToOrder);
  985. expect(addItemToOrder!.lines.length).toBe(1);
  986. expect(addItemToOrder!.lines[0].quantity).toBe(1);
  987. expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_1');
  988. activeOrder = addItemToOrder!;
  989. firstOrderLineId = addItemToOrder!.lines[0].id;
  990. });
  991. it('activeOrder returns order after item has been added', async () => {
  992. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  993. expect(result.activeOrder!.id).toBe(activeOrder.id);
  994. expect(result.activeOrder!.state).toBe('AddingItems');
  995. });
  996. it('activeOrder resolves customer user', async () => {
  997. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  998. expect(result.activeOrder!.customer!.user).toEqual({
  999. id: 'T_2',
  1000. identifier: 'hayden.zieme12@hotmail.com',
  1001. });
  1002. });
  1003. it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
  1004. const { addItemToOrder } = await shopClient.query<
  1005. AddItemToOrder.Mutation,
  1006. AddItemToOrder.Variables
  1007. >(ADD_ITEM_TO_ORDER, {
  1008. productVariantId: 'T_1',
  1009. quantity: 2,
  1010. });
  1011. orderResultGuard.assertSuccess(addItemToOrder);
  1012. expect(addItemToOrder!.lines.length).toBe(1);
  1013. expect(addItemToOrder!.lines[0].quantity).toBe(3);
  1014. });
  1015. it('adjustOrderLine adjusts the quantity', async () => {
  1016. const { adjustOrderLine } = await shopClient.query<
  1017. AdjustItemQuantity.Mutation,
  1018. AdjustItemQuantity.Variables
  1019. >(ADJUST_ITEM_QUANTITY, {
  1020. orderLineId: firstOrderLineId,
  1021. quantity: 50,
  1022. });
  1023. orderResultGuard.assertSuccess(adjustOrderLine);
  1024. expect(adjustOrderLine!.lines.length).toBe(1);
  1025. expect(adjustOrderLine!.lines[0].quantity).toBe(50);
  1026. });
  1027. it('removeItemFromOrder removes the correct item', async () => {
  1028. const { addItemToOrder } = await shopClient.query<
  1029. AddItemToOrder.Mutation,
  1030. AddItemToOrder.Variables
  1031. >(ADD_ITEM_TO_ORDER, {
  1032. productVariantId: 'T_3',
  1033. quantity: 3,
  1034. });
  1035. orderResultGuard.assertSuccess(addItemToOrder);
  1036. expect(addItemToOrder!.lines.length).toBe(2);
  1037. expect(addItemToOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  1038. const { removeOrderLine } = await shopClient.query<
  1039. RemoveItemFromOrder.Mutation,
  1040. RemoveItemFromOrder.Variables
  1041. >(REMOVE_ITEM_FROM_ORDER, {
  1042. orderLineId: firstOrderLineId,
  1043. });
  1044. orderResultGuard.assertSuccess(removeOrderLine);
  1045. expect(removeOrderLine!.lines.length).toBe(1);
  1046. expect(removeOrderLine!.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
  1047. });
  1048. it('nextOrderStates returns next valid states', async () => {
  1049. const result = await shopClient.query<GetNextOrderStates.Query>(GET_NEXT_STATES);
  1050. expect(result.nextOrderStates).toEqual(['ArrangingPayment', 'Cancelled']);
  1051. });
  1052. it('logging out and back in again resumes the last active order', async () => {
  1053. await shopClient.asAnonymousUser();
  1054. const result1 = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1055. expect(result1.activeOrder).toBeNull();
  1056. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  1057. const result2 = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1058. expect(result2.activeOrder!.id).toBe(activeOrder.id);
  1059. });
  1060. it('cannot setCustomerForOrder when already logged in', async () => {
  1061. const { setCustomerForOrder } = await shopClient.query<
  1062. SetCustomerForOrder.Mutation,
  1063. SetCustomerForOrder.Variables
  1064. >(SET_CUSTOMER, {
  1065. input: {
  1066. emailAddress: 'newperson@email.com',
  1067. firstName: 'New',
  1068. lastName: 'Person',
  1069. },
  1070. });
  1071. orderResultGuard.assertErrorResult(setCustomerForOrder);
  1072. expect(setCustomerForOrder!.message).toBe(
  1073. 'Cannot set a Customer for the Order when already logged in',
  1074. );
  1075. expect(setCustomerForOrder!.errorCode).toBe(ErrorCode.ALREADY_LOGGED_IN_ERROR);
  1076. });
  1077. describe('shipping', () => {
  1078. let shippingMethods: GetShippingMethods.EligibleShippingMethods[];
  1079. it(
  1080. 'setOrderShippingAddress throws with invalid countryCode',
  1081. assertThrowsWithMessage(() => {
  1082. const address: CreateAddressInput = {
  1083. streetLine1: '12 the street',
  1084. countryCode: 'INVALID',
  1085. };
  1086. return shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
  1087. SET_SHIPPING_ADDRESS,
  1088. {
  1089. input: address,
  1090. },
  1091. );
  1092. }, `The countryCode "INVALID" was not recognized`),
  1093. );
  1094. it('setOrderShippingAddress sets shipping address', async () => {
  1095. const address: CreateAddressInput = {
  1096. fullName: 'name',
  1097. company: 'company',
  1098. streetLine1: '12 the street',
  1099. streetLine2: null,
  1100. city: 'foo',
  1101. province: 'bar',
  1102. postalCode: '123456',
  1103. countryCode: 'US',
  1104. phoneNumber: '4444444',
  1105. };
  1106. const { setOrderShippingAddress } = await shopClient.query<
  1107. SetShippingAddress.Mutation,
  1108. SetShippingAddress.Variables
  1109. >(SET_SHIPPING_ADDRESS, {
  1110. input: address,
  1111. });
  1112. expect(setOrderShippingAddress!.shippingAddress).toEqual({
  1113. fullName: 'name',
  1114. company: 'company',
  1115. streetLine1: '12 the street',
  1116. streetLine2: null,
  1117. city: 'foo',
  1118. province: 'bar',
  1119. postalCode: '123456',
  1120. country: 'United States of America',
  1121. phoneNumber: '4444444',
  1122. });
  1123. });
  1124. it('eligibleShippingMethods lists shipping methods', async () => {
  1125. const result = await shopClient.query<GetShippingMethods.Query>(
  1126. GET_ELIGIBLE_SHIPPING_METHODS,
  1127. );
  1128. shippingMethods = result.eligibleShippingMethods;
  1129. expect(shippingMethods).toEqual([
  1130. {
  1131. id: 'T_1',
  1132. price: 500,
  1133. code: 'standard-shipping',
  1134. name: 'Standard Shipping',
  1135. description: '',
  1136. },
  1137. {
  1138. id: 'T_2',
  1139. price: 1000,
  1140. code: 'express-shipping',
  1141. name: 'Express Shipping',
  1142. description: '',
  1143. },
  1144. ]);
  1145. });
  1146. it('shipping is initially unset', async () => {
  1147. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1148. expect(result.activeOrder!.shipping).toEqual(0);
  1149. expect(result.activeOrder!.shippingLines).toEqual([]);
  1150. });
  1151. it('setOrderShippingMethod sets the shipping method', async () => {
  1152. const result = await shopClient.query<
  1153. SetShippingMethod.Mutation,
  1154. SetShippingMethod.Variables
  1155. >(SET_SHIPPING_METHOD, {
  1156. id: shippingMethods[1].id,
  1157. });
  1158. const activeOrderResult = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1159. const order = activeOrderResult.activeOrder!;
  1160. expect(order.shipping).toBe(shippingMethods[1].price);
  1161. expect(order.shippingLines[0].shippingMethod!.id).toBe(shippingMethods[1].id);
  1162. expect(order.shippingLines[0].shippingMethod!.description).toBe(
  1163. shippingMethods[1].description,
  1164. );
  1165. });
  1166. it('shipping method is preserved after adjustOrderLine', async () => {
  1167. const activeOrderResult = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1168. activeOrder = activeOrderResult.activeOrder!;
  1169. const { adjustOrderLine } = await shopClient.query<
  1170. AdjustItemQuantity.Mutation,
  1171. AdjustItemQuantity.Variables
  1172. >(ADJUST_ITEM_QUANTITY, {
  1173. orderLineId: activeOrder.lines[0].id,
  1174. quantity: 10,
  1175. });
  1176. orderResultGuard.assertSuccess(adjustOrderLine);
  1177. expect(adjustOrderLine!.shipping).toBe(shippingMethods[1].price);
  1178. expect(adjustOrderLine!.shippingLines[0].shippingMethod!.id).toBe(shippingMethods[1].id);
  1179. expect(adjustOrderLine!.shippingLines[0].shippingMethod!.description).toBe(
  1180. shippingMethods[1].description,
  1181. );
  1182. });
  1183. });
  1184. describe('payment', () => {
  1185. it('attempting add a Payment returns error result when in AddingItems state', async () => {
  1186. const { addPaymentToOrder } = await shopClient.query<
  1187. AddPaymentToOrder.Mutation,
  1188. AddPaymentToOrder.Variables
  1189. >(ADD_PAYMENT, {
  1190. input: {
  1191. method: testSuccessfulPaymentMethod.code,
  1192. metadata: {},
  1193. },
  1194. });
  1195. orderResultGuard.assertErrorResult(addPaymentToOrder);
  1196. expect(addPaymentToOrder!.message).toBe(
  1197. `A Payment may only be added when Order is in "ArrangingPayment" state`,
  1198. );
  1199. expect(addPaymentToOrder!.errorCode).toBe(ErrorCode.ORDER_PAYMENT_STATE_ERROR);
  1200. });
  1201. it('transitions to the ArrangingPayment state', async () => {
  1202. const { transitionOrderToState } = await shopClient.query<
  1203. TransitionToState.Mutation,
  1204. TransitionToState.Variables
  1205. >(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
  1206. orderResultGuard.assertSuccess(transitionOrderToState);
  1207. expect(pick(transitionOrderToState, ['id', 'state'])).toEqual({
  1208. id: activeOrder.id,
  1209. state: 'ArrangingPayment',
  1210. });
  1211. });
  1212. it('attempting to add an item returns error result when in ArrangingPayment state', async () => {
  1213. const { addItemToOrder } = await shopClient.query<
  1214. AddItemToOrder.Mutation,
  1215. AddItemToOrder.Variables
  1216. >(ADD_ITEM_TO_ORDER, {
  1217. productVariantId: 'T_4',
  1218. quantity: 1,
  1219. });
  1220. orderResultGuard.assertErrorResult(addItemToOrder);
  1221. expect(addItemToOrder.message).toBe(
  1222. `Order contents may only be modified when in the "AddingItems" state`,
  1223. );
  1224. expect(addItemToOrder.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  1225. });
  1226. it('attempting to modify item quantity returns error result when in ArrangingPayment state', async () => {
  1227. const { adjustOrderLine } = await shopClient.query<
  1228. AdjustItemQuantity.Mutation,
  1229. AdjustItemQuantity.Variables
  1230. >(ADJUST_ITEM_QUANTITY, {
  1231. orderLineId: activeOrder.lines[0].id,
  1232. quantity: 12,
  1233. });
  1234. orderResultGuard.assertErrorResult(adjustOrderLine);
  1235. expect(adjustOrderLine.message).toBe(
  1236. `Order contents may only be modified when in the "AddingItems" state`,
  1237. );
  1238. expect(adjustOrderLine.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  1239. });
  1240. it('attempting to remove an item returns error result when in ArrangingPayment state', async () => {
  1241. const { removeOrderLine } = await shopClient.query<
  1242. RemoveItemFromOrder.Mutation,
  1243. RemoveItemFromOrder.Variables
  1244. >(REMOVE_ITEM_FROM_ORDER, {
  1245. orderLineId: activeOrder.lines[0].id,
  1246. });
  1247. orderResultGuard.assertErrorResult(removeOrderLine);
  1248. expect(removeOrderLine.message).toBe(
  1249. `Order contents may only be modified when in the "AddingItems" state`,
  1250. );
  1251. expect(removeOrderLine.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  1252. });
  1253. it('attempting to remove all items returns error result when in ArrangingPayment state', async () => {
  1254. const { removeAllOrderLines } = await shopClient.query<RemoveAllOrderLines.Mutation>(
  1255. REMOVE_ALL_ORDER_LINES,
  1256. );
  1257. orderResultGuard.assertErrorResult(removeAllOrderLines);
  1258. expect(removeAllOrderLines.message).toBe(
  1259. `Order contents may only be modified when in the "AddingItems" state`,
  1260. );
  1261. expect(removeAllOrderLines.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  1262. });
  1263. it('attempting to setOrderShippingMethod returns error result when in ArrangingPayment state', async () => {
  1264. const shippingMethodsResult = await shopClient.query<GetShippingMethods.Query>(
  1265. GET_ELIGIBLE_SHIPPING_METHODS,
  1266. );
  1267. const shippingMethods = shippingMethodsResult.eligibleShippingMethods;
  1268. const { setOrderShippingMethod } = await shopClient.query<
  1269. SetShippingMethod.Mutation,
  1270. SetShippingMethod.Variables
  1271. >(SET_SHIPPING_METHOD, {
  1272. id: shippingMethods[0].id,
  1273. });
  1274. orderResultGuard.assertErrorResult(setOrderShippingMethod);
  1275. expect(setOrderShippingMethod.message).toBe(
  1276. `Order contents may only be modified when in the "AddingItems" state`,
  1277. );
  1278. expect(setOrderShippingMethod.errorCode).toBe(ErrorCode.ORDER_MODIFICATION_ERROR);
  1279. });
  1280. it('adds a declined payment', async () => {
  1281. const { addPaymentToOrder } = await shopClient.query<
  1282. AddPaymentToOrder.Mutation,
  1283. AddPaymentToOrder.Variables
  1284. >(ADD_PAYMENT, {
  1285. input: {
  1286. method: testFailingPaymentMethod.code,
  1287. metadata: {
  1288. foo: 'bar',
  1289. },
  1290. },
  1291. });
  1292. orderResultGuard.assertErrorResult(addPaymentToOrder);
  1293. expect(addPaymentToOrder!.message).toBe('The payment was declined');
  1294. expect(addPaymentToOrder!.errorCode).toBe(ErrorCode.PAYMENT_DECLINED_ERROR);
  1295. expect((addPaymentToOrder as any).paymentErrorMessage).toBe('Insufficient funds');
  1296. const { activeOrder: order } = await shopClient.query<GetActiveOrderWithPayments.Query>(
  1297. GET_ACTIVE_ORDER_WITH_PAYMENTS,
  1298. );
  1299. const payment = order!.payments![0];
  1300. expect(order!.state).toBe('ArrangingPayment');
  1301. expect(order!.payments!.length).toBe(1);
  1302. expect(payment.method).toBe(testFailingPaymentMethod.code);
  1303. expect(payment.state).toBe('Declined');
  1304. expect(payment.transactionId).toBe(null);
  1305. expect(payment.metadata).toEqual({
  1306. public: { foo: 'bar' },
  1307. });
  1308. });
  1309. it('adds an error payment and returns error result', async () => {
  1310. const { addPaymentToOrder } = await shopClient.query<
  1311. AddPaymentToOrder.Mutation,
  1312. AddPaymentToOrder.Variables
  1313. >(ADD_PAYMENT, {
  1314. input: {
  1315. method: testErrorPaymentMethod.code,
  1316. metadata: {
  1317. foo: 'bar',
  1318. },
  1319. },
  1320. });
  1321. orderResultGuard.assertErrorResult(addPaymentToOrder);
  1322. expect(addPaymentToOrder!.message).toBe('The payment failed');
  1323. expect(addPaymentToOrder!.errorCode).toBe(ErrorCode.PAYMENT_FAILED_ERROR);
  1324. expect((addPaymentToOrder as any).paymentErrorMessage).toBe('Something went horribly wrong');
  1325. const result = await shopClient.query<GetActiveOrderPayments.Query>(
  1326. GET_ACTIVE_ORDER_PAYMENTS,
  1327. );
  1328. const payment = result.activeOrder!.payments![1];
  1329. expect(result.activeOrder!.payments!.length).toBe(2);
  1330. expect(payment.method).toBe(testErrorPaymentMethod.code);
  1331. expect(payment.state).toBe('Error');
  1332. expect(payment.errorMessage).toBe('Something went horribly wrong');
  1333. });
  1334. it('adds a successful payment and transitions Order state', async () => {
  1335. const { addPaymentToOrder } = await shopClient.query<
  1336. AddPaymentToOrder.Mutation,
  1337. AddPaymentToOrder.Variables
  1338. >(ADD_PAYMENT, {
  1339. input: {
  1340. method: testSuccessfulPaymentMethod.code,
  1341. metadata: {
  1342. baz: 'quux',
  1343. },
  1344. },
  1345. });
  1346. orderResultGuard.assertSuccess(addPaymentToOrder);
  1347. const payment = addPaymentToOrder!.payments!.find(p => p.transactionId === '12345')!;
  1348. expect(addPaymentToOrder!.state).toBe('PaymentSettled');
  1349. expect(addPaymentToOrder!.active).toBe(false);
  1350. expect(addPaymentToOrder!.payments!.length).toBe(3);
  1351. expect(payment.method).toBe(testSuccessfulPaymentMethod.code);
  1352. expect(payment.state).toBe('Settled');
  1353. expect(payment.transactionId).toBe('12345');
  1354. expect(payment.metadata).toEqual({
  1355. public: { baz: 'quux' },
  1356. });
  1357. });
  1358. it('does not create new address when Customer already has address', async () => {
  1359. const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
  1360. GET_CUSTOMER,
  1361. { id: customers[0].id },
  1362. );
  1363. expect(customer!.addresses!.length).toBe(1);
  1364. });
  1365. });
  1366. describe('orderByCode', () => {
  1367. describe('immediately after Order is placed', () => {
  1368. it('works when authenticated', async () => {
  1369. const result = await shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
  1370. GET_ORDER_BY_CODE,
  1371. {
  1372. code: activeOrder.code,
  1373. },
  1374. );
  1375. expect(result.orderByCode!.id).toBe(activeOrder.id);
  1376. });
  1377. it('works when anonymous', async () => {
  1378. await shopClient.asAnonymousUser();
  1379. const result = await shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
  1380. GET_ORDER_BY_CODE,
  1381. {
  1382. code: activeOrder.code,
  1383. },
  1384. );
  1385. expect(result.orderByCode!.id).toBe(activeOrder.id);
  1386. });
  1387. it(
  1388. `throws error for another user's Order`,
  1389. assertThrowsWithMessage(async () => {
  1390. authenticatedUserEmailAddress = customers[1].emailAddress;
  1391. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  1392. return shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
  1393. GET_ORDER_BY_CODE,
  1394. {
  1395. code: activeOrder.code,
  1396. },
  1397. );
  1398. }, `You are not currently authorized to perform this action`),
  1399. );
  1400. });
  1401. describe('3 hours after the Order has been placed', () => {
  1402. let dateNowMock: any;
  1403. beforeAll(() => {
  1404. // mock Date.now: add 3 hours
  1405. const nowIn3H = Date.now() + 3 * 3600 * 1000;
  1406. dateNowMock = jest.spyOn(global.Date, 'now').mockImplementation(() => nowIn3H);
  1407. });
  1408. it('still works when authenticated as owner', async () => {
  1409. authenticatedUserEmailAddress = customers[0].emailAddress;
  1410. await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
  1411. const result = await shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
  1412. GET_ORDER_BY_CODE,
  1413. {
  1414. code: activeOrder.code,
  1415. },
  1416. );
  1417. expect(result.orderByCode!.id).toBe(activeOrder.id);
  1418. });
  1419. it(
  1420. 'access denied when anonymous',
  1421. assertThrowsWithMessage(async () => {
  1422. await shopClient.asAnonymousUser();
  1423. await shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
  1424. GET_ORDER_BY_CODE,
  1425. {
  1426. code: activeOrder.code,
  1427. },
  1428. );
  1429. }, `You are not currently authorized to perform this action`),
  1430. );
  1431. afterAll(() => {
  1432. // restore Date.now
  1433. dateNowMock.mockRestore();
  1434. });
  1435. });
  1436. });
  1437. });
  1438. describe('order merging', () => {
  1439. let customers: GetCustomerList.Items[];
  1440. beforeAll(async () => {
  1441. const result = await adminClient.query<GetCustomerList.Query>(GET_CUSTOMER_LIST);
  1442. customers = result.customers.items;
  1443. });
  1444. it('merges guest order with no existing order', async () => {
  1445. await shopClient.asAnonymousUser();
  1446. const { addItemToOrder } = await shopClient.query<
  1447. AddItemToOrder.Mutation,
  1448. AddItemToOrder.Variables
  1449. >(ADD_ITEM_TO_ORDER, {
  1450. productVariantId: 'T_1',
  1451. quantity: 1,
  1452. });
  1453. orderResultGuard.assertSuccess(addItemToOrder);
  1454. expect(addItemToOrder!.lines.length).toBe(1);
  1455. expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_1');
  1456. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  1457. username: customers[1].emailAddress,
  1458. password: 'test',
  1459. });
  1460. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1461. expect(activeOrder!.lines.length).toBe(1);
  1462. expect(activeOrder!.lines[0].productVariant.id).toBe('T_1');
  1463. });
  1464. it('merges guest order with existing order', async () => {
  1465. await shopClient.asAnonymousUser();
  1466. const { addItemToOrder } = await shopClient.query<
  1467. AddItemToOrder.Mutation,
  1468. AddItemToOrder.Variables
  1469. >(ADD_ITEM_TO_ORDER, {
  1470. productVariantId: 'T_2',
  1471. quantity: 1,
  1472. });
  1473. orderResultGuard.assertSuccess(addItemToOrder);
  1474. expect(addItemToOrder!.lines.length).toBe(1);
  1475. expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_2');
  1476. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  1477. username: customers[1].emailAddress,
  1478. password: 'test',
  1479. });
  1480. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1481. expect(activeOrder!.lines.length).toBe(2);
  1482. expect(activeOrder!.lines[0].productVariant.id).toBe('T_1');
  1483. expect(activeOrder!.lines[1].productVariant.id).toBe('T_2');
  1484. });
  1485. /**
  1486. * See https://github.com/vendure-ecommerce/vendure/issues/263
  1487. */
  1488. it('does not merge when logging in to a different account (issue #263)', async () => {
  1489. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  1490. username: customers[2].emailAddress,
  1491. password: 'test',
  1492. });
  1493. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1494. expect(activeOrder).toBeNull();
  1495. });
  1496. it('does not merge when logging back to other account (issue #263)', async () => {
  1497. const { addItemToOrder } = await shopClient.query<
  1498. AddItemToOrder.Mutation,
  1499. AddItemToOrder.Variables
  1500. >(ADD_ITEM_TO_ORDER, {
  1501. productVariantId: 'T_3',
  1502. quantity: 1,
  1503. });
  1504. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  1505. username: customers[1].emailAddress,
  1506. password: 'test',
  1507. });
  1508. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1509. expect(activeOrder!.lines.length).toBe(2);
  1510. expect(activeOrder!.lines[0].productVariant.id).toBe('T_1');
  1511. expect(activeOrder!.lines[1].productVariant.id).toBe('T_2');
  1512. });
  1513. // https://github.com/vendure-ecommerce/vendure/issues/754
  1514. it('handles merging when an existing order has OrderLines', async () => {
  1515. async function setShippingOnActiveOrder() {
  1516. await shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
  1517. SET_SHIPPING_ADDRESS,
  1518. {
  1519. input: {
  1520. streetLine1: '12 the street',
  1521. countryCode: 'US',
  1522. },
  1523. },
  1524. );
  1525. const { eligibleShippingMethods } = await shopClient.query<GetShippingMethods.Query>(
  1526. GET_ELIGIBLE_SHIPPING_METHODS,
  1527. );
  1528. await shopClient.query<SetShippingMethod.Mutation, SetShippingMethod.Variables>(
  1529. SET_SHIPPING_METHOD,
  1530. {
  1531. id: eligibleShippingMethods[1].id,
  1532. },
  1533. );
  1534. }
  1535. // Set up an existing order and add a ShippingLine
  1536. await shopClient.asUserWithCredentials(customers[2].emailAddress, 'test');
  1537. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1538. productVariantId: 'T_3',
  1539. quantity: 1,
  1540. });
  1541. await setShippingOnActiveOrder();
  1542. // Now start a new guest order
  1543. await shopClient.query(LOG_OUT);
  1544. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1545. productVariantId: 'T_4',
  1546. quantity: 1,
  1547. });
  1548. await setShippingOnActiveOrder();
  1549. // attempt to log in and merge the guest order with the existing order
  1550. const { login } = await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(
  1551. ATTEMPT_LOGIN,
  1552. {
  1553. username: customers[2].emailAddress,
  1554. password: 'test',
  1555. },
  1556. );
  1557. expect(login.identifier).toBe(customers[2].emailAddress);
  1558. });
  1559. });
  1560. describe('security of customer data', () => {
  1561. let customers: GetCustomerList.Items[];
  1562. beforeAll(async () => {
  1563. const result = await adminClient.query<GetCustomerList.Query>(GET_CUSTOMER_LIST);
  1564. customers = result.customers.items;
  1565. });
  1566. it('cannot setCustomOrder to existing non-guest Customer', async () => {
  1567. await shopClient.asAnonymousUser();
  1568. const { addItemToOrder } = await shopClient.query<
  1569. AddItemToOrder.Mutation,
  1570. AddItemToOrder.Variables
  1571. >(ADD_ITEM_TO_ORDER, {
  1572. productVariantId: 'T_1',
  1573. quantity: 1,
  1574. });
  1575. const { setCustomerForOrder } = await shopClient.query<
  1576. SetCustomerForOrder.Mutation,
  1577. SetCustomerForOrder.Variables
  1578. >(SET_CUSTOMER, {
  1579. input: {
  1580. emailAddress: customers[0].emailAddress,
  1581. firstName: 'Evil',
  1582. lastName: 'Hacker',
  1583. },
  1584. });
  1585. orderResultGuard.assertErrorResult(setCustomerForOrder);
  1586. expect(setCustomerForOrder!.message).toBe('The email address is not available.');
  1587. expect(setCustomerForOrder!.errorCode).toBe(ErrorCode.EMAIL_ADDRESS_CONFLICT_ERROR);
  1588. const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
  1589. GET_CUSTOMER,
  1590. {
  1591. id: customers[0].id,
  1592. },
  1593. );
  1594. expect(customer!.firstName).not.toBe('Evil');
  1595. expect(customer!.lastName).not.toBe('Hacker');
  1596. });
  1597. it('guest cannot access Addresses of guest customer', async () => {
  1598. await shopClient.asAnonymousUser();
  1599. const { addItemToOrder } = await shopClient.query<
  1600. AddItemToOrder.Mutation,
  1601. AddItemToOrder.Variables
  1602. >(ADD_ITEM_TO_ORDER, {
  1603. productVariantId: 'T_1',
  1604. quantity: 1,
  1605. });
  1606. await shopClient.query<SetCustomerForOrder.Mutation, SetCustomerForOrder.Variables>(
  1607. SET_CUSTOMER,
  1608. {
  1609. input: {
  1610. emailAddress: 'test@test.com',
  1611. firstName: 'Evil',
  1612. lastName: 'Hacker',
  1613. },
  1614. },
  1615. );
  1616. const { activeOrder } = await shopClient.query<GetCustomerAddresses.Query>(
  1617. GET_ACTIVE_ORDER_ADDRESSES,
  1618. );
  1619. expect(activeOrder!.customer!.addresses).toEqual([]);
  1620. });
  1621. it('guest cannot access Orders of guest customer', async () => {
  1622. await shopClient.asAnonymousUser();
  1623. const { addItemToOrder } = await shopClient.query<
  1624. AddItemToOrder.Mutation,
  1625. AddItemToOrder.Variables
  1626. >(ADD_ITEM_TO_ORDER, {
  1627. productVariantId: 'T_1',
  1628. quantity: 1,
  1629. });
  1630. await shopClient.query<SetCustomerForOrder.Mutation, SetCustomerForOrder.Variables>(
  1631. SET_CUSTOMER,
  1632. {
  1633. input: {
  1634. emailAddress: 'test@test.com',
  1635. firstName: 'Evil',
  1636. lastName: 'Hacker',
  1637. },
  1638. },
  1639. );
  1640. const { activeOrder } = await shopClient.query<GetCustomerOrders.Query>(GET_ACTIVE_ORDER_ORDERS);
  1641. expect(activeOrder!.customer!.orders.items).toEqual([]);
  1642. });
  1643. });
  1644. describe('order custom fields', () => {
  1645. it('custom fields added to type', async () => {
  1646. await shopClient.asAnonymousUser();
  1647. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1648. productVariantId: 'T_1',
  1649. quantity: 1,
  1650. });
  1651. const { activeOrder } = await shopClient.query(GET_ORDER_CUSTOM_FIELDS);
  1652. expect(activeOrder?.customFields).toEqual({
  1653. orderImage: null,
  1654. giftWrap: false,
  1655. });
  1656. });
  1657. it('setting order custom fields', async () => {
  1658. const { setOrderCustomFields } = await shopClient.query(SET_ORDER_CUSTOM_FIELDS, {
  1659. input: {
  1660. customFields: { giftWrap: true, orderImageId: 'T_1' },
  1661. },
  1662. });
  1663. expect(setOrderCustomFields?.customFields).toEqual({
  1664. orderImage: { id: 'T_1' },
  1665. giftWrap: true,
  1666. });
  1667. const { activeOrder } = await shopClient.query(GET_ORDER_CUSTOM_FIELDS);
  1668. expect(activeOrder?.customFields).toEqual({
  1669. orderImage: { id: 'T_1' },
  1670. giftWrap: true,
  1671. });
  1672. });
  1673. });
  1674. describe('remove all order lines', () => {
  1675. beforeAll(async () => {
  1676. await shopClient.asAnonymousUser();
  1677. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1678. productVariantId: 'T_1',
  1679. quantity: 1,
  1680. });
  1681. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1682. productVariantId: 'T_2',
  1683. quantity: 3,
  1684. });
  1685. });
  1686. it('should remove all order lines', async () => {
  1687. const { removeAllOrderLines } = await shopClient.query<
  1688. RemoveAllOrderLines.Mutation,
  1689. RemoveAllOrderLines.Variables
  1690. >(REMOVE_ALL_ORDER_LINES);
  1691. orderResultGuard.assertSuccess(removeAllOrderLines);
  1692. expect(removeAllOrderLines?.total).toBe(0);
  1693. expect(removeAllOrderLines?.lines.length).toBe(0);
  1694. });
  1695. });
  1696. describe('validation of product variant availability', () => {
  1697. const bonsaiProductId = 'T_20';
  1698. const bonsaiVariantId = 'T_34';
  1699. beforeAll(async () => {
  1700. await shopClient.asAnonymousUser();
  1701. });
  1702. it(
  1703. 'addItemToOrder errors when product is disabled',
  1704. assertThrowsWithMessage(async () => {
  1705. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  1706. input: {
  1707. id: bonsaiProductId,
  1708. enabled: false,
  1709. },
  1710. });
  1711. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1712. productVariantId: bonsaiVariantId,
  1713. quantity: 1,
  1714. });
  1715. }, `No ProductVariant with the id '34' could be found`),
  1716. );
  1717. it(
  1718. 'addItemToOrder errors when product variant is disabled',
  1719. assertThrowsWithMessage(async () => {
  1720. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  1721. input: {
  1722. id: bonsaiProductId,
  1723. enabled: true,
  1724. },
  1725. });
  1726. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  1727. UPDATE_PRODUCT_VARIANTS,
  1728. {
  1729. input: [
  1730. {
  1731. id: bonsaiVariantId,
  1732. enabled: false,
  1733. },
  1734. ],
  1735. },
  1736. );
  1737. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1738. productVariantId: bonsaiVariantId,
  1739. quantity: 1,
  1740. });
  1741. }, `No ProductVariant with the id '34' could be found`),
  1742. );
  1743. it(
  1744. 'addItemToOrder errors when product is deleted',
  1745. assertThrowsWithMessage(async () => {
  1746. await adminClient.query<DeleteProduct.Mutation, DeleteProduct.Variables>(DELETE_PRODUCT, {
  1747. id: bonsaiProductId,
  1748. });
  1749. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1750. productVariantId: bonsaiVariantId,
  1751. quantity: 1,
  1752. });
  1753. }, `No ProductVariant with the id '34' could be found`),
  1754. );
  1755. it(
  1756. 'addItemToOrder errors when product variant is deleted',
  1757. assertThrowsWithMessage(async () => {
  1758. await adminClient.query<DeleteProductVariant.Mutation, DeleteProductVariant.Variables>(
  1759. DELETE_PRODUCT_VARIANT,
  1760. {
  1761. id: bonsaiVariantId,
  1762. },
  1763. );
  1764. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  1765. productVariantId: bonsaiVariantId,
  1766. quantity: 1,
  1767. });
  1768. }, `No ProductVariant with the id '34' could be found`),
  1769. );
  1770. let orderWithDeletedProductVariantId: string;
  1771. it('errors when transitioning to ArrangingPayment with deleted variant', async () => {
  1772. const orchidProductId = 'T_19';
  1773. const orchidVariantId = 'T_33';
  1774. await shopClient.asUserWithCredentials('marques.sawayn@hotmail.com', 'test');
  1775. const { addItemToOrder } = await shopClient.query<
  1776. AddItemToOrder.Mutation,
  1777. AddItemToOrder.Variables
  1778. >(ADD_ITEM_TO_ORDER, {
  1779. productVariantId: orchidVariantId,
  1780. quantity: 1,
  1781. });
  1782. orderResultGuard.assertSuccess(addItemToOrder);
  1783. orderWithDeletedProductVariantId = addItemToOrder.id;
  1784. await adminClient.query<DeleteProduct.Mutation, DeleteProduct.Variables>(DELETE_PRODUCT, {
  1785. id: orchidProductId,
  1786. });
  1787. const { transitionOrderToState } = await shopClient.query<
  1788. TransitionToState.Mutation,
  1789. TransitionToState.Variables
  1790. >(TRANSITION_TO_STATE, {
  1791. state: 'ArrangingPayment',
  1792. });
  1793. orderResultGuard.assertErrorResult(transitionOrderToState);
  1794. expect(transitionOrderToState!.transitionError).toBe(
  1795. `Cannot transition to "ArrangingPayment" because the Order contains ProductVariants which are no longer available`,
  1796. );
  1797. expect(transitionOrderToState!.errorCode).toBe(ErrorCode.ORDER_STATE_TRANSITION_ERROR);
  1798. });
  1799. // https://github.com/vendure-ecommerce/vendure/issues/1567
  1800. it('allows transitioning to Cancelled with deleted variant', async () => {
  1801. const { cancelOrder } = await adminClient.query<
  1802. CancelOrderMutation,
  1803. CancelOrderMutationVariables
  1804. >(CANCEL_ORDER, {
  1805. input: {
  1806. orderId: orderWithDeletedProductVariantId,
  1807. },
  1808. });
  1809. orderResultGuard.assertSuccess(cancelOrder);
  1810. expect(cancelOrder.state).toBe('Cancelled');
  1811. });
  1812. });
  1813. // https://github.com/vendure-ecommerce/vendure/issues/1195
  1814. describe('shipping method invalidation', () => {
  1815. let GBShippingMethodId: string;
  1816. let ATShippingMethodId: string;
  1817. beforeAll(async () => {
  1818. // First we will remove all ShippingMethods and set up 2 specialized ones
  1819. const { shippingMethods } = await adminClient.query<GetShippingMethodList.Query>(
  1820. GET_SHIPPING_METHOD_LIST,
  1821. );
  1822. for (const method of shippingMethods.items) {
  1823. await adminClient.query<DeleteShippingMethod.Mutation, DeleteShippingMethod.Variables>(
  1824. DELETE_SHIPPING_METHOD,
  1825. {
  1826. id: method.id,
  1827. },
  1828. );
  1829. }
  1830. function createCountryCodeShippingMethodInput(countryCode: string): CreateShippingMethodInput {
  1831. return {
  1832. code: `${countryCode}-shipping`,
  1833. translations: [
  1834. { languageCode: LanguageCode.en, name: `${countryCode} shipping`, description: '' },
  1835. ],
  1836. fulfillmentHandler: manualFulfillmentHandler.code,
  1837. checker: {
  1838. code: countryCodeShippingEligibilityChecker.code,
  1839. arguments: [{ name: 'countryCode', value: countryCode }],
  1840. },
  1841. calculator: {
  1842. code: defaultShippingCalculator.code,
  1843. arguments: [
  1844. { name: 'rate', value: '1000' },
  1845. { name: 'taxRate', value: '0' },
  1846. { name: 'includesTax', value: 'auto' },
  1847. ],
  1848. },
  1849. };
  1850. }
  1851. // Now create 2 shipping methods, valid only for a single country
  1852. const result1 = await adminClient.query<
  1853. CreateShippingMethod.Mutation,
  1854. CreateShippingMethod.Variables
  1855. >(CREATE_SHIPPING_METHOD, {
  1856. input: createCountryCodeShippingMethodInput('GB'),
  1857. });
  1858. GBShippingMethodId = result1.createShippingMethod.id;
  1859. const result2 = await adminClient.query<
  1860. CreateShippingMethod.Mutation,
  1861. CreateShippingMethod.Variables
  1862. >(CREATE_SHIPPING_METHOD, {
  1863. input: createCountryCodeShippingMethodInput('AT'),
  1864. });
  1865. ATShippingMethodId = result2.createShippingMethod.id;
  1866. // Now create an order to GB and set the GB shipping method
  1867. const { addItemToOrder } = await shopClient.query<
  1868. AddItemToOrder.Mutation,
  1869. AddItemToOrder.Variables
  1870. >(ADD_ITEM_TO_ORDER, {
  1871. productVariantId: 'T_1',
  1872. quantity: 1,
  1873. });
  1874. await shopClient.query<SetCustomerForOrder.Mutation, SetCustomerForOrder.Variables>(
  1875. SET_CUSTOMER,
  1876. {
  1877. input: {
  1878. emailAddress: 'test-2@test.com',
  1879. firstName: 'Test',
  1880. lastName: 'Person 2',
  1881. },
  1882. },
  1883. );
  1884. await shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
  1885. SET_SHIPPING_ADDRESS,
  1886. {
  1887. input: {
  1888. streetLine1: '12 the street',
  1889. countryCode: 'GB',
  1890. },
  1891. },
  1892. );
  1893. await shopClient.query<SetShippingMethod.Mutation, SetShippingMethod.Variables>(
  1894. SET_SHIPPING_METHOD,
  1895. {
  1896. id: GBShippingMethodId,
  1897. },
  1898. );
  1899. });
  1900. it('if selected method no longer eligible, next best is set automatically', async () => {
  1901. const result1 = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1902. expect(result1.activeOrder?.shippingLines[0].shippingMethod.id).toBe(GBShippingMethodId);
  1903. await shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
  1904. SET_SHIPPING_ADDRESS,
  1905. {
  1906. input: {
  1907. streetLine1: '12 the street',
  1908. countryCode: 'AT',
  1909. },
  1910. },
  1911. );
  1912. const result2 = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1913. expect(result2.activeOrder?.shippingLines[0].shippingMethod.id).toBe(ATShippingMethodId);
  1914. });
  1915. it('if no method is eligible, shipping lines are cleared', async () => {
  1916. await shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
  1917. SET_SHIPPING_ADDRESS,
  1918. {
  1919. input: {
  1920. streetLine1: '12 the street',
  1921. countryCode: 'US',
  1922. },
  1923. },
  1924. );
  1925. const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1926. expect(result.activeOrder?.shippingLines).toEqual([]);
  1927. });
  1928. // https://github.com/vendure-ecommerce/vendure/issues/1441
  1929. it('shipping methods are re-evaluated when all OrderLines are removed', async () => {
  1930. const { createShippingMethod } = await adminClient.query<
  1931. CreateShippingMethod.Mutation,
  1932. CreateShippingMethod.Variables
  1933. >(CREATE_SHIPPING_METHOD, {
  1934. input: {
  1935. code: `min-price-shipping`,
  1936. translations: [
  1937. { languageCode: LanguageCode.en, name: `min price shipping`, description: '' },
  1938. ],
  1939. fulfillmentHandler: manualFulfillmentHandler.code,
  1940. checker: {
  1941. code: defaultShippingEligibilityChecker.code,
  1942. arguments: [{ name: 'orderMinimum', value: '100' }],
  1943. },
  1944. calculator: {
  1945. code: defaultShippingCalculator.code,
  1946. arguments: [
  1947. { name: 'rate', value: '1000' },
  1948. { name: 'taxRate', value: '0' },
  1949. { name: 'includesTax', value: 'auto' },
  1950. ],
  1951. },
  1952. },
  1953. });
  1954. const minPriceShippingMethodId = createShippingMethod.id;
  1955. await shopClient.query<SetShippingMethod.Mutation, SetShippingMethod.Variables>(
  1956. SET_SHIPPING_METHOD,
  1957. {
  1958. id: minPriceShippingMethodId,
  1959. },
  1960. );
  1961. const result1 = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  1962. expect(result1.activeOrder?.shippingLines[0].shippingMethod.id).toBe(minPriceShippingMethodId);
  1963. const { removeAllOrderLines } = await shopClient.query<
  1964. RemoveAllOrderLines.Mutation,
  1965. RemoveAllOrderLines.Variables
  1966. >(REMOVE_ALL_ORDER_LINES);
  1967. orderResultGuard.assertSuccess(removeAllOrderLines);
  1968. expect(removeAllOrderLines.shippingLines.length).toBe(0);
  1969. expect(removeAllOrderLines.shippingWithTax).toBe(0);
  1970. });
  1971. });
  1972. describe('edge cases', () => {
  1973. it('calling setShippingMethod and setBillingMethod in parallel does not introduce race condition', async () => {
  1974. const shippingAddress: CreateAddressInput = {
  1975. fullName: 'name',
  1976. company: 'company',
  1977. streetLine1: '12 Shipping Street',
  1978. streetLine2: null,
  1979. city: 'foo',
  1980. province: 'bar',
  1981. postalCode: '123456',
  1982. countryCode: 'US',
  1983. phoneNumber: '4444444',
  1984. };
  1985. const billingAddress: CreateAddressInput = {
  1986. fullName: 'name',
  1987. company: 'company',
  1988. streetLine1: '22 Billing Avenue',
  1989. streetLine2: null,
  1990. city: 'foo',
  1991. province: 'bar',
  1992. postalCode: '123456',
  1993. countryCode: 'US',
  1994. phoneNumber: '4444444',
  1995. };
  1996. await Promise.all([
  1997. shopClient.query<SetBillingAddress.Mutation, SetBillingAddress.Variables>(
  1998. SET_BILLING_ADDRESS,
  1999. {
  2000. input: billingAddress,
  2001. },
  2002. ),
  2003. shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
  2004. SET_SHIPPING_ADDRESS,
  2005. {
  2006. input: shippingAddress,
  2007. },
  2008. ),
  2009. ]);
  2010. const { activeOrder } = await shopClient.query(gql`
  2011. query {
  2012. activeOrder {
  2013. shippingAddress {
  2014. ...OrderAddress
  2015. }
  2016. billingAddress {
  2017. ...OrderAddress
  2018. }
  2019. }
  2020. }
  2021. fragment OrderAddress on OrderAddress {
  2022. fullName
  2023. company
  2024. streetLine1
  2025. streetLine2
  2026. city
  2027. province
  2028. postalCode
  2029. countryCode
  2030. phoneNumber
  2031. }
  2032. `);
  2033. expect(activeOrder.shippingAddress).toEqual(shippingAddress);
  2034. expect(activeOrder.billingAddress).toEqual(billingAddress);
  2035. });
  2036. });
  2037. });
  2038. const GET_ORDER_CUSTOM_FIELDS = gql`
  2039. query GetOrderCustomFields {
  2040. activeOrder {
  2041. id
  2042. customFields {
  2043. giftWrap
  2044. orderImage {
  2045. id
  2046. }
  2047. }
  2048. }
  2049. }
  2050. `;
  2051. const SET_ORDER_CUSTOM_FIELDS = gql`
  2052. mutation SetOrderCustomFields($input: UpdateOrderInput!) {
  2053. setOrderCustomFields(input: $input) {
  2054. ... on Order {
  2055. id
  2056. customFields {
  2057. giftWrap
  2058. orderImage {
  2059. id
  2060. }
  2061. }
  2062. }
  2063. ... on ErrorResult {
  2064. errorCode
  2065. message
  2066. }
  2067. }
  2068. }
  2069. `;
  2070. export const LOG_OUT = gql`
  2071. mutation LogOut {
  2072. logout {
  2073. success
  2074. }
  2075. }
  2076. `;
  2077. export const ADD_ITEM_TO_ORDER_WITH_CUSTOM_FIELDS = gql`
  2078. mutation AddItemToOrderWithCustomFields(
  2079. $productVariantId: ID!
  2080. $quantity: Int!
  2081. $customFields: OrderLineCustomFieldsInput
  2082. ) {
  2083. addItemToOrder(
  2084. productVariantId: $productVariantId
  2085. quantity: $quantity
  2086. customFields: $customFields
  2087. ) {
  2088. ...UpdatedOrder
  2089. ... on ErrorResult {
  2090. errorCode
  2091. message
  2092. }
  2093. }
  2094. }
  2095. ${UPDATED_ORDER_FRAGMENT}
  2096. `;
  2097. const ADJUST_ORDER_LINE_WITH_CUSTOM_FIELDS = gql`
  2098. mutation ($orderLineId: ID!, $quantity: Int!, $customFields: OrderLineCustomFieldsInput) {
  2099. adjustOrderLine(orderLineId: $orderLineId, quantity: $quantity, customFields: $customFields) {
  2100. ... on Order {
  2101. lines {
  2102. id
  2103. customFields {
  2104. notes
  2105. lineImage {
  2106. id
  2107. }
  2108. lineImages {
  2109. id
  2110. }
  2111. }
  2112. }
  2113. }
  2114. }
  2115. }
  2116. `;