|
@@ -1,6 +1,6 @@
|
|
|
/* tslint:disable:no-non-null-assertion */
|
|
/* tslint:disable:no-non-null-assertion */
|
|
|
import { OnModuleInit } from '@nestjs/common';
|
|
import { OnModuleInit } from '@nestjs/common';
|
|
|
-import { RegisterCustomerInput } from '@vendure/common/lib/generated-shop-types';
|
|
|
|
|
|
|
+import { ErrorCode, RegisterCustomerInput } from '@vendure/common/lib/generated-shop-types';
|
|
|
import { pick } from '@vendure/common/lib/pick';
|
|
import { pick } from '@vendure/common/lib/pick';
|
|
|
import {
|
|
import {
|
|
|
AccountRegistrationEvent,
|
|
AccountRegistrationEvent,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
PasswordResetEvent,
|
|
PasswordResetEvent,
|
|
|
VendurePlugin,
|
|
VendurePlugin,
|
|
|
} from '@vendure/core';
|
|
} from '@vendure/core';
|
|
|
-import { createTestEnvironment } from '@vendure/testing';
|
|
|
|
|
|
|
+import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
|
|
|
import { DocumentNode } from 'graphql';
|
|
import { DocumentNode } from 'graphql';
|
|
|
import gql from 'graphql-tag';
|
|
import gql from 'graphql-tag';
|
|
|
import path from 'path';
|
|
import path from 'path';
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
Permission,
|
|
Permission,
|
|
|
} from './graphql/generated-e2e-admin-types';
|
|
} from './graphql/generated-e2e-admin-types';
|
|
|
import {
|
|
import {
|
|
|
|
|
+ CurrentUserShopFragment,
|
|
|
GetActiveCustomer,
|
|
GetActiveCustomer,
|
|
|
RefreshToken,
|
|
RefreshToken,
|
|
|
Register,
|
|
Register,
|
|
@@ -70,21 +71,29 @@ let sendEmailFn: jest.Mock;
|
|
|
class TestEmailPlugin implements OnModuleInit {
|
|
class TestEmailPlugin implements OnModuleInit {
|
|
|
constructor(private eventBus: EventBus) {}
|
|
constructor(private eventBus: EventBus) {}
|
|
|
onModuleInit() {
|
|
onModuleInit() {
|
|
|
- this.eventBus.ofType(AccountRegistrationEvent).subscribe((event) => {
|
|
|
|
|
|
|
+ this.eventBus.ofType(AccountRegistrationEvent).subscribe(event => {
|
|
|
sendEmailFn(event);
|
|
sendEmailFn(event);
|
|
|
});
|
|
});
|
|
|
- this.eventBus.ofType(PasswordResetEvent).subscribe((event) => {
|
|
|
|
|
|
|
+ this.eventBus.ofType(PasswordResetEvent).subscribe(event => {
|
|
|
sendEmailFn(event);
|
|
sendEmailFn(event);
|
|
|
});
|
|
});
|
|
|
- this.eventBus.ofType(IdentifierChangeRequestEvent).subscribe((event) => {
|
|
|
|
|
|
|
+ this.eventBus.ofType(IdentifierChangeRequestEvent).subscribe(event => {
|
|
|
sendEmailFn(event);
|
|
sendEmailFn(event);
|
|
|
});
|
|
});
|
|
|
- this.eventBus.ofType(IdentifierChangeEvent).subscribe((event) => {
|
|
|
|
|
|
|
+ this.eventBus.ofType(IdentifierChangeEvent).subscribe(event => {
|
|
|
sendEmailFn(event);
|
|
sendEmailFn(event);
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const successErrorGuard: ErrorResultGuard<{ success: boolean }> = createErrorResultGuard<{
|
|
|
|
|
+ success: boolean;
|
|
|
|
|
+}>(input => input.success != null);
|
|
|
|
|
+
|
|
|
|
|
+const currentUserErrorGuard: ErrorResultGuard<CurrentUserShopFragment> = createErrorResultGuard<
|
|
|
|
|
+ CurrentUserShopFragment
|
|
|
|
|
+>(input => input.identifier != null);
|
|
|
|
|
+
|
|
|
describe('Shop auth & accounts', () => {
|
|
describe('Shop auth & accounts', () => {
|
|
|
const { server, adminClient, shopClient } = createTestEnvironment(
|
|
const { server, adminClient, shopClient } = createTestEnvironment(
|
|
|
mergeConfig(testConfig, {
|
|
mergeConfig(testConfig, {
|
|
@@ -115,6 +124,24 @@ describe('Shop auth & accounts', () => {
|
|
|
sendEmailFn = jest.fn();
|
|
sendEmailFn = jest.fn();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ it('does not return error result on email address conflict', async () => {
|
|
|
|
|
+ // To prevent account enumeration attacks
|
|
|
|
|
+ const { customers } = await adminClient.query<GetCustomerList.Query>(GET_CUSTOMER_LIST);
|
|
|
|
|
+ const input: RegisterCustomerInput = {
|
|
|
|
|
+ firstName: 'Duplicate',
|
|
|
|
|
+ lastName: 'Person',
|
|
|
|
|
+ phoneNumber: '123456',
|
|
|
|
|
+ emailAddress: customers.items[0].emailAddress,
|
|
|
|
|
+ };
|
|
|
|
|
+ const { registerCustomerAccount } = await shopClient.query<Register.Mutation, Register.Variables>(
|
|
|
|
|
+ REGISTER_ACCOUNT,
|
|
|
|
|
+ {
|
|
|
|
|
+ input,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ successErrorGuard.assertSuccess(registerCustomerAccount);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
it('register a new account without password', async () => {
|
|
it('register a new account without password', async () => {
|
|
|
const verificationTokenPromise = getVerificationTokenPromise();
|
|
const verificationTokenPromise = getVerificationTokenPromise();
|
|
|
const input: RegisterCustomerInput = {
|
|
const input: RegisterCustomerInput = {
|
|
@@ -123,13 +150,17 @@ describe('Shop auth & accounts', () => {
|
|
|
phoneNumber: '123456',
|
|
phoneNumber: '123456',
|
|
|
emailAddress,
|
|
emailAddress,
|
|
|
};
|
|
};
|
|
|
- const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
|
|
|
|
|
- input,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const { registerCustomerAccount } = await shopClient.query<Register.Mutation, Register.Variables>(
|
|
|
|
|
+ REGISTER_ACCOUNT,
|
|
|
|
|
+ {
|
|
|
|
|
+ input,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ successErrorGuard.assertSuccess(registerCustomerAccount);
|
|
|
|
|
|
|
|
verificationToken = await verificationTokenPromise;
|
|
verificationToken = await verificationTokenPromise;
|
|
|
|
|
|
|
|
- expect(result.registerCustomerAccount).toBe(true);
|
|
|
|
|
|
|
+ expect(registerCustomerAccount.success).toBe(true);
|
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
|
expect(verificationToken).toBeDefined();
|
|
expect(verificationToken).toBeDefined();
|
|
|
|
|
|
|
@@ -152,7 +183,7 @@ describe('Shop auth & accounts', () => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('issues a new token if attempting to register a second time', async () => {
|
|
it('issues a new token if attempting to register a second time', async () => {
|
|
|
- const sendEmail = new Promise<string>((resolve) => {
|
|
|
|
|
|
|
+ const sendEmail = new Promise<string>(resolve => {
|
|
|
sendEmailFn.mockImplementation((event: AccountRegistrationEvent) => {
|
|
sendEmailFn.mockImplementation((event: AccountRegistrationEvent) => {
|
|
|
resolve(event.user.getNativeAuthenticationMethod().verificationToken!);
|
|
resolve(event.user.getNativeAuthenticationMethod().verificationToken!);
|
|
|
});
|
|
});
|
|
@@ -162,13 +193,17 @@ describe('Shop auth & accounts', () => {
|
|
|
lastName: 'Tester',
|
|
lastName: 'Tester',
|
|
|
emailAddress,
|
|
emailAddress,
|
|
|
};
|
|
};
|
|
|
- const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
|
|
|
|
|
- input,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const { registerCustomerAccount } = await shopClient.query<Register.Mutation, Register.Variables>(
|
|
|
|
|
+ REGISTER_ACCOUNT,
|
|
|
|
|
+ {
|
|
|
|
|
+ input,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ successErrorGuard.assertSuccess(registerCustomerAccount);
|
|
|
|
|
|
|
|
const newVerificationToken = await sendEmail;
|
|
const newVerificationToken = await sendEmail;
|
|
|
|
|
|
|
|
- expect(result.registerCustomerAccount).toBe(true);
|
|
|
|
|
|
|
+ expect(registerCustomerAccount.success).toBe(true);
|
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
|
expect(newVerificationToken).not.toBe(verificationToken);
|
|
expect(newVerificationToken).not.toBe(verificationToken);
|
|
|
|
|
|
|
@@ -176,19 +211,19 @@ describe('Shop auth & accounts', () => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('refreshCustomerVerification issues a new token', async () => {
|
|
it('refreshCustomerVerification issues a new token', async () => {
|
|
|
- const sendEmail = new Promise<string>((resolve) => {
|
|
|
|
|
|
|
+ const sendEmail = new Promise<string>(resolve => {
|
|
|
sendEmailFn.mockImplementation((event: AccountRegistrationEvent) => {
|
|
sendEmailFn.mockImplementation((event: AccountRegistrationEvent) => {
|
|
|
resolve(event.user.getNativeAuthenticationMethod().verificationToken!);
|
|
resolve(event.user.getNativeAuthenticationMethod().verificationToken!);
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
- const result = await shopClient.query<RefreshToken.Mutation, RefreshToken.Variables>(
|
|
|
|
|
- REFRESH_TOKEN,
|
|
|
|
|
- { emailAddress },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const { refreshCustomerVerification } = await shopClient.query<
|
|
|
|
|
+ RefreshToken.Mutation,
|
|
|
|
|
+ RefreshToken.Variables
|
|
|
|
|
+ >(REFRESH_TOKEN, { emailAddress });
|
|
|
|
|
+ successErrorGuard.assertSuccess(refreshCustomerVerification);
|
|
|
const newVerificationToken = await sendEmail;
|
|
const newVerificationToken = await sendEmail;
|
|
|
|
|
|
|
|
- expect(result.refreshCustomerVerification).toBe(true);
|
|
|
|
|
|
|
+ expect(refreshCustomerVerification.success).toBe(true);
|
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
|
expect(newVerificationToken).not.toBe(verificationToken);
|
|
expect(newVerificationToken).not.toBe(verificationToken);
|
|
|
|
|
|
|
@@ -196,56 +231,62 @@ describe('Shop auth & accounts', () => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('refreshCustomerVerification does nothing with an unrecognized emailAddress', async () => {
|
|
it('refreshCustomerVerification does nothing with an unrecognized emailAddress', async () => {
|
|
|
- const result = await shopClient.query<RefreshToken.Mutation, RefreshToken.Variables>(
|
|
|
|
|
- REFRESH_TOKEN,
|
|
|
|
|
- {
|
|
|
|
|
- emailAddress: 'never-been-registered@test.com',
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ const { refreshCustomerVerification } = await shopClient.query<
|
|
|
|
|
+ RefreshToken.Mutation,
|
|
|
|
|
+ RefreshToken.Variables
|
|
|
|
|
+ >(REFRESH_TOKEN, {
|
|
|
|
|
+ emailAddress: 'never-been-registered@test.com',
|
|
|
|
|
+ });
|
|
|
|
|
+ successErrorGuard.assertSuccess(refreshCustomerVerification);
|
|
|
await waitForSendEmailFn();
|
|
await waitForSendEmailFn();
|
|
|
- expect(result.refreshCustomerVerification).toBe(true);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ expect(refreshCustomerVerification.success).toBe(true);
|
|
|
expect(sendEmailFn).not.toHaveBeenCalled();
|
|
expect(sendEmailFn).not.toHaveBeenCalled();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('login fails before verification', async () => {
|
|
it('login fails before verification', async () => {
|
|
|
- try {
|
|
|
|
|
- await shopClient.asUserWithCredentials(emailAddress, '');
|
|
|
|
|
- fail('should have thrown');
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- expect(getErrorCode(err)).toBe('UNAUTHORIZED');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const result = await shopClient.asUserWithCredentials(emailAddress, '');
|
|
|
|
|
+ expect(result.errorCode).toBe(ErrorCode.INVALID_CREDENTIALS_ERROR);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it(
|
|
|
|
|
- 'verification fails with wrong token',
|
|
|
|
|
- assertThrowsWithMessage(
|
|
|
|
|
- () =>
|
|
|
|
|
- shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
|
|
|
|
|
- password,
|
|
|
|
|
- token: 'bad-token',
|
|
|
|
|
- }),
|
|
|
|
|
- `Verification token not recognized`,
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ it('verification fails with wrong token', async () => {
|
|
|
|
|
+ const { verifyCustomerAccount } = await shopClient.query<Verify.Mutation, Verify.Variables>(
|
|
|
|
|
+ VERIFY_EMAIL,
|
|
|
|
|
+ {
|
|
|
|
|
+ password,
|
|
|
|
|
+ token: 'bad-token',
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ currentUserErrorGuard.assertErrorResult(verifyCustomerAccount);
|
|
|
|
|
|
|
|
- it(
|
|
|
|
|
- 'verification fails with no password',
|
|
|
|
|
- assertThrowsWithMessage(
|
|
|
|
|
- () =>
|
|
|
|
|
- shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
|
|
|
|
|
- token: verificationToken,
|
|
|
|
|
- }),
|
|
|
|
|
- `A password must be provided as it was not set during registration`,
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ expect(verifyCustomerAccount.message).toBe(`Verification token not recognized`);
|
|
|
|
|
+ expect(verifyCustomerAccount.errorCode).toBe(ErrorCode.VERIFICATION_TOKEN_INVALID_ERROR);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('verification fails with no password', async () => {
|
|
|
|
|
+ const { verifyCustomerAccount } = await shopClient.query<Verify.Mutation, Verify.Variables>(
|
|
|
|
|
+ VERIFY_EMAIL,
|
|
|
|
|
+ {
|
|
|
|
|
+ token: verificationToken,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ currentUserErrorGuard.assertErrorResult(verifyCustomerAccount);
|
|
|
|
|
+
|
|
|
|
|
+ expect(verifyCustomerAccount.message).toBe(`A password must be provided.`);
|
|
|
|
|
+ expect(verifyCustomerAccount.errorCode).toBe(ErrorCode.MISSING_PASSWORD_ERROR);
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
it('verification succeeds with password and correct token', async () => {
|
|
it('verification succeeds with password and correct token', async () => {
|
|
|
- const result = await shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
|
|
|
|
|
- password,
|
|
|
|
|
- token: verificationToken,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const { verifyCustomerAccount } = await shopClient.query<Verify.Mutation, Verify.Variables>(
|
|
|
|
|
+ VERIFY_EMAIL,
|
|
|
|
|
+ {
|
|
|
|
|
+ password,
|
|
|
|
|
+ token: verificationToken,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ currentUserErrorGuard.assertSuccess(verifyCustomerAccount);
|
|
|
|
|
|
|
|
- expect(result.verifyCustomerAccount.user.identifier).toBe('test1@test.com');
|
|
|
|
|
|
|
+ expect(verifyCustomerAccount.identifier).toBe('test1@test.com');
|
|
|
const { activeCustomer } = await shopClient.query<GetActiveCustomer.Query>(GET_ACTIVE_CUSTOMER);
|
|
const { activeCustomer } = await shopClient.query<GetActiveCustomer.Query>(GET_ACTIVE_CUSTOMER);
|
|
|
newCustomerId = activeCustomer!.id;
|
|
newCustomerId = activeCustomer!.id;
|
|
|
});
|
|
});
|
|
@@ -256,25 +297,32 @@ describe('Shop auth & accounts', () => {
|
|
|
lastName: 'Hacker',
|
|
lastName: 'Hacker',
|
|
|
emailAddress,
|
|
emailAddress,
|
|
|
};
|
|
};
|
|
|
- const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
|
|
|
|
|
- input,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const { registerCustomerAccount } = await shopClient.query<Register.Mutation, Register.Variables>(
|
|
|
|
|
+ REGISTER_ACCOUNT,
|
|
|
|
|
+ {
|
|
|
|
|
+ input,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ successErrorGuard.assertSuccess(registerCustomerAccount);
|
|
|
|
|
+
|
|
|
await waitForSendEmailFn();
|
|
await waitForSendEmailFn();
|
|
|
- expect(result.registerCustomerAccount).toBe(true);
|
|
|
|
|
|
|
+ expect(registerCustomerAccount.success).toBe(true);
|
|
|
expect(sendEmailFn).not.toHaveBeenCalled();
|
|
expect(sendEmailFn).not.toHaveBeenCalled();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it(
|
|
|
|
|
- 'verification fails if attempted a second time',
|
|
|
|
|
- assertThrowsWithMessage(
|
|
|
|
|
- () =>
|
|
|
|
|
- shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
|
|
|
|
|
- password,
|
|
|
|
|
- token: verificationToken,
|
|
|
|
|
- }),
|
|
|
|
|
- `Verification token not recognized`,
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ it('verification fails if attempted a second time', async () => {
|
|
|
|
|
+ const { verifyCustomerAccount } = await shopClient.query<Verify.Mutation, Verify.Variables>(
|
|
|
|
|
+ VERIFY_EMAIL,
|
|
|
|
|
+ {
|
|
|
|
|
+ password,
|
|
|
|
|
+ token: verificationToken,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ currentUserErrorGuard.assertErrorResult(verifyCustomerAccount);
|
|
|
|
|
+
|
|
|
|
|
+ expect(verifyCustomerAccount.message).toBe(`Verification token not recognized`);
|
|
|
|
|
+ expect(verifyCustomerAccount.errorCode).toBe(ErrorCode.VERIFICATION_TOKEN_INVALID_ERROR);
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
it('customer history contains entries for registration & verification', async () => {
|
|
it('customer history contains entries for registration & verification', async () => {
|
|
|
const { customer } = await adminClient.query<
|
|
const { customer } = await adminClient.query<
|
|
@@ -322,13 +370,17 @@ describe('Shop auth & accounts', () => {
|
|
|
emailAddress,
|
|
emailAddress,
|
|
|
password,
|
|
password,
|
|
|
};
|
|
};
|
|
|
- const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
|
|
|
|
|
- input,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const { registerCustomerAccount } = await shopClient.query<Register.Mutation, Register.Variables>(
|
|
|
|
|
+ REGISTER_ACCOUNT,
|
|
|
|
|
+ {
|
|
|
|
|
+ input,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ successErrorGuard.assertSuccess(registerCustomerAccount);
|
|
|
|
|
|
|
|
verificationToken = await verificationTokenPromise;
|
|
verificationToken = await verificationTokenPromise;
|
|
|
|
|
|
|
|
- expect(result.registerCustomerAccount).toBe(true);
|
|
|
|
|
|
|
+ expect(registerCustomerAccount.success).toBe(true);
|
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
|
expect(verificationToken).toBeDefined();
|
|
expect(verificationToken).toBeDefined();
|
|
|
|
|
|
|
@@ -349,25 +401,31 @@ describe('Shop auth & accounts', () => {
|
|
|
pick(customers.items[0], ['firstName', 'lastName', 'emailAddress', 'phoneNumber']),
|
|
pick(customers.items[0], ['firstName', 'lastName', 'emailAddress', 'phoneNumber']),
|
|
|
).toEqual(pick(input, ['firstName', 'lastName', 'emailAddress', 'phoneNumber']));
|
|
).toEqual(pick(input, ['firstName', 'lastName', 'emailAddress', 'phoneNumber']));
|
|
|
});
|
|
});
|
|
|
- it(
|
|
|
|
|
- 'verification fails with password',
|
|
|
|
|
- assertThrowsWithMessage(
|
|
|
|
|
- () =>
|
|
|
|
|
- shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
|
|
|
|
|
- token: verificationToken,
|
|
|
|
|
- password: 'new password',
|
|
|
|
|
- }),
|
|
|
|
|
- `A password has already been set during registration`,
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+
|
|
|
|
|
+ it('verification fails with password', async () => {
|
|
|
|
|
+ const { verifyCustomerAccount } = await shopClient.query<Verify.Mutation, Verify.Variables>(
|
|
|
|
|
+ VERIFY_EMAIL,
|
|
|
|
|
+ {
|
|
|
|
|
+ token: verificationToken,
|
|
|
|
|
+ password: 'new password',
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ currentUserErrorGuard.assertErrorResult(verifyCustomerAccount);
|
|
|
|
|
+
|
|
|
|
|
+ expect(verifyCustomerAccount.message).toBe(`A password has already been set during registration`);
|
|
|
|
|
+ expect(verifyCustomerAccount.errorCode).toBe(ErrorCode.PASSWORD_ALREADY_SET_ERROR);
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
it('verification succeeds with no password and correct token', async () => {
|
|
it('verification succeeds with no password and correct token', async () => {
|
|
|
- const a = 1;
|
|
|
|
|
- const result = await shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
|
|
|
|
|
- token: verificationToken,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const { verifyCustomerAccount } = await shopClient.query<Verify.Mutation, Verify.Variables>(
|
|
|
|
|
+ VERIFY_EMAIL,
|
|
|
|
|
+ {
|
|
|
|
|
+ token: verificationToken,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ currentUserErrorGuard.assertSuccess(verifyCustomerAccount);
|
|
|
|
|
|
|
|
- expect(result.verifyCustomerAccount.user.identifier).toBe('test2@test.com');
|
|
|
|
|
|
|
+ expect(verifyCustomerAccount.identifier).toBe('test2@test.com');
|
|
|
const { activeCustomer } = await shopClient.query<GetActiveCustomer.Query>(GET_ACTIVE_CUSTOMER);
|
|
const { activeCustomer } = await shopClient.query<GetActiveCustomer.Query>(GET_ACTIVE_CUSTOMER);
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
@@ -388,58 +446,65 @@ describe('Shop auth & accounts', () => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('requestPasswordReset silently fails with invalid identifier', async () => {
|
|
it('requestPasswordReset silently fails with invalid identifier', async () => {
|
|
|
- const result = await shopClient.query<
|
|
|
|
|
|
|
+ const { requestPasswordReset } = await shopClient.query<
|
|
|
RequestPasswordReset.Mutation,
|
|
RequestPasswordReset.Mutation,
|
|
|
RequestPasswordReset.Variables
|
|
RequestPasswordReset.Variables
|
|
|
>(REQUEST_PASSWORD_RESET, {
|
|
>(REQUEST_PASSWORD_RESET, {
|
|
|
identifier: 'invalid-identifier',
|
|
identifier: 'invalid-identifier',
|
|
|
});
|
|
});
|
|
|
|
|
+ successErrorGuard.assertSuccess(requestPasswordReset);
|
|
|
|
|
|
|
|
await waitForSendEmailFn();
|
|
await waitForSendEmailFn();
|
|
|
- expect(result.requestPasswordReset).toBe(true);
|
|
|
|
|
|
|
+ expect(requestPasswordReset.success).toBe(true);
|
|
|
expect(sendEmailFn).not.toHaveBeenCalled();
|
|
expect(sendEmailFn).not.toHaveBeenCalled();
|
|
|
expect(passwordResetToken).not.toBeDefined();
|
|
expect(passwordResetToken).not.toBeDefined();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('requestPasswordReset sends reset token', async () => {
|
|
it('requestPasswordReset sends reset token', async () => {
|
|
|
const passwordResetTokenPromise = getPasswordResetTokenPromise();
|
|
const passwordResetTokenPromise = getPasswordResetTokenPromise();
|
|
|
- const result = await shopClient.query<
|
|
|
|
|
|
|
+ const { requestPasswordReset } = await shopClient.query<
|
|
|
RequestPasswordReset.Mutation,
|
|
RequestPasswordReset.Mutation,
|
|
|
RequestPasswordReset.Variables
|
|
RequestPasswordReset.Variables
|
|
|
>(REQUEST_PASSWORD_RESET, {
|
|
>(REQUEST_PASSWORD_RESET, {
|
|
|
identifier: customer.emailAddress,
|
|
identifier: customer.emailAddress,
|
|
|
});
|
|
});
|
|
|
|
|
+ successErrorGuard.assertSuccess(requestPasswordReset);
|
|
|
|
|
|
|
|
passwordResetToken = await passwordResetTokenPromise;
|
|
passwordResetToken = await passwordResetTokenPromise;
|
|
|
|
|
|
|
|
- expect(result.requestPasswordReset).toBe(true);
|
|
|
|
|
|
|
+ expect(requestPasswordReset.success).toBe(true);
|
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
|
expect(passwordResetToken).toBeDefined();
|
|
expect(passwordResetToken).toBeDefined();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it(
|
|
|
|
|
- 'resetPassword fails with wrong token',
|
|
|
|
|
- assertThrowsWithMessage(
|
|
|
|
|
- () =>
|
|
|
|
|
- shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(RESET_PASSWORD, {
|
|
|
|
|
- password: 'newPassword',
|
|
|
|
|
- token: 'bad-token',
|
|
|
|
|
- }),
|
|
|
|
|
- `Password reset token not recognized`,
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ it('resetPassword returns error result with wrong token', async () => {
|
|
|
|
|
+ const { resetPassword } = await shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(
|
|
|
|
|
+ RESET_PASSWORD,
|
|
|
|
|
+ {
|
|
|
|
|
+ password: 'newPassword',
|
|
|
|
|
+ token: 'bad-token',
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ currentUserErrorGuard.assertErrorResult(resetPassword);
|
|
|
|
|
+
|
|
|
|
|
+ expect(resetPassword.message).toBe(`Password reset token not recognized`);
|
|
|
|
|
+ expect(resetPassword.errorCode).toBe(ErrorCode.PASSWORD_RESET_TOKEN_INVALID_ERROR);
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
it('resetPassword works with valid token', async () => {
|
|
it('resetPassword works with valid token', async () => {
|
|
|
- const result = await shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(
|
|
|
|
|
|
|
+ const { resetPassword } = await shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(
|
|
|
RESET_PASSWORD,
|
|
RESET_PASSWORD,
|
|
|
{
|
|
{
|
|
|
token: passwordResetToken,
|
|
token: passwordResetToken,
|
|
|
password: 'newPassword',
|
|
password: 'newPassword',
|
|
|
},
|
|
},
|
|
|
);
|
|
);
|
|
|
|
|
+ currentUserErrorGuard.assertSuccess(resetPassword);
|
|
|
|
|
+
|
|
|
|
|
+ expect(resetPassword.identifier).toBe(customer.emailAddress);
|
|
|
|
|
|
|
|
const loginResult = await shopClient.asUserWithCredentials(customer.emailAddress, 'newPassword');
|
|
const loginResult = await shopClient.asUserWithCredentials(customer.emailAddress, 'newPassword');
|
|
|
- expect(loginResult.user.identifier).toBe(customer.emailAddress);
|
|
|
|
|
|
|
+ expect(loginResult.identifier).toBe(customer.emailAddress);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('customer history for password reset', async () => {
|
|
it('customer history for password reset', async () => {
|
|
@@ -500,41 +565,40 @@ describe('Shop auth & accounts', () => {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it('throws if password is incorrect', async () => {
|
|
|
|
|
- try {
|
|
|
|
|
- await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
|
|
|
|
|
- await shopClient.query<
|
|
|
|
|
- RequestUpdateEmailAddress.Mutation,
|
|
|
|
|
- RequestUpdateEmailAddress.Variables
|
|
|
|
|
- >(REQUEST_UPDATE_EMAIL_ADDRESS, {
|
|
|
|
|
- password: 'bad password',
|
|
|
|
|
- newEmailAddress: NEW_EMAIL_ADDRESS,
|
|
|
|
|
- });
|
|
|
|
|
- fail('should have thrown');
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- expect(getErrorCode(err)).toBe('UNAUTHORIZED');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ it('return error result if password is incorrect', async () => {
|
|
|
|
|
+ await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
|
|
|
|
|
+ const { requestUpdateCustomerEmailAddress } = await shopClient.query<
|
|
|
|
|
+ RequestUpdateEmailAddress.Mutation,
|
|
|
|
|
+ RequestUpdateEmailAddress.Variables
|
|
|
|
|
+ >(REQUEST_UPDATE_EMAIL_ADDRESS, {
|
|
|
|
|
+ password: 'bad password',
|
|
|
|
|
+ newEmailAddress: NEW_EMAIL_ADDRESS,
|
|
|
|
|
+ });
|
|
|
|
|
+ successErrorGuard.assertErrorResult(requestUpdateCustomerEmailAddress);
|
|
|
|
|
+
|
|
|
|
|
+ expect(requestUpdateCustomerEmailAddress.message).toBe('The provided credentials are invalid');
|
|
|
|
|
+ expect(requestUpdateCustomerEmailAddress.errorCode).toBe(ErrorCode.INVALID_CREDENTIALS_ERROR);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it(
|
|
|
|
|
- 'throws if email address already in use',
|
|
|
|
|
- assertThrowsWithMessage(async () => {
|
|
|
|
|
- await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
|
|
|
|
|
- const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
|
|
|
|
|
- GET_CUSTOMER,
|
|
|
|
|
- { id: 'T_2' },
|
|
|
|
|
- );
|
|
|
|
|
- const otherCustomer = result.customer!;
|
|
|
|
|
|
|
+ it('return error result email address already in use', async () => {
|
|
|
|
|
+ await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
|
|
|
|
|
+ const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
|
|
|
|
|
+ id: 'T_2',
|
|
|
|
|
+ });
|
|
|
|
|
+ const otherCustomer = result.customer!;
|
|
|
|
|
+
|
|
|
|
|
+ const { requestUpdateCustomerEmailAddress } = await shopClient.query<
|
|
|
|
|
+ RequestUpdateEmailAddress.Mutation,
|
|
|
|
|
+ RequestUpdateEmailAddress.Variables
|
|
|
|
|
+ >(REQUEST_UPDATE_EMAIL_ADDRESS, {
|
|
|
|
|
+ password: PASSWORD,
|
|
|
|
|
+ newEmailAddress: otherCustomer.emailAddress,
|
|
|
|
|
+ });
|
|
|
|
|
+ successErrorGuard.assertErrorResult(requestUpdateCustomerEmailAddress);
|
|
|
|
|
|
|
|
- await shopClient.query<
|
|
|
|
|
- RequestUpdateEmailAddress.Mutation,
|
|
|
|
|
- RequestUpdateEmailAddress.Variables
|
|
|
|
|
- >(REQUEST_UPDATE_EMAIL_ADDRESS, {
|
|
|
|
|
- password: PASSWORD,
|
|
|
|
|
- newEmailAddress: otherCustomer.emailAddress,
|
|
|
|
|
- });
|
|
|
|
|
- }, 'This email address is not available'),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ expect(requestUpdateCustomerEmailAddress.message).toBe('The email address is not available.');
|
|
|
|
|
+ expect(requestUpdateCustomerEmailAddress.errorCode).toBe(ErrorCode.EMAIL_ADDRESS_CONFLICT_ERROR);
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
it('triggers event with token', async () => {
|
|
it('triggers event with token', async () => {
|
|
|
await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
|
|
await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
|
|
@@ -556,30 +620,32 @@ describe('Shop auth & accounts', () => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('cannot login with new email address before verification', async () => {
|
|
it('cannot login with new email address before verification', async () => {
|
|
|
- try {
|
|
|
|
|
- await shopClient.asUserWithCredentials(NEW_EMAIL_ADDRESS, PASSWORD);
|
|
|
|
|
- fail('should have thrown');
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- expect(getErrorCode(err)).toBe('UNAUTHORIZED');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const result = await shopClient.asUserWithCredentials(NEW_EMAIL_ADDRESS, PASSWORD);
|
|
|
|
|
+
|
|
|
|
|
+ expect(result.errorCode).toBe(ErrorCode.INVALID_CREDENTIALS_ERROR);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it(
|
|
|
|
|
- 'throws with bad token',
|
|
|
|
|
- assertThrowsWithMessage(async () => {
|
|
|
|
|
- await shopClient.query<UpdateEmailAddress.Mutation, UpdateEmailAddress.Variables>(
|
|
|
|
|
- UPDATE_EMAIL_ADDRESS,
|
|
|
|
|
- { token: 'bad token' },
|
|
|
|
|
- );
|
|
|
|
|
- }, 'Identifier change token not recognized'),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ it('return error result for bad token', async () => {
|
|
|
|
|
+ const { updateCustomerEmailAddress } = await shopClient.query<
|
|
|
|
|
+ UpdateEmailAddress.Mutation,
|
|
|
|
|
+ UpdateEmailAddress.Variables
|
|
|
|
|
+ >(UPDATE_EMAIL_ADDRESS, { token: 'bad token' });
|
|
|
|
|
+ successErrorGuard.assertErrorResult(updateCustomerEmailAddress);
|
|
|
|
|
|
|
|
- it('verify the new email address', async () => {
|
|
|
|
|
- const result = await shopClient.query<UpdateEmailAddress.Mutation, UpdateEmailAddress.Variables>(
|
|
|
|
|
- UPDATE_EMAIL_ADDRESS,
|
|
|
|
|
- { token: emailUpdateToken },
|
|
|
|
|
|
|
+ expect(updateCustomerEmailAddress.message).toBe('Identifier change token not recognized');
|
|
|
|
|
+ expect(updateCustomerEmailAddress.errorCode).toBe(
|
|
|
|
|
+ ErrorCode.IDENTIFIER_CHANGE_TOKEN_INVALID_ERROR,
|
|
|
);
|
|
);
|
|
|
- expect(result.updateCustomerEmailAddress).toBe(true);
|
|
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('verify the new email address', async () => {
|
|
|
|
|
+ const { updateCustomerEmailAddress } = await shopClient.query<
|
|
|
|
|
+ UpdateEmailAddress.Mutation,
|
|
|
|
|
+ UpdateEmailAddress.Variables
|
|
|
|
|
+ >(UPDATE_EMAIL_ADDRESS, { token: emailUpdateToken });
|
|
|
|
|
+ successErrorGuard.assertSuccess(updateCustomerEmailAddress);
|
|
|
|
|
+
|
|
|
|
|
+ expect(updateCustomerEmailAddress.success).toBe(true);
|
|
|
|
|
|
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
expect(sendEmailFn).toHaveBeenCalled();
|
|
|
expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
|
|
expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
|
|
@@ -593,12 +659,9 @@ describe('Shop auth & accounts', () => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('cannot login with old email address after verification', async () => {
|
|
it('cannot login with old email address after verification', async () => {
|
|
|
- try {
|
|
|
|
|
- await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
|
|
|
|
|
- fail('should have thrown');
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- expect(getErrorCode(err)).toBe('UNAUTHORIZED');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const result = await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
|
|
|
|
|
+
|
|
|
|
|
+ expect(result.errorCode).toBe(ErrorCode.INVALID_CREDENTIALS_ERROR);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('customer history for email update', async () => {
|
|
it('customer history for email update', async () => {
|
|
@@ -699,7 +762,7 @@ describe('Shop auth & accounts', () => {
|
|
|
* A "sleep" function which allows the sendEmailFn time to get called.
|
|
* A "sleep" function which allows the sendEmailFn time to get called.
|
|
|
*/
|
|
*/
|
|
|
function waitForSendEmailFn() {
|
|
function waitForSendEmailFn() {
|
|
|
- return new Promise((resolve) => setTimeout(resolve, 10));
|
|
|
|
|
|
|
+ return new Promise(resolve => setTimeout(resolve, 10));
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -730,64 +793,79 @@ describe('Expiring tokens', () => {
|
|
|
await server.destroy();
|
|
await server.destroy();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it(
|
|
|
|
|
- 'attempting to verify after token has expired throws',
|
|
|
|
|
- assertThrowsWithMessage(async () => {
|
|
|
|
|
- const verificationTokenPromise = getVerificationTokenPromise();
|
|
|
|
|
- const input: RegisterCustomerInput = {
|
|
|
|
|
- firstName: 'Barry',
|
|
|
|
|
- lastName: 'Wallace',
|
|
|
|
|
- emailAddress: 'barry.wallace@test.com',
|
|
|
|
|
- };
|
|
|
|
|
- const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
|
|
|
|
|
|
|
+ it('attempting to verify after token has expired throws', async () => {
|
|
|
|
|
+ const verificationTokenPromise = getVerificationTokenPromise();
|
|
|
|
|
+ const input: RegisterCustomerInput = {
|
|
|
|
|
+ firstName: 'Barry',
|
|
|
|
|
+ lastName: 'Wallace',
|
|
|
|
|
+ emailAddress: 'barry.wallace@test.com',
|
|
|
|
|
+ };
|
|
|
|
|
+ const { registerCustomerAccount } = await shopClient.query<Register.Mutation, Register.Variables>(
|
|
|
|
|
+ REGISTER_ACCOUNT,
|
|
|
|
|
+ {
|
|
|
input,
|
|
input,
|
|
|
- });
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ successErrorGuard.assertSuccess(registerCustomerAccount);
|
|
|
|
|
|
|
|
- const verificationToken = await verificationTokenPromise;
|
|
|
|
|
|
|
+ const verificationToken = await verificationTokenPromise;
|
|
|
|
|
|
|
|
- expect(result.registerCustomerAccount).toBe(true);
|
|
|
|
|
- expect(sendEmailFn).toHaveBeenCalledTimes(1);
|
|
|
|
|
- expect(verificationToken).toBeDefined();
|
|
|
|
|
|
|
+ expect(registerCustomerAccount.success).toBe(true);
|
|
|
|
|
+ expect(sendEmailFn).toHaveBeenCalledTimes(1);
|
|
|
|
|
+ expect(verificationToken).toBeDefined();
|
|
|
|
|
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 3));
|
|
|
|
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 3));
|
|
|
|
|
|
|
|
- return shopClient.query(VERIFY_EMAIL, {
|
|
|
|
|
|
|
+ const { verifyCustomerAccount } = await shopClient.query<Verify.Mutation, Verify.Variables>(
|
|
|
|
|
+ VERIFY_EMAIL,
|
|
|
|
|
+ {
|
|
|
password: 'test',
|
|
password: 'test',
|
|
|
token: verificationToken,
|
|
token: verificationToken,
|
|
|
- });
|
|
|
|
|
- }, `Verification token has expired. Use refreshCustomerVerification to send a new token.`),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ currentUserErrorGuard.assertErrorResult(verifyCustomerAccount);
|
|
|
|
|
|
|
|
- it(
|
|
|
|
|
- 'attempting to reset password after token has expired throws',
|
|
|
|
|
- assertThrowsWithMessage(async () => {
|
|
|
|
|
- const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
|
|
|
|
|
- GET_CUSTOMER,
|
|
|
|
|
- { id: 'T_1' },
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ expect(verifyCustomerAccount.message).toBe(
|
|
|
|
|
+ `Verification token has expired. Use refreshCustomerVerification to send a new token.`,
|
|
|
|
|
+ );
|
|
|
|
|
+ expect(verifyCustomerAccount.errorCode).toBe(ErrorCode.VERIFICATION_TOKEN_EXPIRED_ERROR);
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- const passwordResetTokenPromise = getPasswordResetTokenPromise();
|
|
|
|
|
- const result = await shopClient.query<
|
|
|
|
|
- RequestPasswordReset.Mutation,
|
|
|
|
|
- RequestPasswordReset.Variables
|
|
|
|
|
- >(REQUEST_PASSWORD_RESET, {
|
|
|
|
|
- identifier: customer!.emailAddress,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ it('attempting to reset password after token has expired returns error result', async () => {
|
|
|
|
|
+ const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
|
|
|
|
|
+ id: 'T_1',
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- const passwordResetToken = await passwordResetTokenPromise;
|
|
|
|
|
|
|
+ const passwordResetTokenPromise = getPasswordResetTokenPromise();
|
|
|
|
|
+ const { requestPasswordReset } = await shopClient.query<
|
|
|
|
|
+ RequestPasswordReset.Mutation,
|
|
|
|
|
+ RequestPasswordReset.Variables
|
|
|
|
|
+ >(REQUEST_PASSWORD_RESET, {
|
|
|
|
|
+ identifier: customer!.emailAddress,
|
|
|
|
|
+ });
|
|
|
|
|
+ successErrorGuard.assertSuccess(requestPasswordReset);
|
|
|
|
|
|
|
|
- expect(result.requestPasswordReset).toBe(true);
|
|
|
|
|
- expect(sendEmailFn).toHaveBeenCalledTimes(1);
|
|
|
|
|
- expect(passwordResetToken).toBeDefined();
|
|
|
|
|
|
|
+ const passwordResetToken = await passwordResetTokenPromise;
|
|
|
|
|
+
|
|
|
|
|
+ expect(requestPasswordReset.success).toBe(true);
|
|
|
|
|
+ expect(sendEmailFn).toHaveBeenCalledTimes(1);
|
|
|
|
|
+ expect(passwordResetToken).toBeDefined();
|
|
|
|
|
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 3));
|
|
|
|
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 3));
|
|
|
|
|
|
|
|
- return shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(RESET_PASSWORD, {
|
|
|
|
|
|
|
+ const { resetPassword } = await shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(
|
|
|
|
|
+ RESET_PASSWORD,
|
|
|
|
|
+ {
|
|
|
password: 'test',
|
|
password: 'test',
|
|
|
token: passwordResetToken,
|
|
token: passwordResetToken,
|
|
|
- });
|
|
|
|
|
- }, `Password reset token has expired.`),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ currentUserErrorGuard.assertErrorResult(resetPassword);
|
|
|
|
|
+
|
|
|
|
|
+ expect(resetPassword.message).toBe(`Password reset token has expired`);
|
|
|
|
|
+ expect(resetPassword.errorCode).toBe(ErrorCode.PASSWORD_RESET_TOKEN_EXPIRED_ERROR);
|
|
|
|
|
+ });
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
describe('Registration without email verification', () => {
|
|
describe('Registration without email verification', () => {
|
|
@@ -817,19 +895,23 @@ describe('Registration without email verification', () => {
|
|
|
await server.destroy();
|
|
await server.destroy();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- it(
|
|
|
|
|
- 'errors if no password is provided',
|
|
|
|
|
- assertThrowsWithMessage(async () => {
|
|
|
|
|
- const input: RegisterCustomerInput = {
|
|
|
|
|
- firstName: 'Glen',
|
|
|
|
|
- lastName: 'Beardsley',
|
|
|
|
|
- emailAddress: userEmailAddress,
|
|
|
|
|
- };
|
|
|
|
|
- const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
|
|
|
|
|
|
|
+ it('Returns error result if no password is provided', async () => {
|
|
|
|
|
+ const input: RegisterCustomerInput = {
|
|
|
|
|
+ firstName: 'Glen',
|
|
|
|
|
+ lastName: 'Beardsley',
|
|
|
|
|
+ emailAddress: userEmailAddress,
|
|
|
|
|
+ };
|
|
|
|
|
+ const { registerCustomerAccount } = await shopClient.query<Register.Mutation, Register.Variables>(
|
|
|
|
|
+ REGISTER_ACCOUNT,
|
|
|
|
|
+ {
|
|
|
input,
|
|
input,
|
|
|
- });
|
|
|
|
|
- }, 'A password must be provided when `authOptions.requireVerification` is set to "false"'),
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ successErrorGuard.assertErrorResult(registerCustomerAccount);
|
|
|
|
|
+
|
|
|
|
|
+ expect(registerCustomerAccount.message).toBe('A password must be provided.');
|
|
|
|
|
+ expect(registerCustomerAccount.errorCode).toBe(ErrorCode.MISSING_PASSWORD_ERROR);
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
it('register a new account with password', async () => {
|
|
it('register a new account with password', async () => {
|
|
|
const input: RegisterCustomerInput = {
|
|
const input: RegisterCustomerInput = {
|
|
@@ -838,11 +920,15 @@ describe('Registration without email verification', () => {
|
|
|
emailAddress: userEmailAddress,
|
|
emailAddress: userEmailAddress,
|
|
|
password: 'test',
|
|
password: 'test',
|
|
|
};
|
|
};
|
|
|
- const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
|
|
|
|
|
- input,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const { registerCustomerAccount } = await shopClient.query<Register.Mutation, Register.Variables>(
|
|
|
|
|
+ REGISTER_ACCOUNT,
|
|
|
|
|
+ {
|
|
|
|
|
+ input,
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
+ successErrorGuard.assertSuccess(registerCustomerAccount);
|
|
|
|
|
|
|
|
- expect(result.registerCustomerAccount).toBe(true);
|
|
|
|
|
|
|
+ expect(registerCustomerAccount.success).toBe(true);
|
|
|
expect(sendEmailFn).not.toHaveBeenCalled();
|
|
expect(sendEmailFn).not.toHaveBeenCalled();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -904,8 +990,9 @@ describe('Updating email address without email verification', () => {
|
|
|
password: 'test',
|
|
password: 'test',
|
|
|
newEmailAddress: NEW_EMAIL_ADDRESS,
|
|
newEmailAddress: NEW_EMAIL_ADDRESS,
|
|
|
});
|
|
});
|
|
|
|
|
+ successErrorGuard.assertSuccess(requestUpdateCustomerEmailAddress);
|
|
|
|
|
|
|
|
- expect(requestUpdateCustomerEmailAddress).toBe(true);
|
|
|
|
|
|
|
+ expect(requestUpdateCustomerEmailAddress.success).toBe(true);
|
|
|
expect(sendEmailFn).toHaveBeenCalledTimes(1);
|
|
expect(sendEmailFn).toHaveBeenCalledTimes(1);
|
|
|
expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
|
|
expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
|
|
|
|
|
|
|
@@ -915,7 +1002,7 @@ describe('Updating email address without email verification', () => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
function getVerificationTokenPromise(): Promise<string> {
|
|
function getVerificationTokenPromise(): Promise<string> {
|
|
|
- return new Promise<any>((resolve) => {
|
|
|
|
|
|
|
+ return new Promise<any>(resolve => {
|
|
|
sendEmailFn.mockImplementation((event: AccountRegistrationEvent) => {
|
|
sendEmailFn.mockImplementation((event: AccountRegistrationEvent) => {
|
|
|
resolve(event.user.getNativeAuthenticationMethod().verificationToken);
|
|
resolve(event.user.getNativeAuthenticationMethod().verificationToken);
|
|
|
});
|
|
});
|
|
@@ -923,7 +1010,7 @@ function getVerificationTokenPromise(): Promise<string> {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function getPasswordResetTokenPromise(): Promise<string> {
|
|
function getPasswordResetTokenPromise(): Promise<string> {
|
|
|
- return new Promise<any>((resolve) => {
|
|
|
|
|
|
|
+ return new Promise<any>(resolve => {
|
|
|
sendEmailFn.mockImplementation((event: PasswordResetEvent) => {
|
|
sendEmailFn.mockImplementation((event: PasswordResetEvent) => {
|
|
|
resolve(event.user.getNativeAuthenticationMethod().passwordResetToken);
|
|
resolve(event.user.getNativeAuthenticationMethod().passwordResetToken);
|
|
|
});
|
|
});
|
|
@@ -934,7 +1021,7 @@ function getEmailUpdateTokenPromise(): Promise<{
|
|
|
identifierChangeToken: string | null;
|
|
identifierChangeToken: string | null;
|
|
|
pendingIdentifier: string | null;
|
|
pendingIdentifier: string | null;
|
|
|
}> {
|
|
}> {
|
|
|
- return new Promise((resolve) => {
|
|
|
|
|
|
|
+ return new Promise(resolve => {
|
|
|
sendEmailFn.mockImplementation((event: IdentifierChangeRequestEvent) => {
|
|
sendEmailFn.mockImplementation((event: IdentifierChangeRequestEvent) => {
|
|
|
resolve(
|
|
resolve(
|
|
|
pick(event.user.getNativeAuthenticationMethod(), [
|
|
pick(event.user.getNativeAuthenticationMethod(), [
|