active-order-strategy.e2e-spec.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. import { DefaultLogger, mergeConfig, orderPercentageDiscount } from '@vendure/core';
  2. import { createTestEnvironment } from '@vendure/testing';
  3. import gql from 'graphql-tag';
  4. import path from 'path';
  5. import { initialData } from '../../../e2e-common/e2e-initial-data';
  6. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  7. import { testSuccessfulPaymentMethod } from './fixtures/test-payment-methods';
  8. import { TokenActiveOrderPlugin } from './fixtures/test-plugins/token-active-order-plugin';
  9. import {
  10. CreatePromotionMutation,
  11. CreatePromotionMutationVariables,
  12. GetCustomerListQuery,
  13. } from './graphql/generated-e2e-admin-types';
  14. import {
  15. AddItemToOrderMutation,
  16. AddItemToOrderMutationVariables,
  17. GetActiveOrderQuery,
  18. } from './graphql/generated-e2e-shop-types';
  19. import { CREATE_PROMOTION, GET_CUSTOMER_LIST } from './graphql/shared-definitions';
  20. import { ADD_ITEM_TO_ORDER, GET_ACTIVE_ORDER } from './graphql/shop-definitions';
  21. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  22. describe('custom ActiveOrderStrategy', () => {
  23. const { server, adminClient, shopClient } = createTestEnvironment(
  24. mergeConfig(testConfig(), {
  25. logger: new DefaultLogger(),
  26. plugins: [TokenActiveOrderPlugin],
  27. paymentOptions: {
  28. paymentMethodHandlers: [testSuccessfulPaymentMethod],
  29. },
  30. customFields: {
  31. Order: [
  32. {
  33. name: 'message',
  34. type: 'string',
  35. nullable: true,
  36. },
  37. ],
  38. },
  39. }),
  40. );
  41. let customers: GetCustomerListQuery['customers']['items'];
  42. beforeAll(async () => {
  43. await server.init({
  44. initialData: {
  45. ...initialData,
  46. paymentMethods: [
  47. {
  48. name: testSuccessfulPaymentMethod.code,
  49. handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
  50. },
  51. ],
  52. },
  53. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  54. customerCount: 3,
  55. });
  56. await adminClient.asSuperAdmin();
  57. const result = await adminClient.query<GetCustomerListQuery>(GET_CUSTOMER_LIST);
  58. customers = result.customers.items;
  59. }, TEST_SETUP_TIMEOUT_MS);
  60. afterAll(async () => {
  61. await server.destroy();
  62. });
  63. it('activeOrder with no createActiveOrder defined returns null', async () => {
  64. const { activeOrder } = await shopClient.query<GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  65. expect(activeOrder).toBeNull();
  66. });
  67. it(
  68. 'addItemToOrder with no createActiveOrder throws',
  69. assertThrowsWithMessage(async () => {
  70. await shopClient.query<AddItemToOrderMutation, AddItemToOrderMutationVariables>(
  71. ADD_ITEM_TO_ORDER,
  72. {
  73. productVariantId: 'T_1',
  74. quantity: 1,
  75. },
  76. );
  77. }, 'No active Order could be determined nor created'),
  78. );
  79. it('activeOrder with valid input', async () => {
  80. const { createOrder } = await shopClient.query(gql`
  81. mutation CreateCustomOrder {
  82. createOrder(customerId: "${customers[1].id}") {
  83. id
  84. orderToken
  85. }
  86. }
  87. `);
  88. expect(createOrder).toEqual({
  89. id: 'T_1',
  90. orderToken: 'token-2',
  91. });
  92. await shopClient.asUserWithCredentials(customers[1].emailAddress, 'test');
  93. const { activeOrder } = await shopClient.query(ACTIVE_ORDER_BY_TOKEN, {
  94. input: {
  95. orderToken: { token: 'token-2' },
  96. },
  97. });
  98. expect(activeOrder).toEqual({
  99. id: 'T_1',
  100. orderToken: 'token-2',
  101. });
  102. });
  103. it('activeOrder with invalid input', async () => {
  104. await shopClient.asUserWithCredentials(customers[1].emailAddress, 'test');
  105. const { activeOrder } = await shopClient.query(ACTIVE_ORDER_BY_TOKEN, {
  106. input: {
  107. orderToken: { token: 'invalid' },
  108. },
  109. });
  110. expect(activeOrder).toBeNull();
  111. });
  112. it('activeOrder with invalid condition', async () => {
  113. // wrong customer logged in
  114. await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
  115. const { activeOrder } = await shopClient.query(ACTIVE_ORDER_BY_TOKEN, {
  116. input: {
  117. orderToken: { token: 'token-2' },
  118. },
  119. });
  120. expect(activeOrder).toBeNull();
  121. });
  122. describe('happy path', () => {
  123. const activeOrderInput = `activeOrderInput: { orderToken: { token: "token-2" } }`;
  124. const TEST_COUPON_CODE = 'TESTCOUPON';
  125. let firstOrderLineId: string;
  126. beforeAll(async () => {
  127. await shopClient.asUserWithCredentials(customers[1].emailAddress, 'test');
  128. const result = await adminClient.query<CreatePromotionMutation, CreatePromotionMutationVariables>(
  129. CREATE_PROMOTION,
  130. {
  131. input: {
  132. enabled: true,
  133. name: 'Free with test coupon',
  134. couponCode: TEST_COUPON_CODE,
  135. conditions: [],
  136. actions: [
  137. {
  138. code: orderPercentageDiscount.code,
  139. arguments: [{ name: 'discount', value: '100' }],
  140. },
  141. ],
  142. },
  143. },
  144. );
  145. });
  146. it('addItemToOrder', async () => {
  147. const { addItemToOrder } = await shopClient.query(gql`
  148. mutation {
  149. addItemToOrder(productVariantId: "T_1", quantity: 1, ${activeOrderInput}) {
  150. ...on Order {
  151. id
  152. orderToken
  153. lines {
  154. id
  155. productVariant { id }
  156. }
  157. }
  158. }
  159. }
  160. `);
  161. expect(addItemToOrder).toEqual({
  162. id: 'T_1',
  163. orderToken: 'token-2',
  164. lines: [
  165. {
  166. id: 'T_1',
  167. productVariant: { id: 'T_1' },
  168. },
  169. ],
  170. });
  171. firstOrderLineId = addItemToOrder.lines[0].id;
  172. });
  173. it('adjustOrderLine', async () => {
  174. const { adjustOrderLine } = await shopClient.query(gql`
  175. mutation {
  176. adjustOrderLine(orderLineId: "${firstOrderLineId}", quantity: 2, ${activeOrderInput}) {
  177. ...on Order {
  178. id
  179. orderToken
  180. lines {
  181. quantity
  182. productVariant { id }
  183. }
  184. }
  185. }
  186. }
  187. `);
  188. expect(adjustOrderLine).toEqual({
  189. id: 'T_1',
  190. orderToken: 'token-2',
  191. lines: [
  192. {
  193. quantity: 2,
  194. productVariant: { id: 'T_1' },
  195. },
  196. ],
  197. });
  198. });
  199. it('removeOrderLine', async () => {
  200. const { removeOrderLine } = await shopClient.query(gql`
  201. mutation {
  202. removeOrderLine(orderLineId: "${firstOrderLineId}", ${activeOrderInput}) {
  203. ...on Order {
  204. id
  205. orderToken
  206. lines {
  207. id
  208. }
  209. }
  210. }
  211. }
  212. `);
  213. expect(removeOrderLine).toEqual({
  214. id: 'T_1',
  215. orderToken: 'token-2',
  216. lines: [],
  217. });
  218. });
  219. it('removeAllOrderLines', async () => {
  220. const { addItemToOrder } = await shopClient.query(gql`
  221. mutation {
  222. addItemToOrder(productVariantId: "T_1", quantity: 1, ${activeOrderInput}) {
  223. ...on Order { lines { id } }
  224. }
  225. }
  226. `);
  227. expect(addItemToOrder.lines.length).toBe(1);
  228. const { removeAllOrderLines } = await shopClient.query(gql`
  229. mutation {
  230. removeAllOrderLines(${activeOrderInput}) {
  231. ...on Order {
  232. id
  233. orderToken
  234. lines { id }
  235. }
  236. }
  237. }
  238. `);
  239. expect(removeAllOrderLines.lines.length).toBe(0);
  240. });
  241. it('applyCouponCode', async () => {
  242. await shopClient.query(gql`
  243. mutation {
  244. addItemToOrder(productVariantId: "T_1", quantity: 1, ${activeOrderInput}) {
  245. ...on Order { lines { id } }
  246. }
  247. }
  248. `);
  249. const { applyCouponCode } = await shopClient.query(gql`
  250. mutation {
  251. applyCouponCode(couponCode: "${TEST_COUPON_CODE}", ${activeOrderInput}) {
  252. ...on Order {
  253. id
  254. orderToken
  255. couponCodes
  256. discounts {
  257. description
  258. }
  259. }
  260. }
  261. }
  262. `);
  263. expect(applyCouponCode).toEqual({
  264. id: 'T_1',
  265. orderToken: 'token-2',
  266. couponCodes: [TEST_COUPON_CODE],
  267. discounts: [{ description: 'Free with test coupon' }],
  268. });
  269. });
  270. it('removeCouponCode', async () => {
  271. const { removeCouponCode } = await shopClient.query(gql`
  272. mutation {
  273. removeCouponCode(couponCode: "${TEST_COUPON_CODE}", ${activeOrderInput}) {
  274. ...on Order {
  275. id
  276. orderToken
  277. couponCodes
  278. discounts {
  279. description
  280. }
  281. }
  282. }
  283. }
  284. `);
  285. expect(removeCouponCode).toEqual({
  286. id: 'T_1',
  287. orderToken: 'token-2',
  288. couponCodes: [],
  289. discounts: [],
  290. });
  291. });
  292. it('setOrderShippingAddress', async () => {
  293. const { setOrderShippingAddress } = await shopClient.query(gql`
  294. mutation {
  295. setOrderShippingAddress(input: {
  296. streetLine1: "Shipping Street"
  297. countryCode: "AT"
  298. }, ${activeOrderInput}) {
  299. ...on Order {
  300. id
  301. orderToken
  302. shippingAddress {
  303. streetLine1
  304. country
  305. }
  306. }
  307. }
  308. }
  309. `);
  310. expect(setOrderShippingAddress).toEqual({
  311. id: 'T_1',
  312. orderToken: 'token-2',
  313. shippingAddress: {
  314. streetLine1: 'Shipping Street',
  315. country: 'Austria',
  316. },
  317. });
  318. });
  319. it('setOrderBillingAddress', async () => {
  320. const { setOrderBillingAddress } = await shopClient.query(gql`
  321. mutation {
  322. setOrderBillingAddress(input: {
  323. streetLine1: "Billing Street"
  324. countryCode: "AT"
  325. }, ${activeOrderInput}) {
  326. ...on Order {
  327. id
  328. orderToken
  329. billingAddress {
  330. streetLine1
  331. country
  332. }
  333. }
  334. }
  335. }
  336. `);
  337. expect(setOrderBillingAddress).toEqual({
  338. id: 'T_1',
  339. orderToken: 'token-2',
  340. billingAddress: {
  341. streetLine1: 'Billing Street',
  342. country: 'Austria',
  343. },
  344. });
  345. });
  346. it('eligibleShippingMethods', async () => {
  347. const { eligibleShippingMethods } = await shopClient.query(gql`
  348. query {
  349. eligibleShippingMethods(${activeOrderInput}) {
  350. id
  351. name
  352. priceWithTax
  353. }
  354. }
  355. `);
  356. expect(eligibleShippingMethods).toEqual([
  357. {
  358. id: 'T_1',
  359. name: 'Standard Shipping',
  360. priceWithTax: 500,
  361. },
  362. {
  363. id: 'T_2',
  364. name: 'Express Shipping',
  365. priceWithTax: 1000,
  366. },
  367. ]);
  368. });
  369. it('setOrderShippingMethod', async () => {
  370. const { setOrderShippingMethod } = await shopClient.query(gql`
  371. mutation {
  372. setOrderShippingMethod(shippingMethodId: "T_1", ${activeOrderInput}) {
  373. ...on Order {
  374. id
  375. orderToken
  376. shippingLines {
  377. price
  378. }
  379. }
  380. }
  381. }
  382. `);
  383. expect(setOrderShippingMethod).toEqual({
  384. id: 'T_1',
  385. orderToken: 'token-2',
  386. shippingLines: [{ price: 500 }],
  387. });
  388. });
  389. it('setOrderCustomFields', async () => {
  390. const { setOrderCustomFields } = await shopClient.query(gql`
  391. mutation {
  392. setOrderCustomFields(input: { customFields: { message: "foo" } }, ${activeOrderInput}) {
  393. ...on Order {
  394. id
  395. orderToken
  396. customFields { message }
  397. }
  398. }
  399. }
  400. `);
  401. expect(setOrderCustomFields).toEqual({
  402. id: 'T_1',
  403. orderToken: 'token-2',
  404. customFields: { message: 'foo' },
  405. });
  406. });
  407. it('eligiblePaymentMethods', async () => {
  408. const { eligiblePaymentMethods } = await shopClient.query(gql`
  409. query {
  410. eligiblePaymentMethods(${activeOrderInput}) {
  411. id
  412. name
  413. code
  414. }
  415. }
  416. `);
  417. expect(eligiblePaymentMethods).toEqual([
  418. {
  419. id: 'T_1',
  420. name: 'test-payment-method',
  421. code: 'test-payment-method',
  422. },
  423. ]);
  424. });
  425. it('nextOrderStates', async () => {
  426. const { nextOrderStates } = await shopClient.query(gql`
  427. query {
  428. nextOrderStates(${activeOrderInput})
  429. }
  430. `);
  431. expect(nextOrderStates).toEqual(['ArrangingPayment', 'Cancelled']);
  432. });
  433. it('transitionOrderToState', async () => {
  434. const { transitionOrderToState } = await shopClient.query(gql`
  435. mutation {
  436. transitionOrderToState(state: "ArrangingPayment", ${activeOrderInput}) {
  437. ...on Order {
  438. id
  439. orderToken
  440. state
  441. }
  442. }
  443. }
  444. `);
  445. expect(transitionOrderToState).toEqual({
  446. id: 'T_1',
  447. orderToken: 'token-2',
  448. state: 'ArrangingPayment',
  449. });
  450. });
  451. it('addPaymentToOrder', async () => {
  452. const { addPaymentToOrder } = await shopClient.query(gql`
  453. mutation {
  454. addPaymentToOrder(input: { method: "test-payment-method", metadata: {}}, ${activeOrderInput}) {
  455. ...on Order {
  456. id
  457. orderToken
  458. state
  459. payments {
  460. state
  461. }
  462. }
  463. }
  464. }
  465. `);
  466. expect(addPaymentToOrder).toEqual({
  467. id: 'T_1',
  468. orderToken: 'token-2',
  469. payments: [
  470. {
  471. state: 'Settled',
  472. },
  473. ],
  474. state: 'PaymentSettled',
  475. });
  476. });
  477. });
  478. });
  479. export const ACTIVE_ORDER_BY_TOKEN = gql`
  480. query ActiveOrderByToken($input: ActiveOrderInput) {
  481. activeOrder(activeOrderInput: $input) {
  482. id
  483. orderToken
  484. }
  485. }
  486. `;