330 lines
11 KiB
TypeScript
330 lines
11 KiB
TypeScript
import { test, expect } from './fixtures/auth.fixture';
|
|
import { navigateAndWait, assertPageHasContent } from './helpers/nav.helper';
|
|
|
|
/**
|
|
* E2E tests for the Identity Providers settings page.
|
|
*
|
|
* These tests use the auth fixture which mocks the backend API.
|
|
* The MockIdentityProviderClient in app.config.ts serves mock data,
|
|
* so these tests verify UI rendering and interaction without a live backend.
|
|
*/
|
|
|
|
const sampleLdapProvider = {
|
|
id: 'e2e-ldap-id',
|
|
name: 'E2E LDAP',
|
|
type: 'ldap',
|
|
enabled: true,
|
|
configuration: {
|
|
host: 'ldap.e2e.test',
|
|
port: '389',
|
|
bindDn: 'cn=admin,dc=e2e,dc=test',
|
|
bindPassword: 'secret',
|
|
searchBase: 'dc=e2e,dc=test',
|
|
},
|
|
description: 'E2E LDAP test provider',
|
|
healthStatus: 'healthy',
|
|
createdAt: '2026-02-24T00:00:00Z',
|
|
updatedAt: '2026-02-24T00:00:00Z',
|
|
createdBy: 'e2e-admin',
|
|
updatedBy: 'e2e-admin',
|
|
};
|
|
|
|
const sampleSamlProvider = {
|
|
id: 'e2e-saml-id',
|
|
name: 'E2E SAML',
|
|
type: 'saml',
|
|
enabled: true,
|
|
configuration: {
|
|
spEntityId: 'stellaops-e2e-sp',
|
|
idpEntityId: 'https://idp.e2e.test',
|
|
idpSsoUrl: 'https://idp.e2e.test/sso',
|
|
},
|
|
description: 'E2E SAML test provider',
|
|
healthStatus: 'healthy',
|
|
createdAt: '2026-02-24T00:00:00Z',
|
|
updatedAt: '2026-02-24T00:00:00Z',
|
|
createdBy: 'e2e-admin',
|
|
updatedBy: 'e2e-admin',
|
|
};
|
|
|
|
const sampleOidcProvider = {
|
|
id: 'e2e-oidc-id',
|
|
name: 'E2E OIDC',
|
|
type: 'oidc',
|
|
enabled: false,
|
|
configuration: {
|
|
authority: 'https://oidc.e2e.test',
|
|
clientId: 'stellaops-e2e',
|
|
clientSecret: 'e2e-secret',
|
|
},
|
|
description: 'E2E OIDC test provider',
|
|
healthStatus: 'disabled',
|
|
createdAt: '2026-02-24T00:00:00Z',
|
|
updatedAt: '2026-02-24T00:00:00Z',
|
|
createdBy: 'e2e-admin',
|
|
updatedBy: 'e2e-admin',
|
|
};
|
|
|
|
test.describe('Identity Providers Settings Page', () => {
|
|
test('should load page and display content', async ({ authenticatedPage: page }) => {
|
|
const errors: string[] = [];
|
|
page.on('console', (msg) => {
|
|
if (msg.type() === 'error' && /NG0\d{3,4}/.test(msg.text())) {
|
|
errors.push(msg.text());
|
|
}
|
|
});
|
|
|
|
// Mock the identity providers API
|
|
await page.route('**/api/v1/platform/identity-providers', (route) => {
|
|
if (route.request().method() === 'GET') {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([sampleLdapProvider, sampleSamlProvider, sampleOidcProvider]),
|
|
});
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
await page.route('**/api/v1/platform/identity-providers/types', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([
|
|
{ type: 'standard', displayName: 'Standard', requiredFields: [], optionalFields: [] },
|
|
{
|
|
type: 'ldap',
|
|
displayName: 'LDAP / Active Directory',
|
|
requiredFields: [
|
|
{ name: 'host', displayName: 'Host', fieldType: 'text', defaultValue: null, description: null },
|
|
{ name: 'port', displayName: 'Port', fieldType: 'number', defaultValue: '389', description: null },
|
|
{ name: 'bindDn', displayName: 'Bind DN', fieldType: 'text', defaultValue: null, description: null },
|
|
{ name: 'bindPassword', displayName: 'Bind Password', fieldType: 'secret', defaultValue: null, description: null },
|
|
{ name: 'searchBase', displayName: 'Search Base', fieldType: 'text', defaultValue: null, description: null },
|
|
],
|
|
optionalFields: [],
|
|
},
|
|
{
|
|
type: 'saml',
|
|
displayName: 'SAML 2.0',
|
|
requiredFields: [
|
|
{ name: 'spEntityId', displayName: 'SP Entity ID', fieldType: 'text', defaultValue: null, description: null },
|
|
{ name: 'idpEntityId', displayName: 'IdP Entity ID', fieldType: 'text', defaultValue: null, description: null },
|
|
],
|
|
optionalFields: [],
|
|
},
|
|
{
|
|
type: 'oidc',
|
|
displayName: 'OpenID Connect',
|
|
requiredFields: [
|
|
{ name: 'authority', displayName: 'Authority', fieldType: 'url', defaultValue: null, description: null },
|
|
{ name: 'clientId', displayName: 'Client ID', fieldType: 'text', defaultValue: null, description: null },
|
|
],
|
|
optionalFields: [],
|
|
},
|
|
]),
|
|
});
|
|
});
|
|
|
|
await navigateAndWait(page, '/settings/identity-providers', { timeout: 30_000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
await assertPageHasContent(page);
|
|
expect(errors).toHaveLength(0);
|
|
});
|
|
|
|
test('should show empty state with no providers', async ({ authenticatedPage: page }) => {
|
|
await page.route('**/api/v1/platform/identity-providers', (route) => {
|
|
if (route.request().method() === 'GET') {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([]),
|
|
});
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
await page.route('**/api/v1/platform/identity-providers/types', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([]),
|
|
});
|
|
});
|
|
|
|
await navigateAndWait(page, '/settings/identity-providers', { timeout: 30_000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
const emptyState = page.locator('.idp-empty-state');
|
|
if (await emptyState.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await expect(emptyState).toContainText('No identity providers');
|
|
}
|
|
});
|
|
|
|
test('should display provider cards with correct type badges', async ({ authenticatedPage: page }) => {
|
|
await page.route('**/api/v1/platform/identity-providers', (route) => {
|
|
if (route.request().method() === 'GET') {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([sampleLdapProvider, sampleSamlProvider, sampleOidcProvider]),
|
|
});
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
await page.route('**/api/v1/platform/identity-providers/types', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([]),
|
|
});
|
|
});
|
|
|
|
await navigateAndWait(page, '/settings/identity-providers', { timeout: 30_000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Verify provider names are visible
|
|
const ldapName = page.locator('text=E2E LDAP').first();
|
|
const samlName = page.locator('text=E2E SAML').first();
|
|
const oidcName = page.locator('text=E2E OIDC').first();
|
|
|
|
if (await ldapName.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await expect(ldapName).toBeVisible();
|
|
}
|
|
if (await samlName.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await expect(samlName).toBeVisible();
|
|
}
|
|
if (await oidcName.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await expect(oidcName).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('should open add provider wizard on button click', async ({ authenticatedPage: page }) => {
|
|
await page.route('**/api/v1/platform/identity-providers', (route) => {
|
|
if (route.request().method() === 'GET') {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([]),
|
|
});
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
await page.route('**/api/v1/platform/identity-providers/types', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([
|
|
{ type: 'standard', displayName: 'Standard', requiredFields: [], optionalFields: [] },
|
|
{ type: 'ldap', displayName: 'LDAP', requiredFields: [], optionalFields: [] },
|
|
{ type: 'saml', displayName: 'SAML', requiredFields: [], optionalFields: [] },
|
|
{ type: 'oidc', displayName: 'OIDC', requiredFields: [], optionalFields: [] },
|
|
]),
|
|
});
|
|
});
|
|
|
|
await navigateAndWait(page, '/settings/identity-providers', { timeout: 30_000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
const addButton = page.locator('button:has-text("Add Provider")').first();
|
|
if (await addButton.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await addButton.click();
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Wizard should be visible
|
|
const wizard = page.locator('app-add-provider-wizard, .wizard-overlay').first();
|
|
if (await wizard.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await expect(wizard).toBeVisible();
|
|
}
|
|
}
|
|
});
|
|
|
|
test('should handle enable/disable toggle', async ({ authenticatedPage: page }) => {
|
|
await page.route('**/api/v1/platform/identity-providers', (route) => {
|
|
if (route.request().method() === 'GET') {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([sampleLdapProvider]),
|
|
});
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
await page.route('**/api/v1/platform/identity-providers/types', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([]),
|
|
});
|
|
});
|
|
|
|
// Mock disable endpoint
|
|
await page.route('**/api/v1/platform/identity-providers/*/disable', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ ...sampleLdapProvider, enabled: false, healthStatus: 'disabled' }),
|
|
});
|
|
});
|
|
|
|
await navigateAndWait(page, '/settings/identity-providers', { timeout: 30_000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Find disable/enable toggle button
|
|
const toggleBtn = page.locator('button:has-text("Disable")').first();
|
|
if (await toggleBtn.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await toggleBtn.click();
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
});
|
|
|
|
test('should handle delete provider', async ({ authenticatedPage: page }) => {
|
|
await page.route('**/api/v1/platform/identity-providers', (route) => {
|
|
if (route.request().method() === 'GET') {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([sampleLdapProvider]),
|
|
});
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
await page.route('**/api/v1/platform/identity-providers/types', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([]),
|
|
});
|
|
});
|
|
|
|
// Mock delete endpoint
|
|
await page.route('**/api/v1/platform/identity-providers/*', (route) => {
|
|
if (route.request().method() === 'DELETE') {
|
|
route.fulfill({ status: 204 });
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
await navigateAndWait(page, '/settings/identity-providers', { timeout: 30_000 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
const deleteBtn = page.locator('button:has-text("Delete")').first();
|
|
if (await deleteBtn.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await deleteBtn.click();
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
});
|
|
});
|