order.e2e-spec.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import gql from 'graphql-tag';
  2. import { GetCustomerList } from 'shared/generated-types';
  3. import { GET_CUSTOMER_LIST } from '../../admin-ui/src/app/data/definitions/customer-definitions';
  4. import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
  5. import { TestClient } from './test-client';
  6. import { TestServer } from './test-server';
  7. describe('Orders', () => {
  8. const client = new TestClient();
  9. const server = new TestServer();
  10. beforeAll(async () => {
  11. const token = await server.init({
  12. productCount: 10,
  13. customerCount: 1,
  14. });
  15. await client.init();
  16. }, TEST_SETUP_TIMEOUT_MS);
  17. afterAll(async () => {
  18. await server.destroy();
  19. });
  20. describe('as anonymous user', () => {
  21. let firstOrderItemId: string;
  22. beforeAll(async () => {
  23. await client.asAnonymousUser();
  24. });
  25. it('addItemToOrder() starts with no session token', () => {
  26. expect(client.getAuthToken()).toBe('');
  27. });
  28. it('activeOrder() returns null before any items have been added', async () => {
  29. const result = await client.query(GET_ACTIVE_ORDER);
  30. expect(result.activeOrder).toBeNull();
  31. });
  32. it('activeOrder() creates an anonymous session', () => {
  33. expect(client.getAuthToken()).not.toBe('');
  34. });
  35. it('addItemToOrder() creates a new Order with an item', async () => {
  36. const result = await client.query(ADD_ITEM_TO_ORDER, {
  37. productVariantId: 'T_1',
  38. quantity: 1,
  39. });
  40. expect(result.addItemToOrder.lines.length).toBe(1);
  41. expect(result.addItemToOrder.lines[0].quantity).toBe(1);
  42. expect(result.addItemToOrder.lines[0].productVariant.id).toBe('T_1');
  43. expect(result.addItemToOrder.lines[0].id).toBe('T_1');
  44. firstOrderItemId = result.addItemToOrder.lines[0].id;
  45. });
  46. it('addItemToOrder() errors with an invalid productVariantId', async () => {
  47. try {
  48. await client.query(ADD_ITEM_TO_ORDER, {
  49. productVariantId: 'T_999',
  50. quantity: 1,
  51. });
  52. fail('Should have thrown');
  53. } catch (err) {
  54. expect(err.message).toEqual(
  55. expect.stringContaining(`No ProductVariant with the id '999' could be found`),
  56. );
  57. }
  58. });
  59. it('addItemToOrder() errors with a negative quantity', async () => {
  60. try {
  61. await client.query(ADD_ITEM_TO_ORDER, {
  62. productVariantId: 'T_999',
  63. quantity: -3,
  64. });
  65. fail('Should have thrown');
  66. } catch (err) {
  67. expect(err.message).toEqual(
  68. expect.stringContaining(`-3 is not a valid quantity for an OrderItem`),
  69. );
  70. }
  71. });
  72. it('addItemToOrder() with an existing productVariantId adds quantity to the existing OrderLine', async () => {
  73. const result = await client.query(ADD_ITEM_TO_ORDER, {
  74. productVariantId: 'T_1',
  75. quantity: 2,
  76. });
  77. expect(result.addItemToOrder.lines.length).toBe(1);
  78. expect(result.addItemToOrder.lines[0].quantity).toBe(3);
  79. });
  80. it('adjustItemQuantity() adjusts the quantity', async () => {
  81. const result = await client.query(ADJUST_ITEM_QUENTITY, {
  82. orderItemId: firstOrderItemId,
  83. quantity: 50,
  84. });
  85. expect(result.adjustItemQuantity.lines.length).toBe(1);
  86. expect(result.adjustItemQuantity.lines[0].quantity).toBe(50);
  87. });
  88. it('adjustItemQuantity() errors with a negative quantity', async () => {
  89. try {
  90. await client.query(ADJUST_ITEM_QUENTITY, {
  91. orderItemId: firstOrderItemId,
  92. quantity: -3,
  93. });
  94. fail('Should have thrown');
  95. } catch (err) {
  96. expect(err.message).toEqual(
  97. expect.stringContaining(`-3 is not a valid quantity for an OrderItem`),
  98. );
  99. }
  100. });
  101. it('adjustItemQuantity() errors with an invalid orderItemId', async () => {
  102. try {
  103. await client.query(ADJUST_ITEM_QUENTITY, {
  104. orderItemId: 'T_999',
  105. quantity: 5,
  106. });
  107. fail('Should have thrown');
  108. } catch (err) {
  109. expect(err.message).toEqual(
  110. expect.stringContaining(`This order does not contain an OrderLine with the id 999`),
  111. );
  112. }
  113. });
  114. it('removeItemFromOrder() removes the correct item', async () => {
  115. const result1 = await client.query(ADD_ITEM_TO_ORDER, {
  116. productVariantId: 'T_3',
  117. quantity: 3,
  118. });
  119. expect(result1.addItemToOrder.lines.length).toBe(2);
  120. expect(result1.addItemToOrder.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  121. const result2 = await client.query(REMOVE_ITEM_FROM_ORDER, {
  122. orderItemId: firstOrderItemId,
  123. });
  124. expect(result2.removeItemFromOrder.lines.length).toBe(1);
  125. expect(result2.removeItemFromOrder.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
  126. });
  127. it('removeItemFromOrder() errors with an invalid orderItemId', async () => {
  128. try {
  129. await client.query(REMOVE_ITEM_FROM_ORDER, {
  130. orderItemId: 'T_999',
  131. });
  132. fail('Should have thrown');
  133. } catch (err) {
  134. expect(err.message).toEqual(
  135. expect.stringContaining(`This order does not contain an OrderLine with the id 999`),
  136. );
  137. }
  138. });
  139. it('nextOrderStates() returns next valid states', async () => {
  140. const result = await client.query(gql`
  141. query {
  142. nextOrderStates
  143. }
  144. `);
  145. expect(result.nextOrderStates).toEqual(['ArrangingPayment']);
  146. });
  147. it('transitionOrderToState() throws for an invalid state', async () => {
  148. try {
  149. await client.query(gql`
  150. mutation {
  151. transitionOrderToState(state: "Completed") {
  152. id
  153. state
  154. }
  155. }
  156. `);
  157. fail('Should have thrown');
  158. } catch (err) {
  159. expect(err.message).toEqual(
  160. expect.stringContaining(`Cannot transition Order from "AddingItems" to "Completed"`),
  161. );
  162. }
  163. });
  164. it('transitionOrderToState() transitions Order to the next valid state', async () => {
  165. const result = await client.query(gql`
  166. mutation {
  167. transitionOrderToState(state: "ArrangingPayment") {
  168. id
  169. state
  170. }
  171. }
  172. `);
  173. expect(result.transitionOrderToState).toEqual({ id: 'T_1', state: 'ArrangingPayment' });
  174. });
  175. });
  176. describe('as authenticated user', () => {
  177. let firstOrderItemId: string;
  178. let activeOrderId: string;
  179. let authenticatedUserEmailAddress: string;
  180. const password = 'test';
  181. beforeAll(async () => {
  182. await client.asSuperAdmin();
  183. const result = await client.query<GetCustomerList.Query, GetCustomerList.Variables>(
  184. GET_CUSTOMER_LIST,
  185. {
  186. options: {
  187. take: 1,
  188. },
  189. },
  190. );
  191. const customer = result.customers.items[0];
  192. authenticatedUserEmailAddress = customer.emailAddress;
  193. await client.asUserWithCredentials(authenticatedUserEmailAddress, password);
  194. });
  195. it('activeOrder() returns null before any items have been added', async () => {
  196. const result = await client.query(GET_ACTIVE_ORDER);
  197. expect(result.activeOrder).toBeNull();
  198. });
  199. it('addItemToOrder() creates a new Order with an item', async () => {
  200. const result = await client.query(ADD_ITEM_TO_ORDER, {
  201. productVariantId: 'T_1',
  202. quantity: 1,
  203. });
  204. expect(result.addItemToOrder.lines.length).toBe(1);
  205. expect(result.addItemToOrder.lines[0].quantity).toBe(1);
  206. expect(result.addItemToOrder.lines[0].productVariant.id).toBe('T_1');
  207. activeOrderId = result.addItemToOrder.id;
  208. firstOrderItemId = result.addItemToOrder.lines[0].id;
  209. });
  210. it('addItemToOrder() with an existing productVariantId adds quantity to the existing OrderLine', async () => {
  211. const result = await client.query(ADD_ITEM_TO_ORDER, {
  212. productVariantId: 'T_1',
  213. quantity: 2,
  214. });
  215. expect(result.addItemToOrder.lines.length).toBe(1);
  216. expect(result.addItemToOrder.lines[0].quantity).toBe(3);
  217. });
  218. it('adjustItemQuantity() adjusts the quantity', async () => {
  219. const result = await client.query(ADJUST_ITEM_QUENTITY, {
  220. orderItemId: firstOrderItemId,
  221. quantity: 50,
  222. });
  223. expect(result.adjustItemQuantity.lines.length).toBe(1);
  224. expect(result.adjustItemQuantity.lines[0].quantity).toBe(50);
  225. });
  226. it('removeItemFromOrder() removes the correct item', async () => {
  227. const result1 = await client.query(ADD_ITEM_TO_ORDER, {
  228. productVariantId: 'T_3',
  229. quantity: 3,
  230. });
  231. expect(result1.addItemToOrder.lines.length).toBe(2);
  232. expect(result1.addItemToOrder.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  233. const result2 = await client.query(REMOVE_ITEM_FROM_ORDER, {
  234. orderItemId: firstOrderItemId,
  235. });
  236. expect(result2.removeItemFromOrder.lines.length).toBe(1);
  237. expect(result2.removeItemFromOrder.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
  238. });
  239. it('nextOrderStates() returns next valid states', async () => {
  240. const result = await client.query(gql`
  241. query {
  242. nextOrderStates
  243. }
  244. `);
  245. expect(result.nextOrderStates).toEqual(['ArrangingPayment']);
  246. });
  247. it('logging out and back in again resumes the last active order', async () => {
  248. await client.asAnonymousUser();
  249. const result1 = await client.query(GET_ACTIVE_ORDER);
  250. expect(result1.activeOrder).toBeNull();
  251. await client.asUserWithCredentials(authenticatedUserEmailAddress, password);
  252. const result2 = await client.query(GET_ACTIVE_ORDER);
  253. expect(result2.activeOrder.id).toBe(activeOrderId);
  254. });
  255. });
  256. });
  257. const TEST_ORDER_FRAGMENT = gql`
  258. fragment TestOrderFragment on Order {
  259. id
  260. lines {
  261. id
  262. quantity
  263. productVariant {
  264. id
  265. }
  266. }
  267. }
  268. `;
  269. const GET_ACTIVE_ORDER = gql`
  270. query {
  271. activeOrder {
  272. id
  273. state
  274. }
  275. }
  276. `;
  277. const ADD_ITEM_TO_ORDER = gql`
  278. mutation AddItemToOrder($productVariantId: ID!, $quantity: Int!) {
  279. addItemToOrder(productVariantId: $productVariantId, quantity: $quantity) {
  280. ...TestOrderFragment
  281. }
  282. }
  283. ${TEST_ORDER_FRAGMENT}
  284. `;
  285. const ADJUST_ITEM_QUENTITY = gql`
  286. mutation AdjustItemQuantity($orderItemId: ID!, $quantity: Int!) {
  287. adjustItemQuantity(orderItemId: $orderItemId, quantity: $quantity) {
  288. ...TestOrderFragment
  289. }
  290. }
  291. ${TEST_ORDER_FRAGMENT}
  292. `;
  293. const REMOVE_ITEM_FROM_ORDER = gql`
  294. mutation RemoveItemFromOrder($orderItemId: ID!) {
  295. removeItemFromOrder(orderItemId: $orderItemId) {
  296. ...TestOrderFragment
  297. }
  298. }
  299. ${TEST_ORDER_FRAGMENT}
  300. `;