order-promotion.e2e-spec.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. /* tslint:disable:no-non-null-assertion */
  2. import { pick } from '@vendure/common/lib/pick';
  3. import {
  4. atLeastNWithFacets,
  5. discountOnItemWithFacets,
  6. minimumOrderAmount,
  7. orderPercentageDiscount,
  8. } from '@vendure/core';
  9. import { createTestEnvironment } from '@vendure/testing';
  10. import gql from 'graphql-tag';
  11. import path from 'path';
  12. import { initialData } from '../../../e2e-common/e2e-initial-data';
  13. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  14. import { testSuccessfulPaymentMethod } from './fixtures/test-payment-methods';
  15. import {
  16. CreatePromotion,
  17. CreatePromotionInput,
  18. GetFacetList,
  19. GetPromoProducts,
  20. HistoryEntryType,
  21. } from './graphql/generated-e2e-admin-types';
  22. import {
  23. AddItemToOrder,
  24. AdjustItemQuantity,
  25. ApplyCouponCode,
  26. GetActiveOrder,
  27. GetOrderPromotionsByCode,
  28. RemoveCouponCode,
  29. SetCustomerForOrder,
  30. } from './graphql/generated-e2e-shop-types';
  31. import { CREATE_PROMOTION, GET_FACET_LIST } from './graphql/shared-definitions';
  32. import {
  33. ADD_ITEM_TO_ORDER,
  34. ADJUST_ITEM_QUANTITY,
  35. APPLY_COUPON_CODE,
  36. GET_ACTIVE_ORDER,
  37. GET_ORDER_PROMOTIONS_BY_CODE,
  38. REMOVE_COUPON_CODE,
  39. SET_CUSTOMER,
  40. } from './graphql/shop-definitions';
  41. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  42. import { addPaymentToOrder, proceedToArrangingPayment } from './utils/test-order-utils';
  43. describe('Promotions applied to Orders', () => {
  44. const { server, adminClient, shopClient } = createTestEnvironment({
  45. ...testConfig,
  46. paymentOptions: {
  47. paymentMethodHandlers: [testSuccessfulPaymentMethod],
  48. },
  49. });
  50. const freeOrderAction = {
  51. code: orderPercentageDiscount.code,
  52. arguments: [{ name: 'discount', type: 'int', value: '100' }],
  53. };
  54. const minOrderAmountCondition = (min: number) => ({
  55. code: minimumOrderAmount.code,
  56. arguments: [
  57. { name: 'amount', type: 'int', value: min.toString() },
  58. { name: 'taxInclusive', type: 'boolean', value: 'true' },
  59. ],
  60. });
  61. let products: GetPromoProducts.Items[];
  62. beforeAll(async () => {
  63. await server.init({
  64. dataDir: path.join(__dirname, '__data__'),
  65. initialData,
  66. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-promotions.csv'),
  67. customerCount: 2,
  68. });
  69. await adminClient.asSuperAdmin();
  70. await getProducts();
  71. await createGlobalPromotions();
  72. }, TEST_SETUP_TIMEOUT_MS);
  73. afterAll(async () => {
  74. await server.destroy();
  75. });
  76. describe('coupon codes', () => {
  77. const TEST_COUPON_CODE = 'TESTCOUPON';
  78. const EXPIRED_COUPON_CODE = 'EXPIRED';
  79. let promoFreeWithCoupon: CreatePromotion.CreatePromotion;
  80. let promoFreeWithExpiredCoupon: CreatePromotion.CreatePromotion;
  81. beforeAll(async () => {
  82. promoFreeWithCoupon = await createPromotion({
  83. enabled: true,
  84. name: 'Free with test coupon',
  85. couponCode: TEST_COUPON_CODE,
  86. conditions: [],
  87. actions: [freeOrderAction],
  88. });
  89. promoFreeWithExpiredCoupon = await createPromotion({
  90. enabled: true,
  91. name: 'Expired coupon',
  92. endsAt: new Date(2010, 0, 0),
  93. couponCode: EXPIRED_COUPON_CODE,
  94. conditions: [],
  95. actions: [freeOrderAction],
  96. });
  97. await shopClient.asAnonymousUser();
  98. const item60 = getVariantBySlug('item-60');
  99. const { addItemToOrder } = await shopClient.query<
  100. AddItemToOrder.Mutation,
  101. AddItemToOrder.Variables
  102. >(ADD_ITEM_TO_ORDER, {
  103. productVariantId: item60.id,
  104. quantity: 1,
  105. });
  106. });
  107. afterAll(async () => {
  108. await deletePromotion(promoFreeWithCoupon.id);
  109. await deletePromotion(promoFreeWithExpiredCoupon.id);
  110. });
  111. it(
  112. 'applyCouponCode throws with nonexistant code',
  113. assertThrowsWithMessage(async () => {
  114. await shopClient.query<ApplyCouponCode.Mutation, ApplyCouponCode.Variables>(
  115. APPLY_COUPON_CODE,
  116. {
  117. couponCode: 'bad code',
  118. },
  119. );
  120. }, 'Coupon code "bad code" is not valid'),
  121. );
  122. it(
  123. 'applyCouponCode throws with expired code',
  124. assertThrowsWithMessage(async () => {
  125. await shopClient.query<ApplyCouponCode.Mutation, ApplyCouponCode.Variables>(
  126. APPLY_COUPON_CODE,
  127. {
  128. couponCode: EXPIRED_COUPON_CODE,
  129. },
  130. );
  131. }, `Coupon code "${EXPIRED_COUPON_CODE}" has expired`),
  132. );
  133. it('applies a valid coupon code', async () => {
  134. const { applyCouponCode } = await shopClient.query<
  135. ApplyCouponCode.Mutation,
  136. ApplyCouponCode.Variables
  137. >(APPLY_COUPON_CODE, {
  138. couponCode: TEST_COUPON_CODE,
  139. });
  140. expect(applyCouponCode!.couponCodes).toEqual([TEST_COUPON_CODE]);
  141. expect(applyCouponCode!.adjustments.length).toBe(1);
  142. expect(applyCouponCode!.adjustments[0].description).toBe('Free with test coupon');
  143. expect(applyCouponCode!.total).toBe(0);
  144. });
  145. it('order history records application', async () => {
  146. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  147. expect(activeOrder!.history.items).toEqual([
  148. {
  149. id: 'T_1',
  150. type: HistoryEntryType.ORDER_COUPON_APPLIED,
  151. data: {
  152. couponCode: TEST_COUPON_CODE,
  153. promotionId: 'T_3',
  154. },
  155. },
  156. ]);
  157. });
  158. it('de-duplicates existing codes', async () => {
  159. const { applyCouponCode } = await shopClient.query<
  160. ApplyCouponCode.Mutation,
  161. ApplyCouponCode.Variables
  162. >(APPLY_COUPON_CODE, {
  163. couponCode: TEST_COUPON_CODE,
  164. });
  165. expect(applyCouponCode!.couponCodes).toEqual([TEST_COUPON_CODE]);
  166. });
  167. it('removes a coupon code', async () => {
  168. const { removeCouponCode } = await shopClient.query<
  169. RemoveCouponCode.Mutation,
  170. RemoveCouponCode.Variables
  171. >(REMOVE_COUPON_CODE, {
  172. couponCode: TEST_COUPON_CODE,
  173. });
  174. expect(removeCouponCode!.adjustments.length).toBe(0);
  175. expect(removeCouponCode!.total).toBe(6000);
  176. });
  177. it('order history records removal', async () => {
  178. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  179. expect(activeOrder!.history.items).toEqual([
  180. {
  181. id: 'T_1',
  182. type: HistoryEntryType.ORDER_COUPON_APPLIED,
  183. data: {
  184. couponCode: TEST_COUPON_CODE,
  185. promotionId: 'T_3',
  186. },
  187. },
  188. {
  189. id: 'T_2',
  190. type: HistoryEntryType.ORDER_COUPON_REMOVED,
  191. data: {
  192. couponCode: TEST_COUPON_CODE,
  193. },
  194. },
  195. ]);
  196. });
  197. it('does not record removal of coupon code that was not added', async () => {
  198. const { removeCouponCode } = await shopClient.query<
  199. RemoveCouponCode.Mutation,
  200. RemoveCouponCode.Variables
  201. >(REMOVE_COUPON_CODE, {
  202. couponCode: 'NOT_THERE',
  203. });
  204. expect(removeCouponCode!.history.items).toEqual([
  205. {
  206. id: 'T_1',
  207. type: HistoryEntryType.ORDER_COUPON_APPLIED,
  208. data: {
  209. couponCode: TEST_COUPON_CODE,
  210. promotionId: 'T_3',
  211. },
  212. },
  213. {
  214. id: 'T_2',
  215. type: HistoryEntryType.ORDER_COUPON_REMOVED,
  216. data: {
  217. couponCode: TEST_COUPON_CODE,
  218. },
  219. },
  220. ]);
  221. });
  222. });
  223. describe('default PromotionConditions', () => {
  224. beforeEach(async () => {
  225. await shopClient.asAnonymousUser();
  226. });
  227. it('minimumOrderAmount', async () => {
  228. const promotion = await createPromotion({
  229. enabled: true,
  230. name: 'Free if order total greater than 100',
  231. conditions: [minOrderAmountCondition(10000)],
  232. actions: [freeOrderAction],
  233. });
  234. const item60 = getVariantBySlug('item-60');
  235. const { addItemToOrder } = await shopClient.query<
  236. AddItemToOrder.Mutation,
  237. AddItemToOrder.Variables
  238. >(ADD_ITEM_TO_ORDER, {
  239. productVariantId: item60.id,
  240. quantity: 1,
  241. });
  242. expect(addItemToOrder!.total).toBe(6000);
  243. expect(addItemToOrder!.adjustments.length).toBe(0);
  244. const { adjustOrderLine } = await shopClient.query<
  245. AdjustItemQuantity.Mutation,
  246. AdjustItemQuantity.Variables
  247. >(ADJUST_ITEM_QUANTITY, {
  248. orderLineId: addItemToOrder!.lines[0].id,
  249. quantity: 2,
  250. });
  251. expect(adjustOrderLine!.total).toBe(0);
  252. expect(adjustOrderLine!.adjustments[0].description).toBe('Free if order total greater than 100');
  253. expect(adjustOrderLine!.adjustments[0].amount).toBe(-12000);
  254. await deletePromotion(promotion.id);
  255. });
  256. it('atLeastNWithFacets', async () => {
  257. const { facets } = await adminClient.query<GetFacetList.Query>(GET_FACET_LIST);
  258. const saleFacetValue = facets.items[0].values[0];
  259. const promotion = await createPromotion({
  260. enabled: true,
  261. name: 'Free if order contains 2 items with Sale facet value',
  262. conditions: [
  263. {
  264. code: atLeastNWithFacets.code,
  265. arguments: [
  266. { name: 'minimum', type: 'int', value: '2' },
  267. { name: 'facets', type: 'facetValueIds', value: `["${saleFacetValue.id}"]` },
  268. ],
  269. },
  270. ],
  271. actions: [freeOrderAction],
  272. });
  273. const itemSale1 = getVariantBySlug('item-sale-1');
  274. const itemSale12 = getVariantBySlug('item-sale-12');
  275. const { addItemToOrder: res1 } = await shopClient.query<
  276. AddItemToOrder.Mutation,
  277. AddItemToOrder.Variables
  278. >(ADD_ITEM_TO_ORDER, {
  279. productVariantId: itemSale1.id,
  280. quantity: 1,
  281. });
  282. expect(res1!.total).toBe(120);
  283. expect(res1!.adjustments.length).toBe(0);
  284. const { addItemToOrder: res2 } = await shopClient.query<
  285. AddItemToOrder.Mutation,
  286. AddItemToOrder.Variables
  287. >(ADD_ITEM_TO_ORDER, {
  288. productVariantId: itemSale12.id,
  289. quantity: 1,
  290. });
  291. expect(res2!.total).toBe(0);
  292. expect(res2!.adjustments.length).toBe(1);
  293. expect(res2!.total).toBe(0);
  294. expect(res2!.adjustments[0].description).toBe(
  295. 'Free if order contains 2 items with Sale facet value',
  296. );
  297. expect(res2!.adjustments[0].amount).toBe(-1320);
  298. await deletePromotion(promotion.id);
  299. });
  300. });
  301. describe('default PromotionActions', () => {
  302. beforeEach(async () => {
  303. await shopClient.asAnonymousUser();
  304. });
  305. it('orderPercentageDiscount', async () => {
  306. const couponCode = '50%_off_order';
  307. const promotion = await createPromotion({
  308. enabled: true,
  309. name: '50% discount on order',
  310. couponCode,
  311. conditions: [],
  312. actions: [
  313. {
  314. code: orderPercentageDiscount.code,
  315. arguments: [{ name: 'discount', type: 'int', value: '50' }],
  316. },
  317. ],
  318. });
  319. const item60 = getVariantBySlug('item-60');
  320. const { addItemToOrder } = await shopClient.query<
  321. AddItemToOrder.Mutation,
  322. AddItemToOrder.Variables
  323. >(ADD_ITEM_TO_ORDER, {
  324. productVariantId: item60.id,
  325. quantity: 1,
  326. });
  327. expect(addItemToOrder!.total).toBe(6000);
  328. expect(addItemToOrder!.adjustments.length).toBe(0);
  329. const { applyCouponCode } = await shopClient.query<
  330. ApplyCouponCode.Mutation,
  331. ApplyCouponCode.Variables
  332. >(APPLY_COUPON_CODE, {
  333. couponCode,
  334. });
  335. expect(applyCouponCode!.adjustments.length).toBe(1);
  336. expect(applyCouponCode!.adjustments[0].description).toBe('50% discount on order');
  337. expect(applyCouponCode!.total).toBe(3000);
  338. await deletePromotion(promotion.id);
  339. });
  340. it('discountOnItemWithFacets', async () => {
  341. const { facets } = await adminClient.query<GetFacetList.Query>(GET_FACET_LIST);
  342. const saleFacetValue = facets.items[0].values[0];
  343. const couponCode = '50%_off_sale_items';
  344. const promotion = await createPromotion({
  345. enabled: true,
  346. name: '50% off sale items',
  347. couponCode,
  348. conditions: [],
  349. actions: [
  350. {
  351. code: discountOnItemWithFacets.code,
  352. arguments: [
  353. { name: 'discount', type: 'int', value: '50' },
  354. { name: 'facets', type: 'facetValueIds', value: `["${saleFacetValue.id}"]` },
  355. ],
  356. },
  357. ],
  358. });
  359. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  360. productVariantId: getVariantBySlug('item-12').id,
  361. quantity: 1,
  362. });
  363. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  364. productVariantId: getVariantBySlug('item-sale-12').id,
  365. quantity: 1,
  366. });
  367. const { addItemToOrder } = await shopClient.query<
  368. AddItemToOrder.Mutation,
  369. AddItemToOrder.Variables
  370. >(ADD_ITEM_TO_ORDER, {
  371. productVariantId: getVariantBySlug('item-sale-1').id,
  372. quantity: 2,
  373. });
  374. expect(addItemToOrder!.adjustments.length).toBe(0);
  375. expect(addItemToOrder!.total).toBe(2640);
  376. const { applyCouponCode } = await shopClient.query<
  377. ApplyCouponCode.Mutation,
  378. ApplyCouponCode.Variables
  379. >(APPLY_COUPON_CODE, {
  380. couponCode,
  381. });
  382. expect(applyCouponCode!.total).toBe(1920);
  383. await deletePromotion(promotion.id);
  384. });
  385. });
  386. describe('per-customer usage limit', () => {
  387. const TEST_COUPON_CODE = 'TESTCOUPON';
  388. let promoWithUsageLimit: CreatePromotion.CreatePromotion;
  389. beforeAll(async () => {
  390. promoWithUsageLimit = await createPromotion({
  391. enabled: true,
  392. name: 'Free with test coupon',
  393. couponCode: TEST_COUPON_CODE,
  394. perCustomerUsageLimit: 1,
  395. conditions: [],
  396. actions: [freeOrderAction],
  397. });
  398. });
  399. afterAll(async () => {
  400. await deletePromotion(promoWithUsageLimit.id);
  401. });
  402. async function createNewActiveOrder() {
  403. const item60 = getVariantBySlug('item-60');
  404. const { addItemToOrder } = await shopClient.query<
  405. AddItemToOrder.Mutation,
  406. AddItemToOrder.Variables
  407. >(ADD_ITEM_TO_ORDER, {
  408. productVariantId: item60.id,
  409. quantity: 1,
  410. });
  411. return addItemToOrder;
  412. }
  413. describe('guest customer', () => {
  414. const GUEST_EMAIL_ADDRESS = 'guest@test.com';
  415. let orderCode: string;
  416. function addGuestCustomerToOrder() {
  417. return shopClient.query<SetCustomerForOrder.Mutation, SetCustomerForOrder.Variables>(
  418. SET_CUSTOMER,
  419. {
  420. input: {
  421. emailAddress: GUEST_EMAIL_ADDRESS,
  422. firstName: 'Guest',
  423. lastName: 'Customer',
  424. },
  425. },
  426. );
  427. }
  428. it('allows initial usage', async () => {
  429. await shopClient.asAnonymousUser();
  430. await createNewActiveOrder();
  431. await addGuestCustomerToOrder();
  432. const { applyCouponCode } = await shopClient.query<
  433. ApplyCouponCode.Mutation,
  434. ApplyCouponCode.Variables
  435. >(APPLY_COUPON_CODE, { couponCode: TEST_COUPON_CODE });
  436. expect(applyCouponCode!.total).toBe(0);
  437. expect(applyCouponCode!.couponCodes).toEqual([TEST_COUPON_CODE]);
  438. await proceedToArrangingPayment(shopClient);
  439. const order = await addPaymentToOrder(shopClient, testSuccessfulPaymentMethod);
  440. expect(order.state).toBe('PaymentSettled');
  441. expect(order.active).toBe(false);
  442. orderCode = order.code;
  443. });
  444. it('adds Promotions to Order once payment arranged', async () => {
  445. const { orderByCode } = await shopClient.query<
  446. GetOrderPromotionsByCode.Query,
  447. GetOrderPromotionsByCode.Variables
  448. >(GET_ORDER_PROMOTIONS_BY_CODE, {
  449. code: orderCode,
  450. });
  451. expect(orderByCode!.promotions.map(pick(['id', 'name']))).toEqual([
  452. { id: 'T_9', name: 'Free with test coupon' },
  453. ]);
  454. });
  455. it('throws when usage exceeds limit', async () => {
  456. await shopClient.asAnonymousUser();
  457. await createNewActiveOrder();
  458. await addGuestCustomerToOrder();
  459. try {
  460. await shopClient.query<ApplyCouponCode.Mutation, ApplyCouponCode.Variables>(
  461. APPLY_COUPON_CODE,
  462. { couponCode: TEST_COUPON_CODE },
  463. );
  464. fail('should have thrown');
  465. } catch (err) {
  466. expect(err.message).toEqual(
  467. expect.stringContaining('Coupon code cannot be used more than once per customer'),
  468. );
  469. }
  470. });
  471. it('removes couponCode from order when adding customer after code applied', async () => {
  472. await shopClient.asAnonymousUser();
  473. await createNewActiveOrder();
  474. const { applyCouponCode } = await shopClient.query<
  475. ApplyCouponCode.Mutation,
  476. ApplyCouponCode.Variables
  477. >(APPLY_COUPON_CODE, { couponCode: TEST_COUPON_CODE });
  478. expect(applyCouponCode!.total).toBe(0);
  479. expect(applyCouponCode!.couponCodes).toEqual([TEST_COUPON_CODE]);
  480. await addGuestCustomerToOrder();
  481. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  482. expect(activeOrder!.couponCodes).toEqual([]);
  483. expect(activeOrder!.total).toBe(6000);
  484. });
  485. });
  486. describe('signed-in customer', () => {
  487. function logInAsRegisteredCustomer() {
  488. return shopClient.asUserWithCredentials('hayden.zieme12@hotmail.com', 'test');
  489. }
  490. it('allows initial usage', async () => {
  491. await logInAsRegisteredCustomer();
  492. await createNewActiveOrder();
  493. const { applyCouponCode } = await shopClient.query<
  494. ApplyCouponCode.Mutation,
  495. ApplyCouponCode.Variables
  496. >(APPLY_COUPON_CODE, { couponCode: TEST_COUPON_CODE });
  497. expect(applyCouponCode!.total).toBe(0);
  498. expect(applyCouponCode!.couponCodes).toEqual([TEST_COUPON_CODE]);
  499. await proceedToArrangingPayment(shopClient);
  500. const order = await addPaymentToOrder(shopClient, testSuccessfulPaymentMethod);
  501. expect(order.state).toBe('PaymentSettled');
  502. expect(order.active).toBe(false);
  503. });
  504. it('throws when usage exceeds limit', async () => {
  505. await logInAsRegisteredCustomer();
  506. await createNewActiveOrder();
  507. try {
  508. await shopClient.query<ApplyCouponCode.Mutation, ApplyCouponCode.Variables>(
  509. APPLY_COUPON_CODE,
  510. { couponCode: TEST_COUPON_CODE },
  511. );
  512. fail('should have thrown');
  513. } catch (err) {
  514. expect(err.message).toEqual(
  515. expect.stringContaining('Coupon code cannot be used more than once per customer'),
  516. );
  517. }
  518. });
  519. it('removes couponCode from order when logging in after code applied', async () => {
  520. await shopClient.asAnonymousUser();
  521. await createNewActiveOrder();
  522. const { applyCouponCode } = await shopClient.query<
  523. ApplyCouponCode.Mutation,
  524. ApplyCouponCode.Variables
  525. >(APPLY_COUPON_CODE, { couponCode: TEST_COUPON_CODE });
  526. expect(applyCouponCode!.couponCodes).toEqual([TEST_COUPON_CODE]);
  527. expect(applyCouponCode!.total).toBe(0);
  528. await logInAsRegisteredCustomer();
  529. const { activeOrder } = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
  530. expect(activeOrder!.total).toBe(6000);
  531. expect(activeOrder!.couponCodes).toEqual([]);
  532. });
  533. });
  534. });
  535. async function getProducts() {
  536. const result = await adminClient.query<GetPromoProducts.Query>(GET_PROMO_PRODUCTS, {
  537. options: {
  538. take: 10,
  539. skip: 0,
  540. },
  541. });
  542. products = result.products.items;
  543. }
  544. async function createGlobalPromotions() {
  545. const { facets } = await adminClient.query<GetFacetList.Query>(GET_FACET_LIST);
  546. const saleFacetValue = facets.items[0].values[0];
  547. await createPromotion({
  548. enabled: true,
  549. name: 'Promo not yet started',
  550. startsAt: new Date(2199, 0, 0),
  551. conditions: [minOrderAmountCondition(100)],
  552. actions: [freeOrderAction],
  553. });
  554. const deletedPromotion = await createPromotion({
  555. enabled: true,
  556. name: 'Deleted promotion',
  557. conditions: [minOrderAmountCondition(100)],
  558. actions: [freeOrderAction],
  559. });
  560. await deletePromotion(deletedPromotion.id);
  561. }
  562. async function createPromotion(input: CreatePromotionInput): Promise<CreatePromotion.CreatePromotion> {
  563. const result = await adminClient.query<CreatePromotion.Mutation, CreatePromotion.Variables>(
  564. CREATE_PROMOTION,
  565. {
  566. input,
  567. },
  568. );
  569. return result.createPromotion;
  570. }
  571. function getVariantBySlug(
  572. slug: 'item-1' | 'item-12' | 'item-60' | 'item-sale-1' | 'item-sale-12',
  573. ): GetPromoProducts.Variants {
  574. return products.find(p => p.slug === slug)!.variants[0];
  575. }
  576. async function deletePromotion(promotionId: string) {
  577. await adminClient.query(gql`
  578. mutation DeletePromotionAdHoc1 {
  579. deletePromotion(id: "${promotionId}") {
  580. result
  581. }
  582. }
  583. `);
  584. }
  585. });
  586. export const GET_PROMO_PRODUCTS = gql`
  587. query GetPromoProducts {
  588. products {
  589. items {
  590. id
  591. slug
  592. variants {
  593. id
  594. price
  595. priceWithTax
  596. sku
  597. facetValues {
  598. id
  599. code
  600. }
  601. }
  602. }
  603. }
  604. }
  605. `;