0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

chore(test): add console ITs for SSO connectors creation and deletion (#5073)

This commit is contained in:
Darcy Ye 2023-12-08 18:55:52 +08:00 committed by GitHub
parent d5fd2834fb
commit 67ab1b3d52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 223 additions and 0 deletions

View file

@ -0,0 +1,38 @@
import { conditionalString } from '@silverhand/essentials';
import { type Page } from 'puppeteer';
import { type SsoConnectorTestCase } from './sso-connectors-test-cases.js';
export const findModalFooterButton = async (isButtonDisabled = false) => {
return page.waitForSelector(
`.ReactModalPortal div[class$=footer] button${conditionalString(
isButtonDisabled && '[disabled]'
)}`
);
};
export const fillSsoConnectorCreationModal = async (
page: Page,
{ connectorFactoryName, connectorName }: SsoConnectorTestCase
) => {
// 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`,
{ text: connectorFactoryName }
);
// Button should be disabled util form is filled.
await expect(findModalFooterButton(true)).resolves.toBeTruthy();
await expect(page).toFill(
'.ReactModalPortal input[type=text][name=connectorName]',
connectorName
);
// Button should enabled.
const createButton = await findModalFooterButton();
await createButton?.click();
};

View file

@ -0,0 +1,43 @@
// Will extend this type definition later since we are going to configure the SSO connectors with specific values.
export type SsoConnectorTestCase = {
connectorName: string;
connectorFactoryName: string;
};
const microsoftEntraIdName = 'Microsoft Entra ID';
const microsoftEntraID: SsoConnectorTestCase = {
connectorName: microsoftEntraIdName,
connectorFactoryName: microsoftEntraIdName,
};
const googleWorkspaceName = 'Google Workspace';
const googleWorkspace: SsoConnectorTestCase = {
connectorName: googleWorkspaceName,
connectorFactoryName: googleWorkspaceName,
};
const oktaName = 'Okta';
const okta: SsoConnectorTestCase = {
connectorName: oktaName,
connectorFactoryName: oktaName,
};
const oidcName = 'OIDC';
const oidc: SsoConnectorTestCase = {
connectorName: oidcName,
connectorFactoryName: oidcName,
};
const samlName = 'SAML';
const saml: SsoConnectorTestCase = {
connectorName: samlName,
connectorFactoryName: samlName,
};
export const ssoConnectorTestCases: SsoConnectorTestCase[] = [
microsoftEntraID,
googleWorkspace,
okta,
oidc,
saml,
];

View file

@ -0,0 +1,142 @@
import { logtoConsoleUrl as logtoConsoleUrlString } from '#src/constants.js';
import {
goToAdminConsole,
expectModalWithTitle,
expectToClickDetailsPageOption,
expectConfirmModalAndAct,
} from '#src/ui-helpers/index.js';
import { expectNavigation, appendPathname } from '#src/utils.js';
import { findModalFooterButton, fillSsoConnectorCreationModal } from './helpers.js';
import { ssoConnectorTestCases } from './sso-connectors-test-cases.js';
await page.setViewport({ width: 1920, height: 1080 });
describe('create SSO connectors', () => {
const logtoConsoleUrl = new URL(logtoConsoleUrlString);
beforeAll(async () => {
// Enter admin console
await goToAdminConsole();
});
it('navigate to Enterprise SSO connectors listing page', async () => {
await expectNavigation(
page.goto(appendPathname('/console/enterprise-sso', logtoConsoleUrl).href)
);
await expect(page).toMatchElement(
'div[class$=main] div[class$=headline] div[class$=titleEllipsis]',
{
text: 'Enterprise SSO',
}
);
expect(page.url()).toBe(new URL(`console/enterprise-sso`, logtoConsoleUrl).href);
});
it('can open create SSO connector modal from table placeholder and create the first SSO connector', async () => {
// When no SSO connector is created, use the create button in placeholder.
await expect(page).toClick('table div[class$=placeholder] button span', {
text: 'Add enterprise connector',
});
await expectModalWithTitle(page, 'Add enterprise connector');
await fillSsoConnectorCreationModal(page, ssoConnectorTestCases[0]!);
await page.waitForNavigation({ waitUntil: 'networkidle0' });
// Come back to Enterprise SSO listing page.
await page.goto(appendPathname('/console/enterprise-sso', logtoConsoleUrl).href);
});
it.each(ssoConnectorTestCases.slice(1))(
'create other SSO connectors %p',
async (ssoConnector) => {
await page.waitForNavigation({ waitUntil: 'networkidle0' });
// When there are existing SSO connector(s), use the create button in page header.
await expect(page).toClick('div[class$=main] div[class$=headline] button[type=button] span', {
text: 'Add enterprise connector',
});
await expectModalWithTitle(page, 'Add enterprise connector');
await fillSsoConnectorCreationModal(page, ssoConnector);
await page.waitForNavigation({ waitUntil: 'networkidle0' });
// Come back to Enterprise SSO listing page.
await page.goto(appendPathname('/console/enterprise-sso', logtoConsoleUrl).href);
}
);
it('should block the create of SSO connector with a duplicated name', async () => {
await page.waitForNavigation({ waitUntil: 'networkidle0' });
// When there are existing SSO connector(s), use the create button in page header.
await expect(page).toClick('div[class$=main] div[class$=headline] button[type=button] span', {
text: 'Add enterprise connector',
});
await expectModalWithTitle(page, 'Add enterprise connector');
/**
* 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 { connectorName } = ssoConnectorTestCases[1]!;
await fillSsoConnectorCreationModal(page, {
connectorFactoryName,
connectorName,
});
// Error message should be shown.
await expect(page).toMatchElement(
'.ReactModalPortal div[class$=field] div[class$=errorMessage]',
{
text: 'Connector name already exists. Please choose a different name.',
}
);
await expect(findModalFooterButton(true)).resolves.toBeTruthy();
await expect(page).toFill(
'.ReactModalPortal input[type=text][name=connectorName]',
`${connectorName} (1)`
);
// Button should enabled.
const createButton = await findModalFooterButton();
await createButton?.click();
// Wait until the user is redirected to the details page.
await page.waitForNavigation({ waitUntil: 'networkidle0' });
/**
* If the page still shows the modal, the URL ends with `/enterprise-sso/create`;
* if the SSO connector is successfully created, user is redirected to the details
* page `/enterprise-sso/${id}` and then automatically be redirected to `/enterprise-sso/${id}/connection` (default tab).
*/
expect(page.url().endsWith('/enterprise-sso/create')).toBeFalsy();
await page.waitForNavigation({ waitUntil: 'networkidle0' });
expect(page.url().endsWith('/connection')).toBeTruthy();
});
it('can delete an SSO connector from details page', async () => {
// Delete connector
await expectToClickDetailsPageOption(page, 'Delete');
await expectConfirmModalAndAct(page, {
title: 'Delete enterprise SSO connector',
actionText: 'Delete',
});
// Wait to navigate to the connector list page
await page.waitForNavigation({ waitUntil: 'networkidle0' });
expect(page.url()).toBe(new URL(`console/enterprise-sso`, logtoConsoleUrl).href);
});
});