shop-auth.e2e-spec.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. /* tslint:disable:no-non-null-assertion */
  2. import { RegisterCustomerInput } from '@vendure/common/lib/generated-shop-types';
  3. import { CreateAdministrator, CreateRole, GetCustomer, Permission } from '@vendure/common/lib/generated-types';
  4. import { pick } from '@vendure/common/lib/pick';
  5. import { DocumentNode } from 'graphql';
  6. import gql from 'graphql-tag';
  7. import path from 'path';
  8. import { CREATE_ADMINISTRATOR, CREATE_ROLE } from '../../../admin-ui/src/app/data/definitions/administrator-definitions';
  9. import { GET_CUSTOMER } from '../../../admin-ui/src/app/data/definitions/customer-definitions';
  10. import { InjectorFn, VendurePlugin } from '../src/config/vendure-plugin/vendure-plugin';
  11. import { EventBus } from '../src/event-bus/event-bus';
  12. import { AccountRegistrationEvent } from '../src/event-bus/events/account-registration-event';
  13. import { IdentifierChangeEvent } from '../src/event-bus/events/identifier-change-event';
  14. import { IdentifierChangeRequestEvent } from '../src/event-bus/events/identifier-change-request-event';
  15. import { PasswordResetEvent } from '../src/event-bus/events/password-reset-event';
  16. import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
  17. import { TestAdminClient, TestShopClient } from './test-client';
  18. import { TestServer } from './test-server';
  19. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  20. let sendEmailFn: jest.Mock;
  21. describe('Shop auth & accounts', () => {
  22. const shopClient = new TestShopClient();
  23. const adminClient = new TestAdminClient();
  24. const server = new TestServer();
  25. beforeAll(async () => {
  26. const token = await server.init(
  27. {
  28. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  29. customerCount: 2,
  30. },
  31. {
  32. plugins: [new TestEmailPlugin()],
  33. },
  34. );
  35. await shopClient.init();
  36. await adminClient.init();
  37. }, TEST_SETUP_TIMEOUT_MS);
  38. afterAll(async () => {
  39. await server.destroy();
  40. });
  41. describe('customer account creation', () => {
  42. const password = 'password';
  43. const emailAddress = 'test1@test.com';
  44. let verificationToken: string;
  45. beforeEach(() => {
  46. sendEmailFn = jest.fn();
  47. });
  48. it(
  49. 'errors if a password is provided',
  50. assertThrowsWithMessage(async () => {
  51. const input: RegisterCustomerInput = {
  52. firstName: 'Sofia',
  53. lastName: 'Green',
  54. emailAddress: 'sofia.green@test.com',
  55. password: 'test',
  56. };
  57. const result = await shopClient.query(REGISTER_ACCOUNT, { input });
  58. }, 'Do not provide a password when `authOptions.requireVerification` is set to "true"'),
  59. );
  60. it('register a new account', async () => {
  61. const verificationTokenPromise = getVerificationTokenPromise();
  62. const input: RegisterCustomerInput = {
  63. firstName: 'Sean',
  64. lastName: 'Tester',
  65. emailAddress,
  66. };
  67. const result = await shopClient.query(REGISTER_ACCOUNT, { input });
  68. verificationToken = await verificationTokenPromise;
  69. expect(result.registerCustomerAccount).toBe(true);
  70. expect(sendEmailFn).toHaveBeenCalled();
  71. expect(verificationToken).toBeDefined();
  72. });
  73. it('issues a new token if attempting to register a second time', async () => {
  74. const sendEmail = new Promise<string>(resolve => {
  75. sendEmailFn.mockImplementation((event: AccountRegistrationEvent) => {
  76. resolve(event.user.verificationToken!);
  77. });
  78. });
  79. const input: RegisterCustomerInput = {
  80. firstName: 'Sean',
  81. lastName: 'Tester',
  82. emailAddress,
  83. };
  84. const result = await shopClient.query(REGISTER_ACCOUNT, { input });
  85. const newVerificationToken = await sendEmail;
  86. expect(result.registerCustomerAccount).toBe(true);
  87. expect(sendEmailFn).toHaveBeenCalled();
  88. expect(newVerificationToken).not.toBe(verificationToken);
  89. verificationToken = newVerificationToken;
  90. });
  91. it('refreshCustomerVerification issues a new token', async () => {
  92. const sendEmail = new Promise<string>(resolve => {
  93. sendEmailFn.mockImplementation((event: AccountRegistrationEvent) => {
  94. resolve(event.user.verificationToken!);
  95. });
  96. });
  97. const result = await shopClient.query(REFRESH_TOKEN, { emailAddress });
  98. const newVerificationToken = await sendEmail;
  99. expect(result.refreshCustomerVerification).toBe(true);
  100. expect(sendEmailFn).toHaveBeenCalled();
  101. expect(newVerificationToken).not.toBe(verificationToken);
  102. verificationToken = newVerificationToken;
  103. });
  104. it('refreshCustomerVerification does nothing with an unrecognized emailAddress', async () => {
  105. const result = await shopClient.query(REFRESH_TOKEN, {
  106. emailAddress: 'never-been-registered@test.com',
  107. });
  108. await waitForSendEmailFn();
  109. expect(result.refreshCustomerVerification).toBe(true);
  110. expect(sendEmailFn).not.toHaveBeenCalled();
  111. });
  112. it('login fails before verification', async () => {
  113. try {
  114. await shopClient.asUserWithCredentials(emailAddress, '');
  115. fail('should have thrown');
  116. } catch (err) {
  117. expect(getErrorCode(err)).toBe('UNAUTHORIZED');
  118. }
  119. });
  120. it(
  121. 'verification fails with wrong token',
  122. assertThrowsWithMessage(
  123. () =>
  124. shopClient.query(VERIFY_EMAIL, {
  125. password,
  126. token: 'bad-token',
  127. }),
  128. `Verification token not recognized`,
  129. ),
  130. );
  131. it('verification succeeds with correct token', async () => {
  132. const result = await shopClient.query(VERIFY_EMAIL, {
  133. password,
  134. token: verificationToken,
  135. });
  136. expect(result.verifyCustomerAccount.user.identifier).toBe('test1@test.com');
  137. });
  138. it('registration silently fails if attempting to register an email already verified', async () => {
  139. const input: RegisterCustomerInput = {
  140. firstName: 'Dodgy',
  141. lastName: 'Hacker',
  142. emailAddress,
  143. };
  144. const result = await shopClient.query(REGISTER_ACCOUNT, { input });
  145. await waitForSendEmailFn();
  146. expect(result.registerCustomerAccount).toBe(true);
  147. expect(sendEmailFn).not.toHaveBeenCalled();
  148. });
  149. it(
  150. 'verification fails if attempted a second time',
  151. assertThrowsWithMessage(
  152. () =>
  153. shopClient.query(VERIFY_EMAIL, {
  154. password,
  155. token: verificationToken,
  156. }),
  157. `Verification token not recognized`,
  158. ),
  159. );
  160. });
  161. describe('password reset', () => {
  162. let passwordResetToken: string;
  163. let customer: GetCustomer.Customer;
  164. beforeAll(async () => {
  165. const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
  166. id: 'T_1',
  167. });
  168. customer = result.customer!;
  169. });
  170. beforeEach(() => {
  171. sendEmailFn = jest.fn();
  172. });
  173. it('requestPasswordReset silently fails with invalid identifier', async () => {
  174. const result = await shopClient.query(REQUEST_PASSWORD_RESET, {
  175. identifier: 'invalid-identifier',
  176. });
  177. await waitForSendEmailFn();
  178. expect(result.requestPasswordReset).toBe(true);
  179. expect(sendEmailFn).not.toHaveBeenCalled();
  180. expect(passwordResetToken).not.toBeDefined();
  181. });
  182. it('requestPasswordReset sends reset token', async () => {
  183. const passwordResetTokenPromise = getPasswordResetTokenPromise();
  184. const result = await shopClient.query(REQUEST_PASSWORD_RESET, {
  185. identifier: customer.emailAddress,
  186. });
  187. passwordResetToken = await passwordResetTokenPromise;
  188. expect(result.requestPasswordReset).toBe(true);
  189. expect(sendEmailFn).toHaveBeenCalled();
  190. expect(passwordResetToken).toBeDefined();
  191. });
  192. it(
  193. 'resetPassword fails with wrong token',
  194. assertThrowsWithMessage(
  195. () =>
  196. shopClient.query(RESET_PASSWORD, {
  197. password: 'newPassword',
  198. token: 'bad-token',
  199. }),
  200. `Password reset token not recognized`,
  201. ),
  202. );
  203. it('resetPassword works with valid token', async () => {
  204. const result = await shopClient.query(RESET_PASSWORD, {
  205. token: passwordResetToken,
  206. password: 'newPassword',
  207. });
  208. const loginResult = await shopClient.asUserWithCredentials(customer.emailAddress, 'newPassword');
  209. expect(loginResult.user.identifier).toBe(customer.emailAddress);
  210. });
  211. });
  212. describe('updating emailAddress', () => {
  213. let emailUpdateToken: string;
  214. let customer: GetCustomer.Customer;
  215. const NEW_EMAIL_ADDRESS = 'new@address.com';
  216. const PASSWORD = 'newPassword';
  217. beforeAll(async () => {
  218. const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, { id: 'T_1' });
  219. customer = result.customer!;
  220. });
  221. beforeEach(() => {
  222. sendEmailFn = jest.fn();
  223. });
  224. it('throws if not logged in', async () => {
  225. try {
  226. await shopClient.asAnonymousUser();
  227. await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
  228. password: PASSWORD,
  229. newEmailAddress: NEW_EMAIL_ADDRESS,
  230. });
  231. fail('should have thrown');
  232. } catch (err) {
  233. expect(getErrorCode(err)).toBe('FORBIDDEN');
  234. }
  235. });
  236. it('throws if password is incorrect', async () => {
  237. try {
  238. await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
  239. await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
  240. password: 'bad password',
  241. newEmailAddress: NEW_EMAIL_ADDRESS,
  242. });
  243. fail('should have thrown');
  244. } catch (err) {
  245. expect(getErrorCode(err)).toBe('UNAUTHORIZED');
  246. }
  247. });
  248. it('throws if email address already in use', assertThrowsWithMessage(
  249. async () => {
  250. await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
  251. const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, { id: 'T_2' });
  252. const otherCustomer = result.customer!;
  253. await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
  254. password: PASSWORD,
  255. newEmailAddress: otherCustomer.emailAddress,
  256. });
  257. },
  258. 'This email address is not available',
  259. ),
  260. );
  261. it('triggers event with token', async () => {
  262. await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
  263. const emailUpdateTokenPromise = getEmailUpdateTokenPromise();
  264. await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
  265. password: PASSWORD,
  266. newEmailAddress: NEW_EMAIL_ADDRESS,
  267. });
  268. const { identifierChangeToken, pendingIdentifier } = await emailUpdateTokenPromise;
  269. emailUpdateToken = identifierChangeToken!;
  270. expect(pendingIdentifier).toBe(NEW_EMAIL_ADDRESS);
  271. expect(emailUpdateToken).toBeTruthy();
  272. });
  273. it('cannot login with new email address before verification', async () => {
  274. try {
  275. await shopClient.asUserWithCredentials(NEW_EMAIL_ADDRESS, PASSWORD);
  276. fail('should have thrown');
  277. } catch (err) {
  278. expect(getErrorCode(err)).toBe('UNAUTHORIZED');
  279. }
  280. });
  281. it('throws with bad token', assertThrowsWithMessage(
  282. async () => {
  283. await shopClient.query(UPDATE_EMAIL_ADDRESS, { token: 'bad token' });
  284. },
  285. 'Identifier change token not recognized',
  286. ),
  287. );
  288. it('verify the new email address', async () => {
  289. const result = await shopClient.query(UPDATE_EMAIL_ADDRESS, { token: emailUpdateToken });
  290. expect(result.updateCustomerEmailAddress).toBe(true);
  291. expect(sendEmailFn).toHaveBeenCalled();
  292. expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
  293. });
  294. it('can login with new email address after verification', async () => {
  295. await shopClient.asUserWithCredentials(NEW_EMAIL_ADDRESS, PASSWORD);
  296. const { activeCustomer } = await shopClient.query(GET_ACTIVE_CUSTOMER);
  297. expect(activeCustomer.id).toBe(customer.id);
  298. expect(activeCustomer.emailAddress).toBe(NEW_EMAIL_ADDRESS);
  299. });
  300. it('cannot login with old email address after verification', async () => {
  301. try {
  302. await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
  303. fail('should have thrown');
  304. } catch (err) {
  305. expect(getErrorCode(err)).toBe('UNAUTHORIZED');
  306. }
  307. });
  308. });
  309. async function assertRequestAllowed<V>(operation: DocumentNode, variables?: V) {
  310. try {
  311. const status = await shopClient.queryStatus(operation, variables);
  312. expect(status).toBe(200);
  313. } catch (e) {
  314. const errorCode = getErrorCode(e);
  315. if (!errorCode) {
  316. fail(`Unexpected failure: ${e}`);
  317. } else {
  318. fail(`Operation should be allowed, got status ${getErrorCode(e)}`);
  319. }
  320. }
  321. }
  322. async function assertRequestForbidden<V>(operation: DocumentNode, variables: V) {
  323. try {
  324. const status = await shopClient.query(operation, variables);
  325. fail(`Should have thrown`);
  326. } catch (e) {
  327. expect(getErrorCode(e)).toBe('FORBIDDEN');
  328. }
  329. }
  330. function getErrorCode(err: any): string {
  331. return err.response.errors[0].extensions.code;
  332. }
  333. async function createAdministratorWithPermissions(
  334. code: string,
  335. permissions: Permission[],
  336. ): Promise<{ identifier: string; password: string }> {
  337. const roleResult = await shopClient.query<CreateRole.Mutation, CreateRole.Variables>(CREATE_ROLE, {
  338. input: {
  339. code,
  340. description: '',
  341. permissions,
  342. },
  343. });
  344. const role = roleResult.createRole;
  345. const identifier = `${code}@${Math.random()
  346. .toString(16)
  347. .substr(2, 8)}`;
  348. const password = `test`;
  349. const adminResult = await shopClient.query<
  350. CreateAdministrator.Mutation,
  351. CreateAdministrator.Variables
  352. >(CREATE_ADMINISTRATOR, {
  353. input: {
  354. emailAddress: identifier,
  355. firstName: code,
  356. lastName: 'Admin',
  357. password,
  358. roleIds: [role.id],
  359. },
  360. });
  361. const admin = adminResult.createAdministrator;
  362. return {
  363. identifier,
  364. password,
  365. };
  366. }
  367. /**
  368. * A "sleep" function which allows the sendEmailFn time to get called.
  369. */
  370. function waitForSendEmailFn() {
  371. return new Promise(resolve => setTimeout(resolve, 10));
  372. }
  373. });
  374. describe('Expiring tokens', () => {
  375. const shopClient = new TestShopClient();
  376. const adminClient = new TestAdminClient();
  377. const server = new TestServer();
  378. beforeAll(async () => {
  379. const token = await server.init(
  380. {
  381. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  382. customerCount: 1,
  383. },
  384. {
  385. plugins: [new TestEmailPlugin()],
  386. authOptions: {
  387. verificationTokenDuration: '1ms',
  388. },
  389. },
  390. );
  391. await shopClient.init();
  392. await adminClient.init();
  393. }, TEST_SETUP_TIMEOUT_MS);
  394. beforeEach(() => {
  395. sendEmailFn = jest.fn();
  396. });
  397. afterAll(async () => {
  398. await server.destroy();
  399. });
  400. it(
  401. 'attempting to verify after token has expired throws',
  402. assertThrowsWithMessage(async () => {
  403. const verificationTokenPromise = getVerificationTokenPromise();
  404. const input: RegisterCustomerInput = {
  405. firstName: 'Barry',
  406. lastName: 'Wallace',
  407. emailAddress: 'barry.wallace@test.com',
  408. };
  409. const result = await shopClient.query(REGISTER_ACCOUNT, { input });
  410. const verificationToken = await verificationTokenPromise;
  411. expect(result.registerCustomerAccount).toBe(true);
  412. expect(sendEmailFn).toHaveBeenCalledTimes(1);
  413. expect(verificationToken).toBeDefined();
  414. await new Promise(resolve => setTimeout(resolve, 3));
  415. return shopClient.query(VERIFY_EMAIL, {
  416. password: 'test',
  417. token: verificationToken,
  418. });
  419. }, `Verification token has expired. Use refreshCustomerVerification to send a new token.`),
  420. );
  421. it(
  422. 'attempting to reset password after token has expired throws',
  423. assertThrowsWithMessage(async () => {
  424. const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
  425. GET_CUSTOMER,
  426. { id: 'T_1' },
  427. );
  428. const passwordResetTokenPromise = getPasswordResetTokenPromise();
  429. const result = await shopClient.query(REQUEST_PASSWORD_RESET, {
  430. identifier: customer!.emailAddress,
  431. });
  432. const passwordResetToken = await passwordResetTokenPromise;
  433. expect(result.requestPasswordReset).toBe(true);
  434. expect(sendEmailFn).toHaveBeenCalledTimes(1);
  435. expect(passwordResetToken).toBeDefined();
  436. await new Promise(resolve => setTimeout(resolve, 3));
  437. return shopClient.query(RESET_PASSWORD, {
  438. password: 'test',
  439. token: passwordResetToken,
  440. });
  441. }, `Password reset token has expired.`),
  442. );
  443. });
  444. describe('Registration without email verification', () => {
  445. const shopClient = new TestShopClient();
  446. const server = new TestServer();
  447. const userEmailAddress = 'glen.beardsley@test.com';
  448. beforeAll(async () => {
  449. const token = await server.init(
  450. {
  451. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  452. customerCount: 1,
  453. },
  454. {
  455. plugins: [new TestEmailPlugin()],
  456. authOptions: {
  457. requireVerification: false,
  458. },
  459. },
  460. );
  461. await shopClient.init();
  462. }, TEST_SETUP_TIMEOUT_MS);
  463. beforeEach(() => {
  464. sendEmailFn = jest.fn();
  465. });
  466. afterAll(async () => {
  467. await server.destroy();
  468. });
  469. it(
  470. 'errors if no password is provided',
  471. assertThrowsWithMessage(async () => {
  472. const input: RegisterCustomerInput = {
  473. firstName: 'Glen',
  474. lastName: 'Beardsley',
  475. emailAddress: userEmailAddress,
  476. };
  477. const result = await shopClient.query(REGISTER_ACCOUNT, { input });
  478. }, 'A password must be provided when `authOptions.requireVerification` is set to "false"'),
  479. );
  480. it('register a new account with password', async () => {
  481. const input: RegisterCustomerInput = {
  482. firstName: 'Glen',
  483. lastName: 'Beardsley',
  484. emailAddress: userEmailAddress,
  485. password: 'test',
  486. };
  487. const result = await shopClient.query(REGISTER_ACCOUNT, { input });
  488. expect(result.registerCustomerAccount).toBe(true);
  489. expect(sendEmailFn).not.toHaveBeenCalled();
  490. });
  491. it('can login after registering', async () => {
  492. await shopClient.asUserWithCredentials(userEmailAddress, 'test');
  493. const result = await shopClient.query(
  494. gql`
  495. query {
  496. me {
  497. identifier
  498. }
  499. }
  500. `,
  501. );
  502. expect(result.me.identifier).toBe(userEmailAddress);
  503. });
  504. });
  505. describe('Updating email address without email verification', () => {
  506. const shopClient = new TestShopClient();
  507. const adminClient = new TestAdminClient();
  508. const server = new TestServer();
  509. let customer: GetCustomer.Customer;
  510. const NEW_EMAIL_ADDRESS = 'new@address.com';
  511. beforeAll(async () => {
  512. const token = await server.init(
  513. {
  514. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  515. customerCount: 1,
  516. },
  517. {
  518. plugins: [new TestEmailPlugin()],
  519. authOptions: {
  520. requireVerification: false,
  521. },
  522. },
  523. );
  524. await shopClient.init();
  525. await adminClient.init();
  526. }, TEST_SETUP_TIMEOUT_MS);
  527. beforeAll(async () => {
  528. const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, { id: 'T_1' });
  529. customer = result.customer!;
  530. });
  531. beforeEach(() => {
  532. sendEmailFn = jest.fn();
  533. });
  534. afterAll(async () => {
  535. await server.destroy();
  536. });
  537. it('updates email address', async () => {
  538. await shopClient.asUserWithCredentials(customer.emailAddress, 'test');
  539. const { requestUpdateCustomerEmailAddress } = await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
  540. password: 'test',
  541. newEmailAddress: NEW_EMAIL_ADDRESS,
  542. });
  543. expect(requestUpdateCustomerEmailAddress).toBe(true);
  544. expect(sendEmailFn).toHaveBeenCalledTimes(1);
  545. expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
  546. const { activeCustomer } = await shopClient.query(GET_ACTIVE_CUSTOMER);
  547. expect(activeCustomer.emailAddress).toBe(NEW_EMAIL_ADDRESS);
  548. });
  549. });
  550. /**
  551. * This mock plugin simulates an EmailPlugin which would send emails
  552. * on the registration & password reset events.
  553. */
  554. class TestEmailPlugin implements VendurePlugin {
  555. onBootstrap(inject: InjectorFn) {
  556. const eventBus = inject(EventBus);
  557. eventBus.subscribe(AccountRegistrationEvent, event => {
  558. sendEmailFn(event);
  559. });
  560. eventBus.subscribe(PasswordResetEvent, event => {
  561. sendEmailFn(event);
  562. });
  563. eventBus.subscribe(IdentifierChangeRequestEvent, event => {
  564. sendEmailFn(event);
  565. });
  566. eventBus.subscribe(IdentifierChangeEvent, event => {
  567. sendEmailFn(event);
  568. });
  569. }
  570. }
  571. function getVerificationTokenPromise(): Promise<string> {
  572. return new Promise<any>(resolve => {
  573. sendEmailFn.mockImplementation((event: AccountRegistrationEvent) => {
  574. resolve(event.user.verificationToken);
  575. });
  576. });
  577. }
  578. function getPasswordResetTokenPromise(): Promise<string> {
  579. return new Promise<any>(resolve => {
  580. sendEmailFn.mockImplementation((event: PasswordResetEvent) => {
  581. resolve(event.user.passwordResetToken);
  582. });
  583. });
  584. }
  585. function getEmailUpdateTokenPromise(): Promise<{ identifierChangeToken: string | null; pendingIdentifier: string | null; }> {
  586. return new Promise(resolve => {
  587. sendEmailFn.mockImplementation((event: IdentifierChangeRequestEvent) => {
  588. resolve(pick(event.user, ['identifierChangeToken', 'pendingIdentifier']));
  589. });
  590. });
  591. }
  592. const REGISTER_ACCOUNT = gql`
  593. mutation Register($input: RegisterCustomerInput!) {
  594. registerCustomerAccount(input: $input)
  595. }
  596. `;
  597. const VERIFY_EMAIL = gql`
  598. mutation Verify($password: String!, $token: String!) {
  599. verifyCustomerAccount(password: $password, token: $token) {
  600. user {
  601. id
  602. identifier
  603. }
  604. }
  605. }
  606. `;
  607. const REFRESH_TOKEN = gql`
  608. mutation RefreshToken($emailAddress: String!) {
  609. refreshCustomerVerification(emailAddress: $emailAddress)
  610. }
  611. `;
  612. const REQUEST_PASSWORD_RESET = gql`
  613. mutation RequestPasswordReset($identifier: String!) {
  614. requestPasswordReset(emailAddress: $identifier)
  615. }
  616. `;
  617. const RESET_PASSWORD = gql`
  618. mutation ResetPassword($token: String!, $password: String!) {
  619. resetPassword(token: $token, password: $password) {
  620. user {
  621. id
  622. identifier
  623. }
  624. }
  625. }
  626. `;
  627. const REQUEST_UPDATE_EMAIL_ADDRESS = gql`
  628. mutation RequestUpdateEmailAddress($password: String! $newEmailAddress: String!) {
  629. requestUpdateCustomerEmailAddress(password: $password, newEmailAddress: $newEmailAddress)
  630. }
  631. `;
  632. const UPDATE_EMAIL_ADDRESS = gql`
  633. mutation UpdateEmailAddress($token: String!) {
  634. updateCustomerEmailAddress(token: $token)
  635. }
  636. `;
  637. const GET_ACTIVE_CUSTOMER = gql`
  638. query {
  639. activeCustomer {
  640. id
  641. emailAddress
  642. }
  643. }
  644. `;