shop-order.e2e-spec.ts 76 KB

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