mirror of
https://github.com/logto-io/logto.git
synced 2025-02-17 22:04:19 -05:00
chore(test): add test cases for SAML and OIDC SSO configuration (#5093)
This commit is contained in:
parent
2f0e2a4400
commit
4a517379b3
3 changed files with 108 additions and 4 deletions
|
@ -1,6 +1,7 @@
|
|||
import { conditionalString } from '@silverhand/essentials';
|
||||
import { conditional, conditionalString } from '@silverhand/essentials';
|
||||
import { type Page } from 'puppeteer';
|
||||
|
||||
import { expectToSaveChanges, waitForToast } from '#src/ui-helpers/index.js';
|
||||
import { dcls, cls } from '#src/utils.js';
|
||||
|
||||
import { type SsoConnectorTestCase, type Protocol } from './sso-connectors-test-cases.js';
|
||||
|
@ -68,6 +69,65 @@ const checkSsoConnectorConnectionTabInfo = async (page: Page, protocol: Protocol
|
|||
}
|
||||
};
|
||||
|
||||
const checkOidcConfigPreview = async (previewResults: Record<string, string>) => {
|
||||
await Promise.all(
|
||||
Object.entries(previewResults).map(async ([key, value]) => {
|
||||
const valueField = await expect(page).toMatchElement(
|
||||
[`${dcls('container')}${cls('oidcConfigPreview')}`, `div:has(${dcls('title')})`].join(' '),
|
||||
{ text: key }
|
||||
);
|
||||
const valueDisplayed = await valueField.$eval(
|
||||
[dcls('content')].join(' '),
|
||||
(element) => element.textContent
|
||||
);
|
||||
|
||||
expect(valueDisplayed).toBe(value);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const checkSamlConfigPreview = async (previewResults: Record<string, string>) => {
|
||||
await Promise.all(
|
||||
Object.entries(previewResults).map(async ([key, value]) => {
|
||||
const valueField = await expect(page).toMatchElement(
|
||||
[`${dcls('samlMetadataForm')}`, `${dcls('container')}`, `div:has(${dcls('title')})`].join(
|
||||
' '
|
||||
),
|
||||
{ text: key }
|
||||
);
|
||||
const valueDisplayed = await valueField.$eval(
|
||||
[
|
||||
dcls('content'),
|
||||
conditional(key === 'Signing certificate' && dcls('certificatePreview')),
|
||||
].join(' '),
|
||||
(element) => element.textContent
|
||||
);
|
||||
|
||||
expect(valueDisplayed).toBe(value);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// Configure the SSO connector connection and check the validity by comparing the preview results.
|
||||
const configureSsoConnectorConnection = async (
|
||||
page: Page,
|
||||
formData: Record<string, string>,
|
||||
protocol: Protocol,
|
||||
previewResults: Record<string, string>
|
||||
) => {
|
||||
await expect(page).toFillForm(dcls('form'), formData);
|
||||
await expectToSaveChanges(page);
|
||||
await waitForToast(page, { text: 'Saved' });
|
||||
|
||||
if (protocol === 'OIDC') {
|
||||
await checkOidcConfigPreview(previewResults);
|
||||
}
|
||||
|
||||
if (protocol === 'SAML') {
|
||||
await checkSamlConfigPreview(previewResults);
|
||||
}
|
||||
};
|
||||
|
||||
export const findModalFooterButton = async (isButtonDisabled = false) => {
|
||||
return page.waitForSelector(
|
||||
`.ReactModalPortal div[class$=footer] button${conditionalString(
|
||||
|
@ -78,7 +138,7 @@ export const findModalFooterButton = async (isButtonDisabled = false) => {
|
|||
|
||||
export const fillSsoConnectorCreationModal = async (
|
||||
page: Page,
|
||||
{ connectorFactoryName, connectorName, protocol }: SsoConnectorTestCase,
|
||||
{ connectorFactoryName, connectorName, protocol, formData, previewResults }: SsoConnectorTestCase,
|
||||
checkConnectionInfo = false
|
||||
) => {
|
||||
// Button should be disabled util form is filled.
|
||||
|
@ -114,5 +174,7 @@ export const fillSsoConnectorCreationModal = async (
|
|||
await page.waitForNavigation({ waitUntil: 'networkidle0' });
|
||||
|
||||
await checkSsoConnectorConnectionTabInfo(page, protocol);
|
||||
|
||||
await configureSsoConnectorConnection(page, formData, protocol, previewResults);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,6 +5,33 @@ export type SsoConnectorTestCase = {
|
|||
connectorName: string;
|
||||
connectorFactoryName: string;
|
||||
protocol: Protocol;
|
||||
formData: Record<string, string>;
|
||||
previewResults: Record<string, string>;
|
||||
};
|
||||
|
||||
const oidcFormData = {
|
||||
clientId: 'client-id',
|
||||
clientSecret: 'client-secret',
|
||||
};
|
||||
|
||||
const samlFormData = {
|
||||
// This is a real metadata URL from Microsoft Entra ID test application.
|
||||
metadataUrl:
|
||||
'https://login.microsoftonline.com/ac016212-4f8d-46c6-892c-57c90a255a02/federationmetadata/2007-06/federationmetadata.xml?appid=f562b098-dc8c-4e20-8c4b-ea55554fbd8c',
|
||||
};
|
||||
|
||||
export const oidcPreviewResults = {
|
||||
'Authorization endpoint': 'https://accounts.google.com/o/oauth2/v2/auth',
|
||||
'Token endpoint': 'https://oauth2.googleapis.com/token',
|
||||
'User information endpoint': 'https://openidconnect.googleapis.com/v1/userinfo',
|
||||
'JSON web key set endpoint': 'https://www.googleapis.com/oauth2/v3/certs',
|
||||
Issuer: 'https://accounts.google.com',
|
||||
};
|
||||
|
||||
export const samlPreviewResults = {
|
||||
'Sign on URL': 'https://login.microsoftonline.com/ac016212-4f8d-46c6-892c-57c90a255a02/saml2',
|
||||
Issuer: 'https://sts.windows.net/ac016212-4f8d-46c6-892c-57c90a255a02/',
|
||||
'Signing certificate': 'Expiring Sunday, October 25, 2026',
|
||||
};
|
||||
|
||||
const microsoftEntraIdName = 'Microsoft Entra ID';
|
||||
|
@ -12,6 +39,8 @@ const microsoftEntraID: SsoConnectorTestCase = {
|
|||
connectorName: microsoftEntraIdName,
|
||||
connectorFactoryName: microsoftEntraIdName,
|
||||
protocol: 'SAML',
|
||||
formData: samlFormData,
|
||||
previewResults: samlPreviewResults,
|
||||
};
|
||||
|
||||
const googleWorkspaceName = 'Google Workspace';
|
||||
|
@ -19,6 +48,8 @@ const googleWorkspace: SsoConnectorTestCase = {
|
|||
connectorName: googleWorkspaceName,
|
||||
connectorFactoryName: googleWorkspaceName,
|
||||
protocol: 'OIDC',
|
||||
formData: oidcFormData,
|
||||
previewResults: oidcPreviewResults,
|
||||
};
|
||||
|
||||
const oktaName = 'Okta';
|
||||
|
@ -26,6 +57,9 @@ const okta: SsoConnectorTestCase = {
|
|||
connectorName: oktaName,
|
||||
connectorFactoryName: oktaName,
|
||||
protocol: 'OIDC',
|
||||
// Google Workspace connector have `issuer` predefined, for other OIDC-protocol-based connectors, we use Google's issuer to test the config fetcher and preview.
|
||||
formData: { ...oidcFormData, issuer: 'https://accounts.google.com' },
|
||||
previewResults: oidcPreviewResults,
|
||||
};
|
||||
|
||||
const oidcName = 'OIDC';
|
||||
|
@ -33,6 +67,9 @@ const oidc: SsoConnectorTestCase = {
|
|||
connectorName: oidcName,
|
||||
connectorFactoryName: oidcName,
|
||||
protocol: 'OIDC',
|
||||
// Google Workspace connector have `issuer` predefined, for other OIDC-protocol-based connectors, we use Google's issuer to test the config fetcher and preview.
|
||||
formData: { ...oidcFormData, issuer: 'https://accounts.google.com' },
|
||||
previewResults: oidcPreviewResults,
|
||||
};
|
||||
|
||||
const samlName = 'SAML';
|
||||
|
@ -40,6 +77,8 @@ const saml: SsoConnectorTestCase = {
|
|||
connectorName: samlName,
|
||||
connectorFactoryName: samlName,
|
||||
protocol: 'SAML',
|
||||
formData: samlFormData,
|
||||
previewResults: samlPreviewResults,
|
||||
};
|
||||
|
||||
export const ssoConnectorTestCases: SsoConnectorTestCase[] = [
|
||||
|
|
|
@ -62,7 +62,8 @@ describe('create SSO connectors', () => {
|
|||
});
|
||||
|
||||
it.each(ssoConnectorTestCases.slice(1))(
|
||||
'create other SSO connectors %p',
|
||||
// The full object of `ssoConnector` test case is too burdensome for the test title, so we only use the index here.
|
||||
'create other SSO connectors %#',
|
||||
async (ssoConnector) => {
|
||||
await page.waitForNavigation({ waitUntil: 'networkidle0' });
|
||||
|
||||
|
@ -100,13 +101,15 @@ describe('create SSO connectors', () => {
|
|||
* To check only the `duplicated connector name` is blocked, even if the
|
||||
* existing SSO connector (with the occupied name) is created with a different connector factory.
|
||||
*/
|
||||
const { connectorFactoryName, protocol } = ssoConnectorTestCases[0]!;
|
||||
const { connectorFactoryName, protocol, formData, previewResults } = ssoConnectorTestCases[0]!;
|
||||
const { connectorName } = ssoConnectorTestCases[1]!;
|
||||
// Since the creation process is expected to be blocked in this test case, we do not want to check the connection info on details page.
|
||||
await fillSsoConnectorCreationModal(page, {
|
||||
connectorFactoryName,
|
||||
connectorName,
|
||||
protocol,
|
||||
formData,
|
||||
previewResults,
|
||||
});
|
||||
|
||||
// Error message should be shown.
|
||||
|
|
Loading…
Add table
Reference in a new issue