order.e2e-spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import gql from 'graphql-tag';
  2. import { Customer } from '../src/entity/customer/customer.entity';
  3. import { OrderLine } from '../src/entity/order-line/order-line.entity';
  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() creates and returns a new order in the default state', async () => {
  29. const result = await client.query(GET_ACTIVE_ORDER);
  30. expect(result.activeOrder).toEqual({ id: 'T_1', state: 'AddingItems' });
  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(['ArrangingShipping']);
  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: "ArrangingShipping") {
  168. id
  169. state
  170. }
  171. }
  172. `);
  173. expect(result.transitionOrderToState).toEqual({ id: 'T_1', state: 'ArrangingShipping' });
  174. });
  175. });
  176. describe('as authenticated user', () => {
  177. let firstOrderItemId: string;
  178. beforeAll(async () => {
  179. await client.asSuperAdmin();
  180. const result = await client.query(gql`
  181. query {
  182. customer(id: "T_1") {
  183. id
  184. emailAddress
  185. }
  186. }
  187. `);
  188. const customer: Customer = result.customer;
  189. await client.asUserWithCredentials(customer.emailAddress, 'test');
  190. });
  191. it('activeOrder() creates and returns a new order in the default state', async () => {
  192. const result = await client.query(GET_ACTIVE_ORDER);
  193. expect(result.activeOrder).toEqual({ id: 'T_2', state: 'AddingItems' });
  194. });
  195. it('addItemToOrder() creates a new Order with an item', async () => {
  196. const result = await client.query(ADD_ITEM_TO_ORDER, {
  197. productVariantId: 'T_1',
  198. quantity: 1,
  199. });
  200. expect(result.addItemToOrder.lines.length).toBe(1);
  201. expect(result.addItemToOrder.lines[0].quantity).toBe(1);
  202. expect(result.addItemToOrder.lines[0].productVariant.id).toBe('T_1');
  203. firstOrderItemId = result.addItemToOrder.lines[0].id;
  204. });
  205. it('addItemToOrder() with an existing productVariantId adds quantity to the existing OrderLine', async () => {
  206. const result = await client.query(ADD_ITEM_TO_ORDER, {
  207. productVariantId: 'T_1',
  208. quantity: 2,
  209. });
  210. expect(result.addItemToOrder.lines.length).toBe(1);
  211. expect(result.addItemToOrder.lines[0].quantity).toBe(3);
  212. });
  213. it('adjustItemQuantity() adjusts the quantity', async () => {
  214. const result = await client.query(ADJUST_ITEM_QUENTITY, {
  215. orderItemId: firstOrderItemId,
  216. quantity: 50,
  217. });
  218. expect(result.adjustItemQuantity.lines.length).toBe(1);
  219. expect(result.adjustItemQuantity.lines[0].quantity).toBe(50);
  220. });
  221. it('removeItemFromOrder() removes the correct item', async () => {
  222. const result1 = await client.query(ADD_ITEM_TO_ORDER, {
  223. productVariantId: 'T_3',
  224. quantity: 3,
  225. });
  226. expect(result1.addItemToOrder.lines.length).toBe(2);
  227. expect(result1.addItemToOrder.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
  228. const result2 = await client.query(REMOVE_ITEM_FROM_ORDER, {
  229. orderItemId: firstOrderItemId,
  230. });
  231. expect(result2.removeItemFromOrder.lines.length).toBe(1);
  232. expect(result2.removeItemFromOrder.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
  233. });
  234. it('nextOrderStates() returns next valid states', async () => {
  235. const result = await client.query(gql`
  236. query {
  237. nextOrderStates
  238. }
  239. `);
  240. expect(result.nextOrderStates).toEqual(['ArrangingShipping']);
  241. });
  242. });
  243. });
  244. const TEST_ORDER_FRAGMENT = gql`
  245. fragment TestOrderFragment on Order {
  246. id
  247. lines {
  248. id
  249. quantity
  250. productVariant {
  251. id
  252. }
  253. }
  254. }
  255. `;
  256. const GET_ACTIVE_ORDER = gql`
  257. query {
  258. activeOrder {
  259. id
  260. state
  261. }
  262. }
  263. `;
  264. const ADD_ITEM_TO_ORDER = gql`
  265. mutation AddItemToOrder($productVariantId: ID!, $quantity: Int!) {
  266. addItemToOrder(productVariantId: $productVariantId, quantity: $quantity) {
  267. ...TestOrderFragment
  268. }
  269. }
  270. ${TEST_ORDER_FRAGMENT}
  271. `;
  272. const ADJUST_ITEM_QUENTITY = gql`
  273. mutation AdjustItemQuantity($orderItemId: ID!, $quantity: Int!) {
  274. adjustItemQuantity(orderItemId: $orderItemId, quantity: $quantity) {
  275. ...TestOrderFragment
  276. }
  277. }
  278. ${TEST_ORDER_FRAGMENT}
  279. `;
  280. const REMOVE_ITEM_FROM_ORDER = gql`
  281. mutation RemoveItemFromOrder($orderItemId: ID!) {
  282. removeItemFromOrder(orderItemId: $orderItemId) {
  283. ...TestOrderFragment
  284. }
  285. }
  286. ${TEST_ORDER_FRAGMENT}
  287. `;