mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
fix(core,test): fix integration test (#5438)
* fix(core,test): fix integration test fix integration tests * fix(test): remove api timeout remove api timeout
This commit is contained in:
parent
50bf70891d
commit
db6e761490
7 changed files with 92 additions and 59 deletions
|
@ -72,7 +72,7 @@ export default function singleSignOnConnectorsRoutes<T extends AuthedRouter>(
|
|||
koaGuard({
|
||||
body: ssoConnectorCreateGuard,
|
||||
response: SsoConnectors.guard,
|
||||
status: [200, 409, 422],
|
||||
status: [200, 400, 409, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { body } = ctx.guard;
|
||||
|
@ -212,7 +212,7 @@ export default function singleSignOnConnectorsRoutes<T extends AuthedRouter>(
|
|||
params: z.object({ id: z.string().min(1) }),
|
||||
body: ssoConnectorPatchGuard,
|
||||
response: ssoConnectorWithProviderConfigGuard,
|
||||
status: [200, 404, 409, 422],
|
||||
status: [200, 400, 404, 409, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -4,7 +4,6 @@ import { logtoConsoleUrl, logtoUrl, logtoCloudUrl } from '#src/constants.js';
|
|||
|
||||
const api = got.extend({
|
||||
prefixUrl: new URL('/api', logtoUrl),
|
||||
timeout: { response: 5000 }, // The default is 60s which is way too long for tests.
|
||||
});
|
||||
|
||||
export default api;
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
import { appendPath, getEnv } from '@silverhand/essentials';
|
||||
|
||||
export const logtoUrl = getEnv('INTEGRATION_TESTS_LOGTO_URL', 'http://localhost:3001');
|
||||
export const logtoOidcUrl = appendPath(new URL(logtoUrl), 'oidc').toString();
|
||||
export const logtoConsoleUrl = getEnv(
|
||||
'INTEGRATION_TESTS_LOGTO_CONSOLE_URL',
|
||||
'http://localhost:3002'
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import fs from 'node:fs';
|
||||
|
||||
import { conditional, conditionalString } from '@silverhand/essentials';
|
||||
import { type Page } from 'puppeteer';
|
||||
|
||||
import { metadataXml } from '#src/__mocks__/sso-connectors-mock.js';
|
||||
import { expectToSaveChanges, waitForToast } from '#src/ui-helpers/index.js';
|
||||
import { dcls, cls } from '#src/utils.js';
|
||||
|
||||
|
@ -115,7 +118,8 @@ const configureSsoConnectorConnection = async (
|
|||
protocol: Protocol,
|
||||
previewResults: Record<string, string>
|
||||
) => {
|
||||
await expect(page).toFillForm(dcls('form'), formData);
|
||||
await fillSsoConnectorConnectionForm(page, formData, protocol);
|
||||
|
||||
await expectToSaveChanges(page);
|
||||
await waitForToast(page, { text: 'Saved' });
|
||||
|
||||
|
@ -128,6 +132,58 @@ const configureSsoConnectorConnection = async (
|
|||
}
|
||||
};
|
||||
|
||||
const fillSsoConnectorConnectionForm = async (
|
||||
page: Page,
|
||||
formData: Record<string, string>,
|
||||
protocol: Protocol
|
||||
) => {
|
||||
if (protocol === 'OIDC') {
|
||||
await expect(page).toFillForm(dcls('form'), formData);
|
||||
return;
|
||||
}
|
||||
|
||||
// Click on the switch config method link
|
||||
await expect(page).toClick([dcls('form'), 'button'].join(' '));
|
||||
|
||||
// Click on the "Upload the metadata XML file" option
|
||||
const dropdownMenu = await page.waitForSelector(
|
||||
['.ReactModalPortal', dcls('dropdownContainer')].join(' ')
|
||||
);
|
||||
|
||||
await page.evaluate((dropdownMenu) => {
|
||||
if (dropdownMenu) {
|
||||
const optionElement = dropdownMenu.querySelectorAll('div[role=menuitem] div');
|
||||
const uploadMetadataXmlOption = Array.from(optionElement).find(
|
||||
(element) => element.textContent === 'Upload the metadata XML file'
|
||||
);
|
||||
|
||||
if (uploadMetadataXmlOption) {
|
||||
const clickEvent = new MouseEvent('click', { bubbles: true });
|
||||
uploadMetadataXmlOption.dispatchEvent(clickEvent);
|
||||
}
|
||||
}
|
||||
}, dropdownMenu);
|
||||
|
||||
await uploadMetadataXml(page);
|
||||
};
|
||||
|
||||
export const uploadMetadataXml = async (page: Page) => {
|
||||
const fileInputElement = await page.waitForSelector('form input[type=file]');
|
||||
expect(fileInputElement).toBeTruthy();
|
||||
|
||||
if (!fileInputElement) {
|
||||
throw new Error('File input element is not found.');
|
||||
}
|
||||
|
||||
const metadataFilePath = './metadata.xml';
|
||||
fs.writeFileSync(metadataFilePath, metadataXml);
|
||||
|
||||
await fileInputElement.uploadFile(metadataFilePath);
|
||||
|
||||
// Delete the mock file
|
||||
fs.unlinkSync(metadataFilePath);
|
||||
};
|
||||
|
||||
export const findModalFooterButton = async (isButtonDisabled = false) => {
|
||||
return page.waitForSelector(
|
||||
`.ReactModalPortal div[class$=footer] button${conditionalString(
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { logtoIssuer, metadataXml } from '#src/__mocks__/sso-connectors-mock.js';
|
||||
|
||||
// Will extend this type definition later since we are going to configure the SSO connectors with specific values.
|
||||
export type Protocol = 'SAML' | 'OIDC';
|
||||
|
||||
|
@ -12,66 +14,39 @@ export type SsoConnectorTestCase = {
|
|||
const oidcFormData = {
|
||||
clientId: 'client-id',
|
||||
clientSecret: 'client-secret',
|
||||
issuer: logtoIssuer,
|
||||
};
|
||||
|
||||
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',
|
||||
metadata: metadataXml,
|
||||
};
|
||||
|
||||
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';
|
||||
const microsoftEntraID: SsoConnectorTestCase = {
|
||||
connectorName: microsoftEntraIdName,
|
||||
connectorFactoryName: microsoftEntraIdName,
|
||||
protocol: 'SAML',
|
||||
formData: samlFormData,
|
||||
previewResults: samlPreviewResults,
|
||||
};
|
||||
|
||||
const googleWorkspaceName = 'Google Workspace';
|
||||
const googleWorkspace: SsoConnectorTestCase = {
|
||||
connectorName: googleWorkspaceName,
|
||||
connectorFactoryName: googleWorkspaceName,
|
||||
protocol: 'OIDC',
|
||||
formData: oidcFormData,
|
||||
previewResults: oidcPreviewResults,
|
||||
};
|
||||
|
||||
const oktaName = 'Okta';
|
||||
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,
|
||||
'Authorization endpoint': `${logtoIssuer}/auth`,
|
||||
'Token endpoint': `${logtoIssuer}/token`,
|
||||
'User information endpoint': `${logtoIssuer}/me`,
|
||||
'JSON web key set endpoint': `${logtoIssuer}/jwks`,
|
||||
Issuer: logtoIssuer,
|
||||
};
|
||||
|
||||
const oidcName = 'OIDC';
|
||||
|
||||
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' },
|
||||
// To avoid external dependencies we use local logtoOidcUrl as the issuer for all the OIDC based connectors.
|
||||
formData: { ...oidcFormData },
|
||||
previewResults: oidcPreviewResults,
|
||||
};
|
||||
|
||||
// These values are decoded from the metadataXml string.
|
||||
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 samlName = 'SAML';
|
||||
const saml: SsoConnectorTestCase = {
|
||||
connectorName: samlName,
|
||||
|
@ -81,10 +56,4 @@ const saml: SsoConnectorTestCase = {
|
|||
previewResults: samlPreviewResults,
|
||||
};
|
||||
|
||||
export const ssoConnectorTestCases: SsoConnectorTestCase[] = [
|
||||
microsoftEntraID,
|
||||
googleWorkspace,
|
||||
okta,
|
||||
oidc,
|
||||
saml,
|
||||
];
|
||||
export const ssoConnectorTestCases: SsoConnectorTestCase[] = [oidc, saml];
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { getSsoConnectors, deleteSsoConnectorById } from '#src/api/sso-connector.js';
|
||||
import { logtoConsoleUrl as logtoConsoleUrlString } from '#src/constants.js';
|
||||
import {
|
||||
goToAdminConsole,
|
||||
expectModalWithTitle,
|
||||
expectToClickDetailsPageOption,
|
||||
expectConfirmModalAndAct,
|
||||
expectToClickDetailsPageOption,
|
||||
expectToSaveChanges,
|
||||
waitForToast,
|
||||
} from '#src/ui-helpers/index.js';
|
||||
import { expectNavigation, appendPathname, dcls, cls } from '#src/utils.js';
|
||||
|
||||
import { findModalFooterButton, fillSsoConnectorCreationModal } from './helpers.js';
|
||||
import { fillSsoConnectorCreationModal, findModalFooterButton } from './helpers.js';
|
||||
import { ssoConnectorTestCases } from './sso-connectors-test-cases.js';
|
||||
|
||||
await page.setViewport({ width: 1920, height: 1080 });
|
||||
|
@ -32,6 +33,12 @@ describe('create SSO connectors', () => {
|
|||
await goToAdminConsole();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
// Delete all SSO connectors
|
||||
const connectors = await getSsoConnectors();
|
||||
await Promise.all(connectors.map(async ({ id }) => deleteSsoConnectorById(id)));
|
||||
});
|
||||
|
||||
it('navigate to Enterprise SSO connectors listing page', async () => {
|
||||
await expectNavigation(
|
||||
page.goto(appendPathname('/console/enterprise-sso', logtoConsoleUrl).href)
|
||||
|
|
Loading…
Reference in a new issue