shop-order.e2e-spec.ts 116 KB

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