0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-20 21:32:31 -05:00

test(core): guard sign-in methods and social connector ids (#498)

* test(core): sign-in-methods guard of sign-in experience

* test(core): social-sign-in-connector-ids guard of sign-in experience

* test(core): guard branding of sign-in experience
This commit is contained in:
IceHe.xyz 2022-04-07 12:18:27 +08:00 committed by GitHub
parent 8cc6a1d138
commit e969e15e3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 322 additions and 111 deletions

View file

@ -0,0 +1,122 @@
import { BrandingStyle, CreateSignInExperience, SignInExperience } from '@logto/schemas';
import { mockBranding, mockSignInExperience } from '@/utils/mock';
import { createRequester } from '@/utils/test-utils';
import signInExperiencesRoutes from './sign-in-experience';
jest.mock('@/queries/sign-in-experience', () => ({
updateDefaultSignInExperience: jest.fn(
async (data: Partial<CreateSignInExperience>): Promise<SignInExperience> => ({
...mockSignInExperience,
...data,
})
),
}));
const signInExperienceRequester = createRequester({ authedRoutes: signInExperiencesRoutes });
const expectPatchResponseStatus = async (signInExperience: any, status: number) => {
const response = await signInExperienceRequester.patch('/sign-in-exp').send(signInExperience);
expect(response.status).toEqual(status);
};
describe('branding', () => {
const colorKeys = ['primaryColor', 'backgroundColor', 'darkPrimaryColor', 'darkBackgroundColor'];
const invalidColors = [
undefined,
null,
'',
'#',
'#1',
'#2B',
'#3cZ',
'#4D9e',
'#5f80E',
'#6GHiXY',
'#78Cb5dA',
'rgb(0,13,255)',
];
const validColors = ['#aB3', '#169deF'];
describe('colors', () => {
test.each(validColors)('%p should succeed', async (validColor) => {
for (const colorKey of colorKeys) {
// eslint-disable-next-line no-await-in-loop
await expectPatchResponseStatus(
{ branding: { ...mockBranding, [colorKey]: validColor } },
200
);
}
});
test.each(invalidColors)('%p should fail', async (invalidColor) => {
for (const colorKey of colorKeys) {
// eslint-disable-next-line no-await-in-loop
await expectPatchResponseStatus(
{ branding: { ...mockBranding, [colorKey]: invalidColor } },
400
);
}
});
});
describe('style', () => {
test.each(Object.values(BrandingStyle))('%p should succeed', async (style) => {
const signInExperience = { branding: { ...mockBranding, style } };
await expectPatchResponseStatus(signInExperience, 200);
});
test.each([undefined, '', 'invalid'])('%p should fail', async (style) => {
const signInExperience = { branding: { ...mockBranding, style } };
await expectPatchResponseStatus(signInExperience, 400);
});
});
describe('logoUrl', () => {
test.each(['http://silverhand.com/silverhand.png', 'https://logto.dev/logto.jpg'])(
'%p should success',
async (logoUrl) => {
const signInExperience = { branding: { ...mockBranding, logoUrl } };
await expectPatchResponseStatus(signInExperience, 200);
}
);
test.each([undefined, null, '', 'invalid'])('%p should fail', async (logoUrl) => {
const signInExperience = { branding: { ...mockBranding, logoUrl } };
await expectPatchResponseStatus(signInExperience, 400);
});
});
describe('slogan', () => {
test.each([undefined, 'Silverhand.', 'Supercharge innovations.'])(
'%p should success',
async (slogan) => {
const signInExperience = {
branding: {
...mockBranding,
style: BrandingStyle.Logo,
slogan,
},
};
await expectPatchResponseStatus(signInExperience, 200);
}
);
test.each([null, ''])('%p should fail', async (slogan) => {
const signInExperience = {
branding: {
...mockBranding,
style: BrandingStyle.Logo,
slogan,
},
};
await expectPatchResponseStatus(signInExperience, 400);
});
});
it('should succeed when branding is valid', async () => {
await expectPatchResponseStatus({ branding: mockBranding }, 200);
});
});

View file

@ -1,10 +1,35 @@
import { BrandingStyle, CreateSignInExperience, Language, SignInExperience } from '@logto/schemas';
import {
CreateSignInExperience,
Language,
SignInExperience,
SignInMethodState,
} from '@logto/schemas';
import { mockBranding, mockLanguageInfo, mockSignInExperience, mockTermsOfUse } from '@/utils/mock';
import {
mockAliyunDmConnectorInstance,
mockAliyunSmsConnectorInstance,
mockFacebookConnectorInstance,
mockGithubConnectorInstance,
mockGoogleConnectorInstance,
mockLanguageInfo,
mockSignInExperience,
mockSignInMethods,
mockTermsOfUse,
} from '@/utils/mock';
import { createRequester } from '@/utils/test-utils';
import signInExperiencesRoutes from './sign-in-experience';
jest.mock('@/connectors', () => ({
getConnectorInstances: jest.fn(async () => [
mockAliyunDmConnectorInstance,
mockAliyunSmsConnectorInstance,
mockFacebookConnectorInstance,
mockGithubConnectorInstance,
mockGoogleConnectorInstance,
]),
}));
jest.mock('@/queries/sign-in-experience', () => ({
updateDefaultSignInExperience: jest.fn(
async (data: Partial<CreateSignInExperience>): Promise<SignInExperience> => ({
@ -24,109 +49,6 @@ const expectPatchResponseStatus = async (signInExperience: any, status: number)
const validBooleans = [true, false];
const invalidBooleans = [undefined, null, 0, 1, '0', '1', 'true', 'false'];
describe('branding', () => {
const colorKeys = ['primaryColor', 'backgroundColor', 'darkPrimaryColor', 'darkBackgroundColor'];
const invalidColors = [
undefined,
null,
'',
'#',
'#1',
'#2B',
'#3cZ',
'#4D9e',
'#5f80E',
'#6GHiXY',
'#78Cb5dA',
'rgb(0,13,255)',
];
const validColors = ['#aB3', '#169deF'];
describe('colors', () => {
test.each(validColors)('%p should succeed', async (validColor) => {
for (const colorKey of colorKeys) {
// eslint-disable-next-line no-await-in-loop
await expectPatchResponseStatus(
{ branding: { ...mockBranding, [colorKey]: validColor } },
200
);
}
});
test.each(invalidColors)('%p should fail', async (invalidColor) => {
for (const colorKey of colorKeys) {
// eslint-disable-next-line no-await-in-loop
await expectPatchResponseStatus(
{ branding: { ...mockBranding, [colorKey]: invalidColor } },
400
);
}
});
});
describe('style', () => {
test.each(Object.values(BrandingStyle))('%p should succeed', async (style) => {
const signInExperience = { branding: { ...mockBranding, style } };
await expectPatchResponseStatus(signInExperience, 200);
});
test.each([undefined, '', 'invalid'])('%p should fail', async (style) => {
const signInExperience = { branding: { ...mockBranding, style } };
await expectPatchResponseStatus(signInExperience, 400);
});
});
describe('logoUrl', () => {
test.each(['http://silverhand.com/silverhand.png', 'https://logto.dev/logto.jpg'])(
'%p should success',
async (logoUrl) => {
const signInExperience = { branding: { ...mockBranding, logoUrl } };
await expectPatchResponseStatus(signInExperience, 200);
}
);
test.each([undefined, null, '', 'invalid'])('%p should fail', async (logoUrl) => {
const signInExperience = { branding: { ...mockBranding, logoUrl } };
await expectPatchResponseStatus(signInExperience, 400);
});
});
describe('slogan', () => {
test.each([undefined, 'Silverhand.', 'Supercharge innovations.'])(
'%p should success',
async (slogan) => {
const signInExperience = {
branding: {
...mockBranding,
style: BrandingStyle.Logo,
slogan,
},
};
await expectPatchResponseStatus(signInExperience, 200);
}
);
test.each([null, ''])('%p should fail', async (slogan) => {
const signInExperience = {
branding: {
...mockBranding,
style: BrandingStyle.Logo,
slogan,
},
};
await expectPatchResponseStatus(signInExperience, 400);
});
});
it('should succeed when branding is valid', async () => {
const response = signInExperienceRequester
.patch('/sign-in-exp')
.send({ branding: mockBranding });
await expect(response).resolves.toMatchObject({ status: 200 });
});
});
describe('terms of use', () => {
describe('enabled', () => {
test.each(validBooleans)('%p should success', async (enabled) => {
@ -197,12 +119,167 @@ describe('languageInfo', () => {
});
});
describe('socialSignInConnectorIds', () => {
it('should throw when the type of social connector IDs is wrong', async () => {
const socialSignInConnectorIds = [123, 456];
const response = await signInExperienceRequester.patch('/sign-in-exp').send({
socialSignInConnectorIds,
describe('signInMethods', () => {
const validSignInMethodStates = Object.values(SignInMethodState);
const invalidSignInMethodStates = [undefined, null, '', ' \t\n\r', 'invalid'];
describe('username', () => {
test.each(validSignInMethodStates)('%p should success', async (state) => {
if (state === SignInMethodState.primary) {
return;
}
const signInExperience = {
signInMethods: {
username: state,
email: SignInMethodState.primary,
sms: SignInMethodState.disabled,
social: SignInMethodState.disabled,
},
};
await expectPatchResponseStatus(signInExperience, 200);
});
test.each(invalidSignInMethodStates)('%p should fail', async (state) => {
if (state === SignInMethodState.primary) {
return;
}
const signInExperience = {
signInMethods: {
username: state,
email: SignInMethodState.primary,
sms: SignInMethodState.disabled,
social: SignInMethodState.disabled,
},
};
await expectPatchResponseStatus(signInExperience, 400);
});
});
describe('email', () => {
test.each(validSignInMethodStates)('%p should success', async (state) => {
if (state === SignInMethodState.primary) {
return;
}
const signInExperience = {
signInMethods: {
username: SignInMethodState.disabled,
email: state,
sms: SignInMethodState.primary,
social: SignInMethodState.disabled,
},
};
await expectPatchResponseStatus(signInExperience, 200);
});
test.each(invalidSignInMethodStates)('%p should fail', async (state) => {
if (state === SignInMethodState.primary) {
return;
}
const signInExperience = {
signInMethods: {
username: SignInMethodState.disabled,
email: state,
sms: SignInMethodState.primary,
social: SignInMethodState.disabled,
},
};
await expectPatchResponseStatus(signInExperience, 400);
});
});
describe('sms', () => {
test.each(validSignInMethodStates)('%p should success', async (state) => {
if (state === SignInMethodState.primary) {
return;
}
const signInExperience = {
signInMethods: {
username: SignInMethodState.disabled,
email: SignInMethodState.disabled,
sms: state,
social: SignInMethodState.primary,
},
socialSignInConnectorIds: ['github'],
};
await expectPatchResponseStatus(signInExperience, 200);
});
test.each(invalidSignInMethodStates)('%p should fail', async (state) => {
if (state === SignInMethodState.primary) {
return;
}
const signInExperience = {
signInMethods: {
username: SignInMethodState.disabled,
email: SignInMethodState.disabled,
sms: state,
social: SignInMethodState.primary,
},
socialSignInConnectorIds: ['github'],
};
await expectPatchResponseStatus(signInExperience, 400);
});
});
describe('social', () => {
test.each(validSignInMethodStates)('%p should success', async (state) => {
if (state === SignInMethodState.primary) {
return;
}
const signInExperience = {
signInMethods: {
username: SignInMethodState.primary,
email: SignInMethodState.disabled,
sms: SignInMethodState.disabled,
social: state,
},
socialSignInConnectorIds: ['github'],
};
await expectPatchResponseStatus(signInExperience, 200);
});
test.each(invalidSignInMethodStates)('%p should fail', async (state) => {
if (state === SignInMethodState.primary) {
return;
}
const signInExperience = {
signInMethods: {
username: SignInMethodState.primary,
email: SignInMethodState.disabled,
sms: SignInMethodState.disabled,
social: state,
},
socialSignInConnectorIds: ['github'],
};
await expectPatchResponseStatus(signInExperience, 400);
});
expect(response.status).toEqual(400);
});
});
describe('socialSignInConnectorIds', () => {
test.each([[['facebook']], [['facebook', 'github']]])(
'%p should success',
async (socialSignInConnectorIds) => {
await expectPatchResponseStatus(
{
signInMethods: { ...mockSignInMethods, social: SignInMethodState.secondary },
socialSignInConnectorIds,
},
200
);
}
);
test.each([[[]], [[null, undefined]], [['', ' \t\n\r']], [[123, 456]]])(
'%p should fail',
async (socialSignInConnectorIds: any[]) => {
await expectPatchResponseStatus(
{
signInMethods: { ...mockSignInMethods, social: SignInMethodState.secondary },
socialSignInConnectorIds,
},
400
);
}
);
});

View file

@ -430,6 +430,18 @@ export const mockAliyunDmConnectorInstance = {
},
};
export const mockAliyunSmsConnectorInstance = {
connector: {
id: 'aliyun-sms',
enabled: true,
config: {},
createdAt: 1_646_382_233_333,
},
metadata: {
type: ConnectorType.SMS,
},
};
export const mockFacebookConnectorInstance = {
connector: {
id: 'facebook',