mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
test(core): add profile fulfillment integration tests (#6294)
* test(core): add profile fufillment integration tests add profile fufillment integration tests * fix: fix integration tests fix integration tests * refactor(test): rebase and update the latest profile api rebase and update the latest profile api
This commit is contained in:
parent
248ee7fb08
commit
9d2770cc27
8 changed files with 569 additions and 16 deletions
|
@ -3,5 +3,6 @@ const prefix = 'experience';
|
|||
export const experienceRoutes = {
|
||||
verification: `${prefix}/verification`,
|
||||
identification: `${prefix}/identification`,
|
||||
profile: `${prefix}/profile`,
|
||||
prefix,
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
type InteractionEvent,
|
||||
type NewPasswordIdentityVerificationPayload,
|
||||
type PasswordVerificationPayload,
|
||||
type UpdateProfileApiPayload,
|
||||
type VerificationCodeIdentifier,
|
||||
} from '@logto/schemas';
|
||||
|
||||
|
@ -196,4 +197,18 @@ export class ExperienceClient extends MockClient {
|
|||
})
|
||||
.json<{ verificationId: string }>();
|
||||
}
|
||||
|
||||
public async resetPassword(payload: { password: string }) {
|
||||
return api.put(`${experienceRoutes.profile}/password`, {
|
||||
headers: { cookie: this.interactionCookie },
|
||||
json: payload,
|
||||
});
|
||||
}
|
||||
|
||||
public async updateProfile(payload: UpdateProfileApiPayload) {
|
||||
return api.post(`${experienceRoutes.profile}`, {
|
||||
headers: { cookie: this.interactionCookie },
|
||||
json: payload,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
} from '@logto/schemas';
|
||||
|
||||
import { type ExperienceClient } from '#src/client/experience/index.js';
|
||||
import { generatePassword } from '#src/utils.js';
|
||||
|
||||
import { initExperienceClient, logoutClient, processSession } from '../client.js';
|
||||
import { expectRejects } from '../index.js';
|
||||
|
@ -104,7 +105,8 @@ export const identifyUserWithUsernamePassword = async (
|
|||
};
|
||||
|
||||
export const registerNewUserWithVerificationCode = async (
|
||||
identifier: VerificationCodeIdentifier
|
||||
identifier: VerificationCodeIdentifier,
|
||||
options?: { fulfillPassword?: boolean }
|
||||
) => {
|
||||
const client = await initExperienceClient();
|
||||
|
||||
|
@ -125,6 +127,20 @@ export const registerNewUserWithVerificationCode = async (
|
|||
verificationId: verifiedVerificationId,
|
||||
});
|
||||
|
||||
if (options?.fulfillPassword) {
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
status: 422,
|
||||
});
|
||||
|
||||
const password = generatePassword();
|
||||
|
||||
await client.updateProfile({
|
||||
type: 'password',
|
||||
value: password,
|
||||
});
|
||||
}
|
||||
|
||||
const { redirectTo } = await client.submitInteraction();
|
||||
|
||||
const userId = await processSession(client, redirectTo);
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
import { ConnectorType } from '@logto/connector-kit';
|
||||
import { InteractionEvent, MfaFactor, SignInIdentifier } from '@logto/schemas';
|
||||
import { authenticator } from 'otplib';
|
||||
|
||||
import { createUserMfaVerification } from '#src/api/admin-user.js';
|
||||
import { initExperienceClient } from '#src/helpers/client.js';
|
||||
import {
|
||||
clearConnectorsByTypes,
|
||||
setEmailConnector,
|
||||
setSmsConnector,
|
||||
} from '#src/helpers/connector.js';
|
||||
import { identifyUserWithUsernamePassword } from '#src/helpers/experience/index.js';
|
||||
import { successfullyVerifyTotp } from '#src/helpers/experience/totp-verification.js';
|
||||
import {
|
||||
successfullySendVerificationCode,
|
||||
successfullyVerifyVerificationCode,
|
||||
} from '#src/helpers/experience/verification-code.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import {
|
||||
enableAllPasswordSignInMethods,
|
||||
enableMandatoryMfaWithTotpAndBackupCode,
|
||||
resetMfaSettings,
|
||||
} from '#src/helpers/sign-in-experience.js';
|
||||
import { generateNewUserProfile, UserApiTest } from '#src/helpers/user.js';
|
||||
import { devFeatureTest, generateEmail } from '#src/utils.js';
|
||||
|
||||
devFeatureTest.describe('Fulfill User Profiles', () => {
|
||||
const userApi = new UserApiTest();
|
||||
|
||||
beforeAll(async () => {
|
||||
await clearConnectorsByTypes([ConnectorType.Email, ConnectorType.Sms]);
|
||||
await Promise.all([setEmailConnector(), setSmsConnector()]);
|
||||
await enableAllPasswordSignInMethods();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await userApi.cleanUp();
|
||||
});
|
||||
|
||||
it('should throw 400 if the interaction event is ForgotPassword', async () => {
|
||||
const client = await initExperienceClient();
|
||||
|
||||
await client.initInteraction({ interactionEvent: InteractionEvent.ForgotPassword });
|
||||
|
||||
await expectRejects(
|
||||
client.updateProfile({ type: SignInIdentifier.Username, value: 'username' }),
|
||||
{
|
||||
status: 400,
|
||||
code: 'session.not_supported_for_forgot_password',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw 404 if the interaction is not identified', async () => {
|
||||
const client = await initExperienceClient();
|
||||
|
||||
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
|
||||
|
||||
await expectRejects(
|
||||
client.updateProfile({ type: SignInIdentifier.Username, value: 'username' }),
|
||||
{
|
||||
status: 404,
|
||||
code: 'session.identifier_not_found',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw 422 if the profile field is already exist in current user account', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
|
||||
await userApi.create({ username, password });
|
||||
|
||||
const client = await initExperienceClient();
|
||||
await identifyUserWithUsernamePassword(client, username, password);
|
||||
|
||||
await expectRejects(
|
||||
client.updateProfile({ type: SignInIdentifier.Username, value: 'username' }),
|
||||
{
|
||||
status: 422,
|
||||
code: 'user.username_exists_in_profile',
|
||||
}
|
||||
);
|
||||
|
||||
await expectRejects(client.updateProfile({ type: 'password', value: 'password' }), {
|
||||
status: 422,
|
||||
code: 'user.password_exists_in_profile',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw 422 if the profile field is used by another user', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
await userApi.create({ username, password });
|
||||
|
||||
const { primaryEmail } = generateNewUserProfile({ primaryEmail: true });
|
||||
await userApi.create({ primaryEmail });
|
||||
|
||||
const client = await initExperienceClient();
|
||||
await identifyUserWithUsernamePassword(client, username, password);
|
||||
|
||||
const { verificationId, code: verificationCode } = await successfullySendVerificationCode(
|
||||
client,
|
||||
{
|
||||
identifier: { type: SignInIdentifier.Email, value: primaryEmail },
|
||||
interactionEvent: InteractionEvent.SignIn,
|
||||
}
|
||||
);
|
||||
|
||||
await successfullyVerifyVerificationCode(client, {
|
||||
identifier: { type: SignInIdentifier.Email, value: primaryEmail },
|
||||
verificationId,
|
||||
code: verificationCode,
|
||||
});
|
||||
|
||||
await expectRejects(client.updateProfile({ type: SignInIdentifier.Email, verificationId }), {
|
||||
status: 422,
|
||||
code: 'user.email_already_in_use',
|
||||
});
|
||||
});
|
||||
|
||||
describe('MFA verification status is required', () => {
|
||||
beforeAll(async () => {
|
||||
await enableMandatoryMfaWithTotpAndBackupCode();
|
||||
});
|
||||
afterAll(async () => {
|
||||
await resetMfaSettings();
|
||||
});
|
||||
|
||||
it('should throw 422 if the mfa is enabled but not verified', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
const user = await userApi.create({ username, password });
|
||||
await createUserMfaVerification(user.id, MfaFactor.TOTP);
|
||||
|
||||
const client = await initExperienceClient();
|
||||
await identifyUserWithUsernamePassword(client, username, password);
|
||||
|
||||
await expectRejects(
|
||||
client.updateProfile({ type: SignInIdentifier.Username, value: 'username' }),
|
||||
{
|
||||
status: 403,
|
||||
code: 'session.mfa.require_mfa_verification',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should update the profile successfully if the mfa is enabled and verified', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
const user = await userApi.create({ username, password });
|
||||
|
||||
const client = await initExperienceClient();
|
||||
await identifyUserWithUsernamePassword(client, username, password);
|
||||
|
||||
const response = await createUserMfaVerification(user.id, MfaFactor.TOTP);
|
||||
|
||||
if (response.type !== MfaFactor.TOTP) {
|
||||
throw new Error('unexpected mfa type');
|
||||
}
|
||||
|
||||
const { secret } = response;
|
||||
const code = authenticator.generate(secret);
|
||||
|
||||
await successfullyVerifyTotp(client, { code });
|
||||
|
||||
const email = generateEmail();
|
||||
|
||||
const { verificationId, code: verificationCode } = await successfullySendVerificationCode(
|
||||
client,
|
||||
{
|
||||
identifier: { type: SignInIdentifier.Email, value: email },
|
||||
interactionEvent: InteractionEvent.SignIn,
|
||||
}
|
||||
);
|
||||
|
||||
await successfullyVerifyVerificationCode(client, {
|
||||
identifier: { type: SignInIdentifier.Email, value: email },
|
||||
verificationId,
|
||||
code: verificationCode,
|
||||
});
|
||||
|
||||
await expect(
|
||||
client.updateProfile({ type: SignInIdentifier.Email, verificationId })
|
||||
).resolves.not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,158 @@
|
|||
import { ConnectorType, InteractionEvent, SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import { updateSignInExperience } from '#src/api/sign-in-experience.js';
|
||||
import { type ExperienceClient } from '#src/client/experience/index.js';
|
||||
import { initExperienceClient } from '#src/helpers/client.js';
|
||||
import { clearConnectorsByTypes, setEmailConnector } from '#src/helpers/connector.js';
|
||||
import { signInWithPassword } from '#src/helpers/experience/index.js';
|
||||
import {
|
||||
successfullySendVerificationCode,
|
||||
successfullyVerifyVerificationCode,
|
||||
} from '#src/helpers/experience/verification-code.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js';
|
||||
import { generateNewUserProfile, UserApiTest } from '#src/helpers/user.js';
|
||||
import { devFeatureTest, generatePassword } from '#src/utils.js';
|
||||
|
||||
const initAndIdentifyForgotPasswordInteraction = async (
|
||||
client: ExperienceClient,
|
||||
email: string
|
||||
) => {
|
||||
await client.initInteraction({ interactionEvent: InteractionEvent.ForgotPassword });
|
||||
const { verificationId, code } = await successfullySendVerificationCode(client, {
|
||||
identifier: { type: SignInIdentifier.Email, value: email },
|
||||
interactionEvent: InteractionEvent.ForgotPassword,
|
||||
});
|
||||
await successfullyVerifyVerificationCode(client, {
|
||||
identifier: { type: SignInIdentifier.Email, value: email },
|
||||
verificationId,
|
||||
code,
|
||||
});
|
||||
await client.identifyUser({ verificationId });
|
||||
};
|
||||
|
||||
devFeatureTest.describe('Reset Password', () => {
|
||||
const userApi = new UserApiTest();
|
||||
|
||||
beforeAll(async () => {
|
||||
await clearConnectorsByTypes([ConnectorType.Email]);
|
||||
await setEmailConnector();
|
||||
await enableAllPasswordSignInMethods();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await userApi.cleanUp();
|
||||
|
||||
// Reset password policy to default value
|
||||
await updateSignInExperience({
|
||||
passwordPolicy: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should 400 if the interaction is not ForgotPassword', async () => {
|
||||
const client = await initExperienceClient();
|
||||
|
||||
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
|
||||
|
||||
await expectRejects(client.resetPassword({ password: 'password' }), {
|
||||
status: 400,
|
||||
code: 'session.invalid_interaction_type',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw 404 if the interaction is not identified', async () => {
|
||||
const client = await initExperienceClient();
|
||||
|
||||
await client.initInteraction({ interactionEvent: InteractionEvent.ForgotPassword });
|
||||
|
||||
await expectRejects(client.resetPassword({ password: 'password' }), {
|
||||
status: 404,
|
||||
code: 'session.identifier_not_found',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw 422 if identify the user using VerificationType other than CodeVerification', async () => {
|
||||
const { username, password } = generateNewUserProfile({ username: true, password: true });
|
||||
await userApi.create({ username, password });
|
||||
const client = await initExperienceClient();
|
||||
await client.initInteraction({ interactionEvent: InteractionEvent.ForgotPassword });
|
||||
const { verificationId } = await client.verifyPassword({
|
||||
identifier: { type: SignInIdentifier.Username, value: username },
|
||||
password,
|
||||
});
|
||||
|
||||
await expectRejects(client.identifyUser({ verificationId }), {
|
||||
status: 422,
|
||||
code: 'session.not_supported_for_forgot_password',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw 422 if the password is same as the current password', async () => {
|
||||
const { primaryEmail, password } = generateNewUserProfile({
|
||||
primaryEmail: true,
|
||||
password: true,
|
||||
});
|
||||
await userApi.create({ primaryEmail, password });
|
||||
const client = await initExperienceClient();
|
||||
|
||||
await initAndIdentifyForgotPasswordInteraction(client, primaryEmail);
|
||||
|
||||
await expectRejects(client.resetPassword({ password }), {
|
||||
status: 422,
|
||||
code: 'user.same_password',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw 422 if the password violates password policy (using email as password)', async () => {
|
||||
await updateSignInExperience({
|
||||
passwordPolicy: {
|
||||
length: { min: 8, max: 32 },
|
||||
characterTypes: { min: 3 },
|
||||
rejects: {
|
||||
pwned: true,
|
||||
repetitionAndSequence: true,
|
||||
userInfo: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { primaryEmail, password } = generateNewUserProfile({
|
||||
primaryEmail: true,
|
||||
password: true,
|
||||
});
|
||||
|
||||
await userApi.create({ primaryEmail, password });
|
||||
|
||||
const client = await initExperienceClient();
|
||||
|
||||
await initAndIdentifyForgotPasswordInteraction(client, primaryEmail);
|
||||
|
||||
await expectRejects(client.resetPassword({ password: primaryEmail }), {
|
||||
status: 422,
|
||||
code: 'password.rejected',
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset password successfully', async () => {
|
||||
const { primaryEmail, password } = generateNewUserProfile({
|
||||
primaryEmail: true,
|
||||
password: true,
|
||||
});
|
||||
await userApi.create({ primaryEmail, password });
|
||||
|
||||
const newPassword = generatePassword();
|
||||
|
||||
const client = await initExperienceClient();
|
||||
|
||||
await initAndIdentifyForgotPasswordInteraction(client, primaryEmail);
|
||||
|
||||
await client.resetPassword({ password: newPassword });
|
||||
|
||||
await client.submitInteraction();
|
||||
|
||||
await signInWithPassword({
|
||||
identifier: { type: SignInIdentifier.Email, value: primaryEmail },
|
||||
password: newPassword,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -98,4 +98,31 @@ devFeatureTest.describe('Register interaction with verification code happy path'
|
|||
await deleteUser(user.id);
|
||||
}
|
||||
);
|
||||
|
||||
describe('fulfill password', () => {
|
||||
beforeAll(async () => {
|
||||
await enableAllVerificationCodeSignInMethods({
|
||||
identifiers: [SignInIdentifier.Email, SignInIdentifier.Phone],
|
||||
password: true,
|
||||
verify: true,
|
||||
});
|
||||
});
|
||||
|
||||
it.each(verificationIdentifierType)(
|
||||
'Should register with verification code using %p and fulfill the password successfully',
|
||||
async (identifier) => {
|
||||
const userId = await registerNewUserWithVerificationCode(
|
||||
{
|
||||
type: identifier,
|
||||
value: identifier === SignInIdentifier.Email ? generateEmail() : generatePhone(),
|
||||
},
|
||||
{
|
||||
fulfillPassword: true,
|
||||
}
|
||||
);
|
||||
|
||||
await deleteUser(userId);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
import { InteractionEvent, SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import { deleteUser } from '#src/api/admin-user.js';
|
||||
import { signInWithPassword } from '#src/helpers/experience/index.js';
|
||||
import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js';
|
||||
import { deleteUser, getUser } from '#src/api/admin-user.js';
|
||||
import { initExperienceClient, logoutClient, processSession } from '#src/helpers/client.js';
|
||||
import { setEmailConnector, setSmsConnector } from '#src/helpers/connector.js';
|
||||
import {
|
||||
identifyUserWithUsernamePassword,
|
||||
signInWithPassword,
|
||||
} from '#src/helpers/experience/index.js';
|
||||
import {
|
||||
successfullySendVerificationCode,
|
||||
successfullyVerifyVerificationCode,
|
||||
} from '#src/helpers/experience/verification-code.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import {
|
||||
enableAllPasswordSignInMethods,
|
||||
enableAllVerificationCodeSignInMethods,
|
||||
} from '#src/helpers/sign-in-experience.js';
|
||||
import { generateNewUser } from '#src/helpers/user.js';
|
||||
import { devFeatureTest } from '#src/utils.js';
|
||||
import { devFeatureTest, generateEmail } from '#src/utils.js';
|
||||
|
||||
const identifiersTypeToUserProfile = Object.freeze({
|
||||
username: 'username',
|
||||
|
@ -15,6 +28,7 @@ const identifiersTypeToUserProfile = Object.freeze({
|
|||
devFeatureTest.describe('sign-in with password verification happy path', () => {
|
||||
beforeAll(async () => {
|
||||
await enableAllPasswordSignInMethods();
|
||||
await Promise.all([setEmailConnector(), setSmsConnector()]);
|
||||
});
|
||||
|
||||
it.each(Object.values(SignInIdentifier))(
|
||||
|
@ -36,4 +50,55 @@ devFeatureTest.describe('sign-in with password verification happy path', () => {
|
|||
await deleteUser(user.id);
|
||||
}
|
||||
);
|
||||
|
||||
it('sign-in with username and password and fulfill the email', async () => {
|
||||
await enableAllVerificationCodeSignInMethods({
|
||||
identifiers: [SignInIdentifier.Email],
|
||||
password: true,
|
||||
verify: true,
|
||||
});
|
||||
|
||||
const { userProfile, user } = await generateNewUser({
|
||||
username: true,
|
||||
password: true,
|
||||
});
|
||||
const { username, password } = userProfile;
|
||||
|
||||
const primaryEmail = generateEmail();
|
||||
|
||||
const client = await initExperienceClient();
|
||||
|
||||
await identifyUserWithUsernamePassword(client, username, password);
|
||||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
status: 422,
|
||||
});
|
||||
|
||||
const { verificationId, code: verificationCode } = await successfullySendVerificationCode(
|
||||
client,
|
||||
{
|
||||
identifier: { type: SignInIdentifier.Email, value: primaryEmail },
|
||||
interactionEvent: InteractionEvent.SignIn,
|
||||
}
|
||||
);
|
||||
|
||||
await successfullyVerifyVerificationCode(client, {
|
||||
identifier: { type: SignInIdentifier.Email, value: primaryEmail },
|
||||
verificationId,
|
||||
code: verificationCode,
|
||||
});
|
||||
|
||||
await client.updateProfile({ type: SignInIdentifier.Email, verificationId });
|
||||
|
||||
const { redirectTo } = await client.submitInteraction();
|
||||
await processSession(client, redirectTo);
|
||||
await logoutClient(client);
|
||||
|
||||
const { primaryEmail: syncedEmail } = await getUser(user.id);
|
||||
|
||||
expect(syncedEmail).toBe(primaryEmail);
|
||||
|
||||
await deleteUser(user.id);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { ConnectorType } from '@logto/connector-kit';
|
||||
import { InteractionEvent, SignInIdentifier } from '@logto/schemas';
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
|
||||
import {
|
||||
|
@ -6,11 +7,21 @@ import {
|
|||
mockSocialConnectorTarget,
|
||||
} from '#src/__mocks__/connectors-mock.js';
|
||||
import { deleteUser, getUser } from '#src/api/admin-user.js';
|
||||
import { updateSignInExperience } from '#src/api/sign-in-experience.js';
|
||||
import { clearConnectorsByTypes, setSocialConnector } from '#src/helpers/connector.js';
|
||||
import { initExperienceClient, logoutClient, processSession } from '#src/helpers/client.js';
|
||||
import {
|
||||
clearConnectorsByTypes,
|
||||
setEmailConnector,
|
||||
setSocialConnector,
|
||||
} from '#src/helpers/connector.js';
|
||||
import { signInWithSocial } from '#src/helpers/experience/index.js';
|
||||
import {
|
||||
successFullyCreateSocialVerification,
|
||||
successFullyVerifySocialAuthorization,
|
||||
} from '#src/helpers/experience/social-verification.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js';
|
||||
import { generateNewUser } from '#src/helpers/user.js';
|
||||
import { devFeatureTest, generateEmail } from '#src/utils.js';
|
||||
import { devFeatureTest, generateEmail, generateUsername } from '#src/utils.js';
|
||||
|
||||
devFeatureTest.describe('social sign-in and sign-up', () => {
|
||||
const connectorIdMap = new Map<string, string>();
|
||||
|
@ -18,16 +29,15 @@ devFeatureTest.describe('social sign-in and sign-up', () => {
|
|||
const email = generateEmail();
|
||||
|
||||
beforeAll(async () => {
|
||||
await clearConnectorsByTypes([ConnectorType.Social]);
|
||||
await updateSignInExperience({
|
||||
signUp: {
|
||||
await enableAllPasswordSignInMethods({
|
||||
identifiers: [],
|
||||
password: false,
|
||||
verify: false,
|
||||
},
|
||||
});
|
||||
await clearConnectorsByTypes([ConnectorType.Social, ConnectorType.Email]);
|
||||
|
||||
const { id: socialConnectorId } = await setSocialConnector();
|
||||
await setEmailConnector();
|
||||
connectorIdMap.set(mockSocialConnectorId, socialConnectorId);
|
||||
});
|
||||
|
||||
|
@ -112,4 +122,81 @@ devFeatureTest.describe('social sign-in and sign-up', () => {
|
|||
|
||||
await deleteUser(userId);
|
||||
});
|
||||
|
||||
describe('fulfill missing user profile', () => {
|
||||
const state = 'state';
|
||||
const redirectUri = 'http://localhost:3000';
|
||||
|
||||
it('should successfully sign-up with social and fulfill missing username', async () => {
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId)!;
|
||||
|
||||
await enableAllPasswordSignInMethods({
|
||||
identifiers: [SignInIdentifier.Username],
|
||||
password: true,
|
||||
verify: false,
|
||||
});
|
||||
|
||||
const client = await initExperienceClient();
|
||||
await client.initInteraction({ interactionEvent: InteractionEvent.SignIn });
|
||||
|
||||
const { verificationId } = await successFullyCreateSocialVerification(client, connectorId, {
|
||||
redirectUri,
|
||||
state,
|
||||
});
|
||||
|
||||
await successFullyVerifySocialAuthorization(client, connectorId, {
|
||||
verificationId,
|
||||
connectorData: {
|
||||
state,
|
||||
redirectUri,
|
||||
code: 'fake_code',
|
||||
userId: generateStandardId(),
|
||||
},
|
||||
});
|
||||
|
||||
await expectRejects(client.identifyUser({ verificationId }), {
|
||||
code: 'user.identity_not_exist',
|
||||
status: 404,
|
||||
});
|
||||
|
||||
await client.updateInteractionEvent({ interactionEvent: InteractionEvent.Register });
|
||||
await client.identifyUser({ verificationId });
|
||||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.updateProfile({ type: SignInIdentifier.Username, value: generateUsername() });
|
||||
|
||||
const { redirectTo } = await client.submitInteraction();
|
||||
const userId = await processSession(client, redirectTo);
|
||||
await logoutClient(client);
|
||||
await deleteUser(userId);
|
||||
});
|
||||
|
||||
it('should directly sync trusted email', async () => {
|
||||
await enableAllPasswordSignInMethods({
|
||||
identifiers: [SignInIdentifier.Email],
|
||||
password: true,
|
||||
verify: true,
|
||||
});
|
||||
|
||||
const userId = await signInWithSocial(
|
||||
connectorIdMap.get(mockSocialConnectorId)!,
|
||||
{
|
||||
id: socialUserId,
|
||||
email,
|
||||
},
|
||||
{
|
||||
registerNewUser: true,
|
||||
}
|
||||
);
|
||||
|
||||
const { primaryEmail } = await getUser(userId);
|
||||
expect(primaryEmail).toBe(email);
|
||||
|
||||
await deleteUser(userId);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue