diff --git a/.changeset/clean-peaches-run.md b/.changeset/clean-peaches-run.md new file mode 100644 index 000000000..c261d231d --- /dev/null +++ b/.changeset/clean-peaches-run.md @@ -0,0 +1,10 @@ +--- +"@logto/connector-mock-standard-email": major +"@logto/connector-mock-email": major +"@logto/connector-mock-sms": major +--- + +update `writeFile` path according to the connector type + +- SMS connector: `/tmp/logto_mock_sms_record.txt` +- Email connector: `/tmp/logto_mock_email_record.txt` diff --git a/.changeset/two-moles-hope.md b/.changeset/two-moles-hope.md new file mode 100644 index 000000000..e110d5245 --- /dev/null +++ b/.changeset/two-moles-hope.md @@ -0,0 +1,5 @@ +--- +"@logto/connector-kit": minor +--- + +add `mockConnectorFilePaths` and deprecate `mockSmsVerificationCodeFileName` diff --git a/packages/connectors/connector-mock-email-alternative/src/index.ts b/packages/connectors/connector-mock-email-alternative/src/index.ts index e764c84f1..9632b5dfb 100644 --- a/packages/connectors/connector-mock-email-alternative/src/index.ts +++ b/packages/connectors/connector-mock-email-alternative/src/index.ts @@ -1,6 +1,5 @@ import { assert } from '@silverhand/essentials'; import fs from 'node:fs/promises'; -import path from 'node:path'; import type { GetConnectorConfig, @@ -13,6 +12,7 @@ import { ConnectorErrorCodes, validateConfig, ConnectorType, + mockConnectorFilePaths, } from '@logto/connector-kit'; import { defaultMetadata } from './constant.js'; @@ -36,8 +36,8 @@ const sendMessage = ); await fs.writeFile( - path.join('/tmp', 'logto_mock_verification_code_record.txt'), - JSON.stringify({ address: to, code: payload.code, type }) + '\n' + mockConnectorFilePaths.Email, + JSON.stringify({ address: to, code: payload.code, type, payload }) + '\n' ); return { address: to, data: payload }; diff --git a/packages/connectors/connector-mock-email/src/index.ts b/packages/connectors/connector-mock-email/src/index.ts index 794d7a1df..91458e325 100644 --- a/packages/connectors/connector-mock-email/src/index.ts +++ b/packages/connectors/connector-mock-email/src/index.ts @@ -1,6 +1,5 @@ import { assert } from '@silverhand/essentials'; import fs from 'node:fs/promises'; -import path from 'node:path'; import type { GetConnectorConfig, @@ -13,6 +12,7 @@ import { ConnectorErrorCodes, validateConfig, ConnectorType, + mockConnectorFilePaths, } from '@logto/connector-kit'; import { defaultMetadata } from './constant.js'; @@ -36,8 +36,8 @@ const sendMessage = ); await fs.writeFile( - path.join('/tmp', 'logto_mock_verification_code_record.txt'), - JSON.stringify({ address: to, code: payload.code, type }) + '\n' + mockConnectorFilePaths.Email, + JSON.stringify({ address: to, code: payload.code, type, payload }) + '\n' ); return { address: to, data: payload }; diff --git a/packages/connectors/connector-mock-sms/src/index.ts b/packages/connectors/connector-mock-sms/src/index.ts index 88ec1b865..6076e3cd0 100644 --- a/packages/connectors/connector-mock-sms/src/index.ts +++ b/packages/connectors/connector-mock-sms/src/index.ts @@ -1,6 +1,5 @@ import { assert } from '@silverhand/essentials'; import fs from 'node:fs/promises'; -import path from 'node:path'; import type { GetConnectorConfig, @@ -13,6 +12,7 @@ import { ConnectorErrorCodes, validateConfig, ConnectorType, + mockConnectorFilePaths, } from '@logto/connector-kit'; import { defaultMetadata } from './constant.js'; @@ -36,8 +36,8 @@ const sendMessage = ); await fs.writeFile( - path.join('/tmp', 'logto_mock_verification_code_record.txt'), - JSON.stringify({ phone: to, code: payload.code, type }) + '\n' + mockConnectorFilePaths.Sms, + JSON.stringify({ phone: to, code: payload.code, type, payload }) + '\n' ); return { phone: to, data: payload }; diff --git a/packages/core/src/libraries/organization-invitation.ts b/packages/core/src/libraries/organization-invitation.ts index 59e4db3e7..a5d5b21ee 100644 --- a/packages/core/src/libraries/organization-invitation.ts +++ b/packages/core/src/libraries/organization-invitation.ts @@ -14,8 +14,6 @@ import type Queries from '#src/tenants/Queries.js'; import { type ConnectorLibrary } from './connector.js'; -const invitationLinkPath = '/invitation'; - /** * The ending statuses of an organization invitation per RFC 0003. It means that the invitation * status cannot be changed anymore. diff --git a/packages/integration-tests/src/helpers/index.ts b/packages/integration-tests/src/helpers/index.ts index 35e1e8182..aa3607b97 100644 --- a/packages/integration-tests/src/helpers/index.ts +++ b/packages/integration-tests/src/helpers/index.ts @@ -1,15 +1,12 @@ import fs from 'node:fs/promises'; import { createServer, type RequestListener } from 'node:http'; -import path from 'node:path'; -import { mockSmsVerificationCodeFileName } from '@logto/connector-kit'; +import { mockConnectorFilePaths } from '@logto/connector-kit'; import { RequestError } from 'got'; import { createUser } from '#src/api/index.js'; import { generateUsername } from '#src/utils.js'; -const temporaryVerificationCodeFilePath = path.join('/tmp', mockSmsVerificationCodeFileName); - export const createUserByAdmin = async ( username?: string, password?: string, @@ -33,8 +30,10 @@ type VerificationCodeRecord = { type: string; }; -export const readVerificationCode = async (): Promise => { - const buffer = await fs.readFile(temporaryVerificationCodeFilePath); +export const readVerificationCode = async ( + forType: keyof typeof mockConnectorFilePaths +): Promise => { + const buffer = await fs.readFile(mockConnectorFilePaths[forType]); const content = buffer.toString(); // For test use only @@ -42,9 +41,11 @@ export const readVerificationCode = async (): Promise => return JSON.parse(content) as VerificationCodeRecord; }; -export const removeVerificationCode = async (): Promise => { +export const removeVerificationCode = async ( + forType: keyof typeof mockConnectorFilePaths +): Promise => { try { - await fs.unlink(temporaryVerificationCodeFilePath); + await fs.unlink(mockConnectorFilePaths[forType]); } catch { // Do nothing } diff --git a/packages/integration-tests/src/helpers/interactions.ts b/packages/integration-tests/src/helpers/interactions.ts index 20840b593..782c79dcd 100644 --- a/packages/integration-tests/src/helpers/interactions.ts +++ b/packages/integration-tests/src/helpers/interactions.ts @@ -101,7 +101,9 @@ export const resetPassword = async ( ...profile, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode( + 'email' in profile ? 'Email' : 'Sms' + ); await client.successSend(patchInteractionIdentifiers, { ...profile, verificationCode, diff --git a/packages/integration-tests/src/tests/api/interaction/forgot-password/happy-path.test.ts b/packages/integration-tests/src/tests/api/interaction/forgot-password/happy-path.test.ts index c40a99212..aeb0a1289 100644 --- a/packages/integration-tests/src/tests/api/interaction/forgot-password/happy-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/forgot-password/happy-path.test.ts @@ -48,7 +48,7 @@ describe('reset password', () => { email: userProfile.primaryEmail, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Email'); expect(verificationCodeRecord).toMatchObject({ address: userProfile.primaryEmail, @@ -108,7 +108,7 @@ describe('reset password', () => { phone: userProfile.primaryPhone, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Sms'); expect(verificationCodeRecord).toMatchObject({ phone: userProfile.primaryPhone, diff --git a/packages/integration-tests/src/tests/api/interaction/forgot-password/sad-path.test.ts b/packages/integration-tests/src/tests/api/interaction/forgot-password/sad-path.test.ts index 2d87d3a78..5b4314a20 100644 --- a/packages/integration-tests/src/tests/api/interaction/forgot-password/sad-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/forgot-password/sad-path.test.ts @@ -31,7 +31,7 @@ describe('reset password flow sad path', () => { email: primaryEmail, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Email'); await client.successSend(patchInteractionIdentifiers, { email: primaryEmail, verificationCode, @@ -59,7 +59,7 @@ describe('reset password flow sad path', () => { phone: primaryPhone, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Sms'); await client.successSend(patchInteractionIdentifiers, { phone: primaryPhone, verificationCode, @@ -95,7 +95,7 @@ describe('reset password flow sad path', () => { phone: primaryPhone, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Sms'); await client.successSend(patchInteractionIdentifiers, { phone: primaryPhone, verificationCode, diff --git a/packages/integration-tests/src/tests/api/interaction/register-with-identifier/happy-path.test.ts b/packages/integration-tests/src/tests/api/interaction/register-with-identifier/happy-path.test.ts index a286c3f4d..80c438cbc 100644 --- a/packages/integration-tests/src/tests/api/interaction/register-with-identifier/happy-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/register-with-identifier/happy-path.test.ts @@ -79,7 +79,7 @@ describe('register with passwordless identifier', () => { email: primaryEmail, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Email'); expect(verificationCodeRecord).toMatchObject({ address: primaryEmail, @@ -125,7 +125,7 @@ describe('register with passwordless identifier', () => { email: primaryEmail, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Email'); const { code } = verificationCodeRecord; @@ -186,7 +186,7 @@ describe('register with passwordless identifier', () => { phone: primaryPhone, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Sms'); expect(verificationCodeRecord).toMatchObject({ phone: primaryPhone, @@ -232,7 +232,7 @@ describe('register with passwordless identifier', () => { phone: primaryPhone, }); - const { code } = await readVerificationCode(); + const { code } = await readVerificationCode('Sms'); await client.successSend(patchInteractionIdentifiers, { phone: primaryPhone, @@ -296,7 +296,7 @@ describe('register with passwordless identifier', () => { email: primaryEmail, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Email'); expect(verificationCodeRecord).toMatchObject({ address: primaryEmail, @@ -351,7 +351,7 @@ describe('register with passwordless identifier', () => { phone: primaryPhone, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Sms'); expect(verificationCodeRecord).toMatchObject({ phone: primaryPhone, diff --git a/packages/integration-tests/src/tests/api/interaction/register-with-identifier/sad-path.test.ts b/packages/integration-tests/src/tests/api/interaction/register-with-identifier/sad-path.test.ts index af8e4073a..f4853d489 100644 --- a/packages/integration-tests/src/tests/api/interaction/register-with-identifier/sad-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/register-with-identifier/sad-path.test.ts @@ -30,7 +30,7 @@ describe('Register with identifiers sad path', () => { await clearSsoConnectors(); }); - it('Should fail to register if sign-in mode is sign-in only', async () => { + it('should fail to register if sign-in mode is sign-in only', async () => { await updateSignInExperience({ signInMode: SignInMode.SignIn }); const client = await initClient(); @@ -48,13 +48,13 @@ describe('Register with identifiers sad path', () => { await updateSignInExperience({ signInMode: SignInMode.SignInAndRegister }); }); - describe('Should fail to register with identifiers if sign-up settings are not enabled', () => { + describe('should fail to register with identifiers if sign-up settings are not enabled', () => { beforeAll(async () => { // This function call will disable all sign-up settings by default await enableAllPasswordSignInMethods(); }); - it('Should fail to register with username and password', async () => { + it('should fail to register with username and password', async () => { const client = await initClient(); await expectRejects( @@ -72,7 +72,7 @@ describe('Register with identifiers sad path', () => { ); }); - it('Should fail to register with email', async () => { + it('should fail to register with email', async () => { await setEmailConnector(); const { primaryEmail } = generateNewUserProfile({ primaryEmail: true }); const client = await initClient(); @@ -85,7 +85,7 @@ describe('Register with identifiers sad path', () => { email: primaryEmail, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Email'); await expectRejects( client.send(patchInteractionIdentifiers, { @@ -102,7 +102,7 @@ describe('Register with identifiers sad path', () => { await clearConnectorsByTypes([ConnectorType.Email]); }); - it('Should fail to register with phone', async () => { + it('should fail to register with phone', async () => { await setSmsConnector(); const { primaryPhone } = generateNewUserProfile({ primaryPhone: true }); @@ -116,7 +116,7 @@ describe('Register with identifiers sad path', () => { phone: primaryPhone, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Sms'); await expectRejects( client.send(patchInteractionIdentifiers, { diff --git a/packages/integration-tests/src/tests/api/interaction/register-with-identifier/single-sign-on.test.ts b/packages/integration-tests/src/tests/api/interaction/register-with-identifier/single-sign-on.test.ts index e60a0c6c3..29790f5b4 100644 --- a/packages/integration-tests/src/tests/api/interaction/register-with-identifier/single-sign-on.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/register-with-identifier/single-sign-on.test.ts @@ -32,7 +32,7 @@ const happyPath = async (email: string) => { email, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Email'); expect(verificationCodeRecord).toMatchObject({ address: email, @@ -124,7 +124,7 @@ describe('test register with email with SSO feature', () => { email, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Email'); await expectRejects( client.send(patchInteractionIdentifiers, { diff --git a/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/happy-path.test.ts b/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/happy-path.test.ts index de0bebde1..ea82bb163 100644 --- a/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/happy-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/happy-path.test.ts @@ -44,7 +44,7 @@ describe('Sign-in flow using verification-code identifiers', () => { email: userProfile.primaryEmail, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Email'); expect(verificationCodeRecord).toMatchObject({ address: userProfile.primaryEmail, @@ -77,7 +77,7 @@ describe('Sign-in flow using verification-code identifiers', () => { phone: userProfile.primaryPhone, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Sms'); expect(verificationCodeRecord).toMatchObject({ phone: userProfile.primaryPhone, @@ -116,7 +116,7 @@ describe('Sign-in flow using verification-code identifiers', () => { email: newEmail, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Email'); const { code } = verificationCodeRecord; @@ -158,7 +158,7 @@ describe('Sign-in flow using verification-code identifiers', () => { phone: newPhone, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Sms'); const { code } = verificationCodeRecord; @@ -205,7 +205,7 @@ describe('Sign-in flow using verification-code identifiers', () => { await client.successSend(sendVerificationCode, { email: userProfile.primaryEmail, }); - const { code } = await readVerificationCode(); + const { code } = await readVerificationCode('Email'); await client.successSend(patchInteractionIdentifiers, { email: userProfile.primaryEmail, @@ -267,7 +267,7 @@ describe('Sign-in flow using verification-code identifiers', () => { await client.successSend(sendVerificationCode, { email: userProfile.primaryEmail, }); - const { code } = await readVerificationCode(); + const { code } = await readVerificationCode('Email'); await client.successSend(patchInteractionIdentifiers, { email: userProfile.primaryEmail, @@ -324,7 +324,7 @@ describe('Sign-in flow using verification-code identifiers', () => { await client.successSend(sendVerificationCode, { email: userProfile.primaryEmail, }); - const { code } = await readVerificationCode(); + const { code } = await readVerificationCode('Email'); await client.successSend(patchInteractionIdentifiers, { email: userProfile.primaryEmail, diff --git a/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/sad-path.test.ts b/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/sad-path.test.ts index cb69a6f8b..4d7387383 100644 --- a/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/sad-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/sad-path.test.ts @@ -33,7 +33,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { await clearSsoConnectors(); }); - it('Should fail to sign in with passcode if sign-in mode is register only', async () => { + it('should fail to sign in with passcode if sign-in mode is register only', async () => { await updateSignInExperience({ signInMode: SignInMode.Register }); const client = await initClient(); @@ -51,7 +51,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { await enableAllVerificationCodeSignInMethods(); }); - it('Should fail to sign in if related identifiers are not enabled', async () => { + it('should fail to sign in if related identifiers are not enabled', async () => { await updateSignInExperience({ signIn: { methods: [], @@ -72,7 +72,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { email: primaryEmail, }); - const { code: emailVerificationCode } = await readVerificationCode(); + const { code: emailVerificationCode } = await readVerificationCode('Email'); await expectRejects( client.send(patchInteractionIdentifiers, { @@ -90,7 +90,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { phone: primaryPhone, }); - const { code: phoneVerificationCode } = await readVerificationCode(); + const { code: phoneVerificationCode } = await readVerificationCode('Sms'); await expectRejects( client.send(patchInteractionIdentifiers, { @@ -107,7 +107,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { await enableAllVerificationCodeSignInMethods(); }); - it('Should fail to update sign in email identifier if verification code is incorrect or mismatch', async () => { + it('should fail to update sign in email identifier if verification code is incorrect or mismatch', async () => { const { userProfile: { primaryEmail }, } = await generateNewUser({ primaryEmail: true }); @@ -121,7 +121,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { email: primaryEmail, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Email'); await expectRejects( client.send(patchInteractionIdentifiers, { @@ -146,7 +146,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { ); }); - it('Should fail to update sign in phone identifier if verification code is incorrect or mismatch', async () => { + it('should fail to update sign in phone identifier if verification code is incorrect or mismatch', async () => { const { userProfile: { primaryPhone }, } = await generateNewUser({ primaryPhone: true }); @@ -160,7 +160,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { phone: primaryPhone, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Sms'); await expectRejects( client.send(patchInteractionIdentifiers, { @@ -185,7 +185,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { ); }); - it('Should fail to sign in with email and passcode if related user is not exist', async () => { + it('should fail to sign in with email and passcode if related user is not exist', async () => { const notExistUserEmail = generateEmail(); const client = await initClient(); @@ -198,7 +198,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { email: notExistUserEmail, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Email'); await client.successSend(patchInteractionIdentifiers, { email: notExistUserEmail, @@ -211,7 +211,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { }); }); - it('Should fail to sign in with phone and passcode if related user is not exist', async () => { + it('should fail to sign in with phone and passcode if related user is not exist', async () => { const notExistUserPhone = generatePhone(); const client = await initClient(); @@ -224,7 +224,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { phone: notExistUserPhone, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Sms'); await client.successSend(patchInteractionIdentifiers, { phone: notExistUserPhone, @@ -237,7 +237,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { }); }); - it('Should fail to sign in if related user is suspended', async () => { + it('should fail to sign in if related user is suspended', async () => { const { userProfile: { primaryEmail }, user: { id: userId }, @@ -254,7 +254,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { email: primaryEmail, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Email'); await client.successSend(patchInteractionIdentifiers, { email: primaryEmail, diff --git a/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/single-sign-on.test.ts b/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/single-sign-on.test.ts index 1e2b3a669..b08acabfa 100644 --- a/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/single-sign-on.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/single-sign-on.test.ts @@ -32,7 +32,7 @@ const happyPath = async (email: string) => { email, }); - const verificationCodeRecord = await readVerificationCode(); + const verificationCodeRecord = await readVerificationCode('Email'); expect(verificationCodeRecord).toMatchObject({ address: email, @@ -70,7 +70,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => { await clearSsoConnectors(); }); - it('Should fail to sign in with email and passcode if the email domain is enabled for SSO only', async () => { + it('should fail to sign in with email and passcode if the email domain is enabled for SSO only', async () => { const email = generateEmail('sso-sad-path.io'); const user = await createUser({ primaryEmail: email }); await updateSignInExperience({ singleSignOnEnabled: true }); @@ -91,7 +91,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => { email, }); - const { code: verificationCode } = await readVerificationCode(); + const { code: verificationCode } = await readVerificationCode('Email'); await expectRejects( client.send(patchInteractionIdentifiers, { @@ -108,7 +108,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => { await deleteSsoConnectorById(id); }); - it('Should sign-in with email with SSO disabled', async () => { + it('should sign-in with email with SSO disabled', async () => { await updateSignInExperience({ singleSignOnEnabled: false }); const { id } = await createSsoConnector({ @@ -123,7 +123,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => { await deleteSsoConnectorById(id); }); - it('Should sign-in with email with SSO enabled but no connector found', async () => { + it('should sign-in with email with SSO enabled but no connector found', async () => { await updateSignInExperience({ singleSignOnEnabled: true }); const { id } = await createSsoConnector({ diff --git a/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/happy-path.test.ts b/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/happy-path.test.ts index f406925c7..e1cd10ba5 100644 --- a/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/happy-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/happy-path.test.ts @@ -121,7 +121,7 @@ describe('Sign-in flow using password identifiers', () => { email: primaryEmail, }); - const { code } = await readVerificationCode(); + const { code } = await readVerificationCode('Email'); await client.successSend(patchInteractionIdentifiers, { email: primaryEmail, @@ -183,7 +183,7 @@ describe('Sign-in flow using password identifiers', () => { phone: primaryPhone, }); - const { code } = await readVerificationCode(); + const { code } = await readVerificationCode('Sms'); await client.successSend(patchInteractionIdentifiers, { phone: primaryPhone, diff --git a/packages/integration-tests/src/tests/api/verification-code.test.ts b/packages/integration-tests/src/tests/api/verification-code.test.ts index 3e47f13d3..e958bdc00 100644 --- a/packages/integration-tests/src/tests/api/verification-code.test.ts +++ b/packages/integration-tests/src/tests/api/verification-code.test.ts @@ -26,7 +26,7 @@ describe('Generic verification code through management API', () => { }); afterEach(async () => { - await removeVerificationCode(); + await Promise.all([removeVerificationCode('Sms'), removeVerificationCode('Email')]); }); it('should create an email verification code on server side', async () => { @@ -34,7 +34,7 @@ describe('Generic verification code through management API', () => { const response = await requestVerificationCode(payload); expect(response.statusCode).toBe(204); - const { code, type, address } = await readVerificationCode(); + const { code, type, address } = await readVerificationCode('Email'); expect(type).toBe(TemplateType.Generic); expect(address).toBe(mockEmail); @@ -46,7 +46,7 @@ describe('Generic verification code through management API', () => { const response = await requestVerificationCode(payload); expect(response.statusCode).toBe(204); - const { code, type, phone } = await readVerificationCode(); + const { code, type, phone } = await readVerificationCode('Sms'); expect(type).toBe(TemplateType.Generic); expect(phone).toBe(mockPhone); @@ -59,7 +59,8 @@ describe('Generic verification code through management API', () => { statusCode: 400, }); - await expect(readVerificationCode()).rejects.toThrow(); + await expect(readVerificationCode('Email')).rejects.toThrow(); + await expect(readVerificationCode('Sms')).rejects.toThrow(); }); it('should fail to send a verification code on server side when no email connector has been set', async () => { @@ -113,7 +114,7 @@ describe('Generic verification code through management API', () => { it('should be able to verify the email verification code', async () => { await requestVerificationCode({ email: mockEmail }); - const { code } = await readVerificationCode(); + const { code } = await readVerificationCode('Email'); await expect( verifyVerificationCode({ email: mockEmail, verificationCode: code }) @@ -123,7 +124,7 @@ describe('Generic verification code through management API', () => { it('should be able to verify the sms verification code', async () => { await requestVerificationCode({ phone: mockPhone }); - const { code } = await readVerificationCode(); + const { code } = await readVerificationCode('Sms'); await expect( verifyVerificationCode({ phone: mockPhone, verificationCode: code }) @@ -132,7 +133,7 @@ describe('Generic verification code through management API', () => { it('should throw when the code is not valid', async () => { await requestVerificationCode({ phone: mockPhone }); - await readVerificationCode(); + await readVerificationCode('Sms'); await expectRejects(verifyVerificationCode({ phone: mockPhone, verificationCode: '666' }), { code: 'verification_code.code_mismatch', statusCode: 400, @@ -143,7 +144,7 @@ describe('Generic verification code through management API', () => { const phoneToVerify = '666'; const phoneToGetCode = mockPhone; await requestVerificationCode({ phone: phoneToGetCode }); - const { code, phone } = await readVerificationCode(); + const { code, phone } = await readVerificationCode('Sms'); expect(phoneToGetCode).toEqual(phone); await expectRejects(verifyVerificationCode({ phone: phoneToVerify, verificationCode: code }), { code: 'verification_code.not_found', @@ -155,7 +156,7 @@ describe('Generic verification code through management API', () => { const emailToVerify = 'verify_email@mail.com'; const emailToGetCode = mockEmail; await requestVerificationCode({ email: emailToGetCode }); - const { code, address } = await readVerificationCode(); + const { code, address } = await readVerificationCode('Email'); expect(emailToGetCode).toEqual(address); await expectRejects(verifyVerificationCode({ email: emailToVerify, verificationCode: code }), { code: 'verification_code.not_found', diff --git a/packages/integration-tests/src/tests/experience/mfa/totp/add-missing-profile-flow.test.ts b/packages/integration-tests/src/tests/experience/mfa/totp/add-missing-profile-flow.test.ts index fc8524a63..fdae696d2 100644 --- a/packages/integration-tests/src/tests/experience/mfa/totp/add-missing-profile-flow.test.ts +++ b/packages/integration-tests/src/tests/experience/mfa/totp/add-missing-profile-flow.test.ts @@ -48,7 +48,7 @@ describe('MFA - TOTP', () => { const experience = new ExpectTotpExperience(await browser.newPage()); await experience.startWith(demoAppUrl, 'sign-in'); await experience.toFillInput('identifier', userProfile.primaryEmail, { submit: true }); - await experience.toCompleteVerification('sign-in'); + await experience.toCompleteVerification('sign-in', 'Email'); // Add missing password await experience.toFillInput('newPassword', 'l0gt0_T3st_P@ssw0rd', { submit: true }); @@ -99,7 +99,7 @@ describe('MFA - TOTP', () => { // Add missing phone number await waitFor(500); await experience.toFillInput('identifier', generatePhone(), { submit: true }); - await experience.toCompleteVerification('continue'); + await experience.toCompleteVerification('continue', 'Sms'); // Bind TOTP await experience.toBindTotp(); @@ -147,7 +147,7 @@ describe('MFA - TOTP', () => { // Add missing email number await waitFor(500); await experience.toFillInput('identifier', generateEmail(), { submit: true }); - await experience.toCompleteVerification('continue'); + await experience.toCompleteVerification('continue', 'Email'); // Bind TOTP await experience.toBindTotp(); @@ -230,7 +230,7 @@ describe('MFA - TOTP', () => { // Add missing email await verificationExperience.toFillInput('identifier', generateEmail(), { submit: true }); - await verificationExperience.toCompleteVerification('continue'); + await verificationExperience.toCompleteVerification('continue', 'Email'); // Wait for the page to load await waitFor(500); await verificationExperience.verifyThenEnd(); diff --git a/packages/integration-tests/src/tests/experience/mfa/totp/passcode-identifier-flow.test.ts b/packages/integration-tests/src/tests/experience/mfa/totp/passcode-identifier-flow.test.ts index 89e9203f4..1ae46f522 100644 --- a/packages/integration-tests/src/tests/experience/mfa/totp/passcode-identifier-flow.test.ts +++ b/packages/integration-tests/src/tests/experience/mfa/totp/passcode-identifier-flow.test.ts @@ -67,7 +67,7 @@ describe('MFA - TOTP', () => { const experience = new ExpectTotpExperience(await browser.newPage()); await experience.startWith(demoAppUrl, 'register'); await experience.toFillInput('id', context.userEmail, { submit: true }); - await experience.toCompleteVerification('register'); + await experience.toCompleteVerification('register', 'Email'); context.setUpTotpSecret(await experience.toBindTotp()); await experience.verifyThenEnd(); @@ -81,7 +81,7 @@ describe('MFA - TOTP', () => { const experience = new ExpectTotpExperience(await browser.newPage()); await experience.startWith(demoAppUrl, 'sign-in'); await experience.toFillInput('identifier', context.userEmail, { submit: true }); - await experience.toCompleteVerification('sign-in'); + await experience.toCompleteVerification('sign-in', 'Email'); await experience.toVerifyTotp(context.totpSecret); const userId = await experience.getUserIdFromDemoAppPage(); await experience.verifyThenEnd(); @@ -95,7 +95,7 @@ describe('MFA - TOTP', () => { const experience = new ExpectTotpExperience(await browser.newPage()); await experience.startWith(demoAppUrl, 'sign-in'); await experience.toFillInput('identifier', userProfile.primaryEmail, { submit: true }); - await experience.toCompleteVerification('sign-in'); + await experience.toCompleteVerification('sign-in', 'Email'); await experience.toBindTotp(); await experience.verifyThenEnd(); // Clean up @@ -147,7 +147,7 @@ describe('MFA - TOTP', () => { const experience = new ExpectTotpExperience(await browser.newPage()); await experience.startWith(demoAppUrl, 'register'); await experience.toFillInput('id', context.userPhone, { submit: true }); - await experience.toCompleteVerification('register'); + await experience.toCompleteVerification('register', 'Sms'); context.setUpTotpSecret(await experience.toBindTotp()); @@ -163,7 +163,7 @@ describe('MFA - TOTP', () => { const experience = new ExpectTotpExperience(await browser.newPage()); await experience.startWith(demoAppUrl, 'sign-in'); await experience.toFillInput('identifier', context.userPhone, { submit: true }); - await experience.toCompleteVerification('sign-in'); + await experience.toCompleteVerification('sign-in', 'Sms'); await experience.toVerifyTotp(context.totpSecret); const userId = await experience.getUserIdFromDemoAppPage(); await experience.verifyThenEnd(); @@ -179,7 +179,7 @@ describe('MFA - TOTP', () => { await experience.toFillInput('identifier', userProfile.primaryPhone.slice(1), { submit: true, }); - await experience.toCompleteVerification('sign-in'); + await experience.toCompleteVerification('sign-in', 'Sms'); await experience.toBindTotp(); await experience.verifyThenEnd(); diff --git a/packages/integration-tests/src/tests/experience/password-policy.test.ts b/packages/integration-tests/src/tests/experience/password-policy.test.ts index 0cc768ec6..338b341c9 100644 --- a/packages/integration-tests/src/tests/experience/password-policy.test.ts +++ b/packages/integration-tests/src/tests/experience/password-policy.test.ts @@ -70,7 +70,7 @@ describe('password policy', () => { // Complete verification code flow await experience.toFillInput('id', email, { submit: true }); - await experience.toCompleteVerification('register'); + await experience.toCompleteVerification('register', 'Email'); // Wait for the password page to load await waitFor(100); @@ -98,7 +98,7 @@ describe('password policy', () => { await experience.toClickSubmit(); // Complete verification code flow - await experience.toCompleteVerification('forgot-password'); + await experience.toCompleteVerification('forgot-password', 'Email'); // Wait for the password page to load await waitFor(100); diff --git a/packages/integration-tests/src/ui-helpers/expect-experience.ts b/packages/integration-tests/src/ui-helpers/expect-experience.ts index 7f9523879..fce36386c 100644 --- a/packages/integration-tests/src/ui-helpers/expect-experience.ts +++ b/packages/integration-tests/src/ui-helpers/expect-experience.ts @@ -131,9 +131,12 @@ export default class ExpectExperience extends ExpectPage { * * @param type The type of experience to expect. */ - async toCompleteVerification(type: ExperienceType) { + async toCompleteVerification( + type: ExperienceType, + connectorType: Parameters['0'] + ) { this.toBeAt(`${type}/verification-code`); - const { code } = await readVerificationCode(); + const { code } = await readVerificationCode(connectorType); await this.toFillVerificationCode(code); } diff --git a/packages/toolkit/connector-kit/src/index.ts b/packages/toolkit/connector-kit/src/index.ts index b95ac72c3..4c406f477 100644 --- a/packages/toolkit/connector-kit/src/index.ts +++ b/packages/toolkit/connector-kit/src/index.ts @@ -5,6 +5,7 @@ import { ConnectorErrorCodes, sendMessagePayloadKeys, type SendMessagePayload, + ConnectorType, } from './types/index.js'; export * from './types/index.js'; @@ -42,8 +43,18 @@ export const parseJsonObject = (...args: Parameters) => { return parsed; }; +/** @deprecated Use {@link mockConnectorFilePaths} instead. */ export const mockSmsVerificationCodeFileName = 'logto_mock_verification_code_record.txt'; +/** + * The file paths for storing the mock sms/email connector records. You can use these file paths to + * read the records for testing. + */ +export const mockConnectorFilePaths = Object.freeze({ + [ConnectorType.Sms]: '/tmp/logto_mock_sms_record.txt', + [ConnectorType.Email]: '/tmp/logto_mock_email_record.txt', +}); + /** * Replace all handlebars that match the keys in {@link SendMessagePayload} with the payload * values. If the payload does not contain the key, the handlebar will be replaced with an empty