shop-order.e2e-spec.ts 83 KB

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