shop-customer.e2e-spec.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /* tslint:disable:no-non-null-assertion */
  2. import { pick } from '@vendure/common/lib/pick';
  3. import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
  4. import gql from 'graphql-tag';
  5. import path from 'path';
  6. import { skip } from 'rxjs/operators';
  7. import { initialData } from '../../../e2e-common/e2e-initial-data';
  8. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  9. import {
  10. AttemptLogin,
  11. GetCustomer,
  12. GetCustomerHistory,
  13. GetCustomerIds,
  14. HistoryEntryType,
  15. } from './graphql/generated-e2e-admin-types';
  16. import {
  17. CreateAddressInput,
  18. CreateAddressShop,
  19. DeleteAddressShop,
  20. ErrorCode,
  21. UpdateAddressInput,
  22. UpdateAddressShop,
  23. UpdateCustomer,
  24. UpdateCustomerInput,
  25. UpdatePassword,
  26. } from './graphql/generated-e2e-shop-types';
  27. import { ATTEMPT_LOGIN, GET_CUSTOMER, GET_CUSTOMER_HISTORY } from './graphql/shared-definitions';
  28. import {
  29. CREATE_ADDRESS,
  30. DELETE_ADDRESS,
  31. UPDATE_ADDRESS,
  32. UPDATE_CUSTOMER,
  33. UPDATE_PASSWORD,
  34. } from './graphql/shop-definitions';
  35. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  36. describe('Shop customers', () => {
  37. const { server, adminClient, shopClient } = createTestEnvironment(testConfig());
  38. let customer: GetCustomer.Customer;
  39. const successErrorGuard: ErrorResultGuard<{ success: boolean }> = createErrorResultGuard(
  40. input => input.success != null,
  41. );
  42. beforeAll(async () => {
  43. await server.init({
  44. initialData,
  45. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  46. customerCount: 2,
  47. });
  48. await adminClient.asSuperAdmin();
  49. // Fetch the first Customer and store it as the `customer` variable.
  50. const { customers } = await adminClient.query<GetCustomerIds.Query>(gql`
  51. query GetCustomerIds {
  52. customers {
  53. items {
  54. id
  55. }
  56. }
  57. }
  58. `);
  59. const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
  60. id: customers.items[0].id,
  61. });
  62. customer = result.customer!;
  63. }, TEST_SETUP_TIMEOUT_MS);
  64. afterAll(async () => {
  65. await server.destroy();
  66. });
  67. it(
  68. 'updateCustomer throws if not logged in',
  69. assertThrowsWithMessage(async () => {
  70. const input: UpdateCustomerInput = {
  71. firstName: 'xyz',
  72. };
  73. await shopClient.query<UpdateCustomer.Mutation, UpdateCustomer.Variables>(UPDATE_CUSTOMER, {
  74. input,
  75. });
  76. }, 'You are not currently authorized to perform this action'),
  77. );
  78. it(
  79. 'createCustomerAddress throws if not logged in',
  80. assertThrowsWithMessage(async () => {
  81. const input: CreateAddressInput = {
  82. streetLine1: '1 Test Street',
  83. countryCode: 'GB',
  84. };
  85. await shopClient.query<CreateAddressShop.Mutation, CreateAddressShop.Variables>(CREATE_ADDRESS, {
  86. input,
  87. });
  88. }, 'You are not currently authorized to perform this action'),
  89. );
  90. it(
  91. 'updateCustomerAddress throws if not logged in',
  92. assertThrowsWithMessage(async () => {
  93. const input: UpdateAddressInput = {
  94. id: 'T_1',
  95. streetLine1: 'zxc',
  96. };
  97. await shopClient.query<UpdateAddressShop.Mutation, UpdateAddressShop.Variables>(UPDATE_ADDRESS, {
  98. input,
  99. });
  100. }, 'You are not currently authorized to perform this action'),
  101. );
  102. it(
  103. 'deleteCustomerAddress throws if not logged in',
  104. assertThrowsWithMessage(async () => {
  105. await shopClient.query<DeleteAddressShop.Mutation, DeleteAddressShop.Variables>(DELETE_ADDRESS, {
  106. id: 'T_1',
  107. });
  108. }, 'You are not currently authorized to perform this action'),
  109. );
  110. describe('logged in Customer', () => {
  111. let addressId: string;
  112. beforeAll(async () => {
  113. await shopClient.query<AttemptLogin.Mutation, AttemptLogin.Variables>(ATTEMPT_LOGIN, {
  114. username: customer.emailAddress,
  115. password: 'test',
  116. rememberMe: false,
  117. });
  118. });
  119. it('updateCustomer works', async () => {
  120. const input: UpdateCustomerInput = {
  121. firstName: 'xyz',
  122. };
  123. const result = await shopClient.query<UpdateCustomer.Mutation, UpdateCustomer.Variables>(
  124. UPDATE_CUSTOMER,
  125. { input },
  126. );
  127. expect(result.updateCustomer.firstName).toBe('xyz');
  128. });
  129. it('customer history for CUSTOMER_DETAIL_UPDATED', async () => {
  130. const result = await adminClient.query<GetCustomerHistory.Query, GetCustomerHistory.Variables>(
  131. GET_CUSTOMER_HISTORY,
  132. {
  133. id: customer.id,
  134. options: {
  135. // skip populated CUSTOMER_ADDRESS_CREATED entry
  136. skip: 3,
  137. },
  138. },
  139. );
  140. expect(result.customer?.history.items.map(pick(['type', 'data']))).toEqual([
  141. {
  142. type: HistoryEntryType.CUSTOMER_DETAIL_UPDATED,
  143. data: {
  144. input: { firstName: 'xyz', id: 'T_1' },
  145. },
  146. },
  147. ]);
  148. });
  149. it('createCustomerAddress works', async () => {
  150. const input: CreateAddressInput = {
  151. streetLine1: '1 Test Street',
  152. countryCode: 'GB',
  153. };
  154. const { createCustomerAddress } = await shopClient.query<
  155. CreateAddressShop.Mutation,
  156. CreateAddressShop.Variables
  157. >(CREATE_ADDRESS, { input });
  158. expect(createCustomerAddress).toEqual({
  159. id: 'T_3',
  160. streetLine1: '1 Test Street',
  161. country: {
  162. code: 'GB',
  163. },
  164. });
  165. addressId = createCustomerAddress.id;
  166. });
  167. it('customer history for CUSTOMER_ADDRESS_CREATED', async () => {
  168. const result = await adminClient.query<GetCustomerHistory.Query, GetCustomerHistory.Variables>(
  169. GET_CUSTOMER_HISTORY,
  170. {
  171. id: customer.id,
  172. options: {
  173. // skip populated CUSTOMER_ADDRESS_CREATED, CUSTOMER_DETAIL_UPDATED entries
  174. skip: 4,
  175. },
  176. },
  177. );
  178. expect(result.customer?.history.items.map(pick(['type', 'data']))).toEqual([
  179. {
  180. type: HistoryEntryType.CUSTOMER_ADDRESS_CREATED,
  181. data: {
  182. address: '1 Test Street, United Kingdom',
  183. },
  184. },
  185. ]);
  186. });
  187. it('updateCustomerAddress works', async () => {
  188. const input: UpdateAddressInput = {
  189. id: addressId,
  190. streetLine1: '5 Test Street',
  191. countryCode: 'AT',
  192. };
  193. const result = await shopClient.query<UpdateAddressShop.Mutation, UpdateAddressShop.Variables>(
  194. UPDATE_ADDRESS,
  195. { input },
  196. );
  197. expect(result.updateCustomerAddress.streetLine1).toEqual('5 Test Street');
  198. expect(result.updateCustomerAddress.country.code).toEqual('AT');
  199. });
  200. it('customer history for CUSTOMER_ADDRESS_UPDATED', async () => {
  201. const result = await adminClient.query<GetCustomerHistory.Query, GetCustomerHistory.Variables>(
  202. GET_CUSTOMER_HISTORY,
  203. { id: customer.id, options: { skip: 5 } },
  204. );
  205. expect(result.customer?.history.items.map(pick(['type', 'data']))).toEqual([
  206. {
  207. type: HistoryEntryType.CUSTOMER_ADDRESS_UPDATED,
  208. data: {
  209. address: '5 Test Street, Austria',
  210. input: {
  211. id: addressId,
  212. streetLine1: '5 Test Street',
  213. countryCode: 'AT',
  214. },
  215. },
  216. },
  217. ]);
  218. });
  219. it(
  220. 'updateCustomerAddress fails for address not owned by Customer',
  221. assertThrowsWithMessage(async () => {
  222. const input: UpdateAddressInput = {
  223. id: 'T_2',
  224. streetLine1: '1 Test Street',
  225. };
  226. await shopClient.query<UpdateAddressShop.Mutation, UpdateAddressShop.Variables>(
  227. UPDATE_ADDRESS,
  228. { input },
  229. );
  230. }, 'You are not currently authorized to perform this action'),
  231. );
  232. it('deleteCustomerAddress works', async () => {
  233. const { deleteCustomerAddress } = await shopClient.query<
  234. DeleteAddressShop.Mutation,
  235. DeleteAddressShop.Variables
  236. >(DELETE_ADDRESS, { id: 'T_3' });
  237. expect(deleteCustomerAddress.success).toBe(true);
  238. });
  239. it('customer history for CUSTOMER_ADDRESS_DELETED', async () => {
  240. const result = await adminClient.query<GetCustomerHistory.Query, GetCustomerHistory.Variables>(
  241. GET_CUSTOMER_HISTORY,
  242. { id: customer.id, options: { skip: 6 } },
  243. );
  244. expect(result.customer?.history.items.map(pick(['type', 'data']))).toEqual([
  245. {
  246. type: HistoryEntryType.CUSTOMER_ADDRESS_DELETED,
  247. data: {
  248. address: '5 Test Street, Austria',
  249. },
  250. },
  251. ]);
  252. });
  253. it(
  254. 'deleteCustomerAddress fails for address not owned by Customer',
  255. assertThrowsWithMessage(async () => {
  256. await shopClient.query<DeleteAddressShop.Mutation, DeleteAddressShop.Variables>(
  257. DELETE_ADDRESS,
  258. { id: 'T_2' },
  259. );
  260. }, 'You are not currently authorized to perform this action'),
  261. );
  262. it('updatePassword return error result with incorrect current password', async () => {
  263. const { updateCustomerPassword } = await shopClient.query<
  264. UpdatePassword.Mutation,
  265. UpdatePassword.Variables
  266. >(UPDATE_PASSWORD, {
  267. old: 'wrong',
  268. new: 'test2',
  269. });
  270. successErrorGuard.assertErrorResult(updateCustomerPassword);
  271. expect(updateCustomerPassword.message).toBe('The provided credentials are invalid');
  272. expect(updateCustomerPassword.errorCode).toBe(ErrorCode.INVALID_CREDENTIALS_ERROR);
  273. });
  274. it('updatePassword works', async () => {
  275. const { updateCustomerPassword } = await shopClient.query<
  276. UpdatePassword.Mutation,
  277. UpdatePassword.Variables
  278. >(UPDATE_PASSWORD, { old: 'test', new: 'test2' });
  279. successErrorGuard.assertSuccess(updateCustomerPassword);
  280. expect(updateCustomerPassword.success).toBe(true);
  281. // Log out and log in with new password
  282. const loginResult = await shopClient.asUserWithCredentials(customer.emailAddress, 'test2');
  283. expect(loginResult.identifier).toBe(customer.emailAddress);
  284. });
  285. it('customer history for CUSTOMER_PASSWORD_UPDATED', async () => {
  286. const result = await adminClient.query<GetCustomerHistory.Query, GetCustomerHistory.Variables>(
  287. GET_CUSTOMER_HISTORY,
  288. { id: customer.id, options: { skip: 7 } },
  289. );
  290. expect(result.customer?.history.items.map(pick(['type', 'data']))).toEqual([
  291. {
  292. type: HistoryEntryType.CUSTOMER_PASSWORD_UPDATED,
  293. data: {},
  294. },
  295. ]);
  296. });
  297. });
  298. });