mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
chore(test): add test cases for SSO connection info (#5092)
This commit is contained in:
parent
86ce940dff
commit
3861bcac36
3 changed files with 96 additions and 10 deletions
|
@ -1,7 +1,72 @@
|
|||
import { conditionalString } from '@silverhand/essentials';
|
||||
import { type Page } from 'puppeteer';
|
||||
|
||||
import { type SsoConnectorTestCase } from './sso-connectors-test-cases.js';
|
||||
import { dcls, cls } from '#src/utils.js';
|
||||
|
||||
import { type SsoConnectorTestCase, type Protocol } from './sso-connectors-test-cases.js';
|
||||
|
||||
const getAndCheckValueByFieldName = async (page: Page, fieldName: string, expectSuffix: string) => {
|
||||
const valueField = await expect(page).toMatchElement(
|
||||
[dcls('form'), `${dcls('field')}:has(${dcls('headline')} > ${dcls('title')})`].join(' '),
|
||||
{ text: fieldName }
|
||||
);
|
||||
const value = await valueField.$eval(
|
||||
[dcls('copyToClipboard'), dcls('row'), dcls('content')].join(' '),
|
||||
(element) => element.textContent
|
||||
);
|
||||
|
||||
expect(value?.endsWith(expectSuffix)).toBeTruthy();
|
||||
};
|
||||
|
||||
// Check the correctness of automatically generated connection info on the `Connection` tab.
|
||||
const checkSsoConnectorConnectionTabInfo = async (page: Page, protocol: Protocol) => {
|
||||
// Wait for the details page redirect to default tab (which is `Connection` tab).
|
||||
await page.waitForNavigation({ waitUntil: 'networkidle0' });
|
||||
|
||||
// eslint-disable-next-line prefer-regex-literals
|
||||
const regExpForDetailsPageUrl = new RegExp('enterprise-sso\\/([^/]+)\\/connection');
|
||||
|
||||
expect(regExpForDetailsPageUrl.test(page.url())).toBe(true);
|
||||
const ssoConnectorIdFromUrl = regExpForDetailsPageUrl.exec(page.url())?.[1];
|
||||
|
||||
if (!ssoConnectorIdFromUrl) {
|
||||
throw new Error('SSO connector ID is not found in URL.');
|
||||
}
|
||||
|
||||
// The SSO connector ID shown on the page should match the ID of the SSO connector in URL.
|
||||
await expect(page).toMatchElement(
|
||||
[
|
||||
dcls('metadata'),
|
||||
dcls('row'),
|
||||
`${dcls('container')}${cls('copyId')}`,
|
||||
dcls('row'),
|
||||
dcls('content'),
|
||||
].join(' '),
|
||||
{ text: ssoConnectorIdFromUrl }
|
||||
);
|
||||
|
||||
if (protocol === 'SAML') {
|
||||
await getAndCheckValueByFieldName(
|
||||
page,
|
||||
'Assertion consumer service URL (Reply URL)',
|
||||
`api/authn/single-sign-on/saml/${ssoConnectorIdFromUrl}`
|
||||
);
|
||||
|
||||
await getAndCheckValueByFieldName(
|
||||
page,
|
||||
'Audience URI (SP Entity ID)',
|
||||
`enterprise-sso/${ssoConnectorIdFromUrl}`
|
||||
);
|
||||
}
|
||||
|
||||
if (protocol === 'OIDC') {
|
||||
await getAndCheckValueByFieldName(
|
||||
page,
|
||||
'Redirect URI (Callback URL)',
|
||||
`callback/${ssoConnectorIdFromUrl}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const findModalFooterButton = async (isButtonDisabled = false) => {
|
||||
return page.waitForSelector(
|
||||
|
@ -13,14 +78,22 @@ export const findModalFooterButton = async (isButtonDisabled = false) => {
|
|||
|
||||
export const fillSsoConnectorCreationModal = async (
|
||||
page: Page,
|
||||
{ connectorFactoryName, connectorName }: SsoConnectorTestCase
|
||||
{ connectorFactoryName, connectorName, protocol }: SsoConnectorTestCase,
|
||||
checkConnectionInfo = false
|
||||
) => {
|
||||
// Button should be disabled util form is filled.
|
||||
await expect(findModalFooterButton(true)).resolves.toBeTruthy();
|
||||
|
||||
// Select connector factory
|
||||
await expect(page).toClick(
|
||||
`.ReactModalPortal div[role=radio] div[class$=ssoConnector] div[class$=content] div[class$=name] span`,
|
||||
[
|
||||
'.ReactModalPortal',
|
||||
'div[role=radio]',
|
||||
dcls('ssoConnector'),
|
||||
dcls('content'),
|
||||
dcls('name'),
|
||||
'span',
|
||||
].join(' '),
|
||||
{ text: connectorFactoryName }
|
||||
);
|
||||
|
||||
|
@ -35,4 +108,11 @@ export const fillSsoConnectorCreationModal = async (
|
|||
// Button should enabled.
|
||||
const createButton = await findModalFooterButton();
|
||||
await createButton?.click();
|
||||
|
||||
if (checkConnectionInfo) {
|
||||
// Wait for the page redirect to details page.
|
||||
await page.waitForNavigation({ waitUntil: 'networkidle0' });
|
||||
|
||||
await checkSsoConnectorConnectionTabInfo(page, protocol);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,37 +1,45 @@
|
|||
// Will extend this type definition later since we are going to configure the SSO connectors with specific values.
|
||||
export type Protocol = 'SAML' | 'OIDC';
|
||||
|
||||
export type SsoConnectorTestCase = {
|
||||
connectorName: string;
|
||||
connectorFactoryName: string;
|
||||
protocol: Protocol;
|
||||
};
|
||||
|
||||
const microsoftEntraIdName = 'Microsoft Entra ID';
|
||||
const microsoftEntraID: SsoConnectorTestCase = {
|
||||
connectorName: microsoftEntraIdName,
|
||||
connectorFactoryName: microsoftEntraIdName,
|
||||
protocol: 'SAML',
|
||||
};
|
||||
|
||||
const googleWorkspaceName = 'Google Workspace';
|
||||
const googleWorkspace: SsoConnectorTestCase = {
|
||||
connectorName: googleWorkspaceName,
|
||||
connectorFactoryName: googleWorkspaceName,
|
||||
protocol: 'OIDC',
|
||||
};
|
||||
|
||||
const oktaName = 'Okta';
|
||||
const okta: SsoConnectorTestCase = {
|
||||
connectorName: oktaName,
|
||||
connectorFactoryName: oktaName,
|
||||
protocol: 'OIDC',
|
||||
};
|
||||
|
||||
const oidcName = 'OIDC';
|
||||
const oidc: SsoConnectorTestCase = {
|
||||
connectorName: oidcName,
|
||||
connectorFactoryName: oidcName,
|
||||
protocol: 'OIDC',
|
||||
};
|
||||
|
||||
const samlName = 'SAML';
|
||||
const saml: SsoConnectorTestCase = {
|
||||
connectorName: samlName,
|
||||
connectorFactoryName: samlName,
|
||||
protocol: 'SAML',
|
||||
};
|
||||
|
||||
export const ssoConnectorTestCases: SsoConnectorTestCase[] = [
|
||||
|
|
|
@ -55,9 +55,7 @@ describe('create SSO connectors', () => {
|
|||
|
||||
await expectModalWithTitle(page, 'Add enterprise connector');
|
||||
|
||||
await fillSsoConnectorCreationModal(page, ssoConnectorTestCases[0]!);
|
||||
|
||||
await page.waitForNavigation({ waitUntil: 'networkidle0' });
|
||||
await fillSsoConnectorCreationModal(page, ssoConnectorTestCases[0]!, true);
|
||||
|
||||
// Come back to Enterprise SSO listing page.
|
||||
await page.goto(appendPathname('/console/enterprise-sso', logtoConsoleUrl).href);
|
||||
|
@ -78,9 +76,7 @@ describe('create SSO connectors', () => {
|
|||
|
||||
await expectModalWithTitle(page, 'Add enterprise connector');
|
||||
|
||||
await fillSsoConnectorCreationModal(page, ssoConnector);
|
||||
|
||||
await page.waitForNavigation({ waitUntil: 'networkidle0' });
|
||||
await fillSsoConnectorCreationModal(page, ssoConnector, true);
|
||||
|
||||
// Come back to Enterprise SSO listing page.
|
||||
await page.goto(appendPathname('/console/enterprise-sso', logtoConsoleUrl).href);
|
||||
|
@ -104,11 +100,13 @@ 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 } = ssoConnectorTestCases[0]!;
|
||||
const { connectorFactoryName, protocol } = 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,
|
||||
});
|
||||
|
||||
// Error message should be shown.
|
||||
|
|
Loading…
Reference in a new issue