shop-order.e2e-spec.ts 119 KB

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