shop-customer.e2e-spec.ts 11 KB

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