chore: clean .gitignore cache
This commit is contained in:
@@ -1,493 +0,0 @@
|
||||
/**
|
||||
* Proxy + Certificate Integration E2E Tests (Phase 6.2)
|
||||
*
|
||||
* Tests for proxy host and SSL certificate integration workflows.
|
||||
* Covers certificate assignment, ACME challenges, renewal, and edge cases.
|
||||
*
|
||||
* Test Categories (15-18 tests):
|
||||
* - Group A: Certificate Assignment (4 tests)
|
||||
* - Group B: ACME Flow Integration (4 tests)
|
||||
* - Group C: Certificate Lifecycle (4 tests)
|
||||
* - Group D: Error Handling & Edge Cases (4 tests)
|
||||
*
|
||||
* API Endpoints:
|
||||
* - GET/POST/DELETE /api/v1/certificates
|
||||
* - GET/POST/PUT/DELETE /api/v1/proxy-hosts
|
||||
* - GET/POST /api/v1/dns-providers
|
||||
*/
|
||||
|
||||
import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
|
||||
import {
|
||||
generateCertificate,
|
||||
generateWildcardCertificate,
|
||||
customCertificateMock,
|
||||
selfSignedTestCert,
|
||||
letsEncryptCertificate,
|
||||
} from '../fixtures/certificates';
|
||||
import { generateProxyHost } from '../fixtures/proxy-hosts';
|
||||
import {
|
||||
waitForToast,
|
||||
waitForLoadingComplete,
|
||||
waitForAPIResponse,
|
||||
waitForModal,
|
||||
clickAndWaitForResponse,
|
||||
} from '../utils/wait-helpers';
|
||||
|
||||
/**
|
||||
* Selectors for Certificate and Proxy Host pages
|
||||
*/
|
||||
const SELECTORS = {
|
||||
// Certificate Page
|
||||
certPageTitle: 'h1',
|
||||
uploadCertButton: 'button:has-text("Upload Certificate"), button:has-text("Add Certificate")',
|
||||
requestCertButton: 'button:has-text("Request Certificate")',
|
||||
certTable: '[data-testid="certificate-table"], table',
|
||||
certRow: '[data-testid="certificate-row"], tbody tr',
|
||||
certDeleteBtn: '[data-testid="cert-delete-btn"], button[aria-label*="Delete"]',
|
||||
certViewBtn: '[data-testid="cert-view-btn"], button[aria-label*="View"]',
|
||||
|
||||
// Proxy Host Page
|
||||
proxyPageTitle: 'h1',
|
||||
createProxyButton: 'button:has-text("Create Proxy Host"), button:has-text("Add Proxy Host")',
|
||||
proxyTable: '[data-testid="proxy-host-table"], table',
|
||||
proxyRow: '[data-testid="proxy-host-row"], tbody tr',
|
||||
proxyEditBtn: '[data-testid="proxy-edit-btn"], button[aria-label*="Edit"]',
|
||||
|
||||
// Form Fields
|
||||
domainInput: 'input[name="domains"], #domains, input[placeholder*="domain"]',
|
||||
certTypeSelect: 'select[name="type"], #cert-type',
|
||||
certSelectDropdown: '[data-testid="cert-select"], select[name="certificate_id"]',
|
||||
certFileInput: 'input[type="file"][name="certificate"]',
|
||||
keyFileInput: 'input[type="file"][name="privateKey"]',
|
||||
forceSSLCheckbox: 'input[name="force_ssl"], input[type="checkbox"][id*="ssl"]',
|
||||
|
||||
// Dialog/Modal
|
||||
confirmDialog: '[role="dialog"], [role="alertdialog"]',
|
||||
confirmButton: 'button:has-text("Confirm"), button:has-text("Delete"), button:has-text("Yes")',
|
||||
cancelButton: 'button:has-text("Cancel"), button:has-text("No")',
|
||||
saveButton: 'button:has-text("Save"), button[type="submit"]',
|
||||
|
||||
// Status/State
|
||||
loadingSkeleton: '[data-testid="loading-skeleton"], .loading',
|
||||
certStatusBadge: '[data-testid="cert-status"], .badge',
|
||||
expiryWarning: '[data-testid="expiry-warning"], .warning',
|
||||
};
|
||||
|
||||
test.describe('Proxy + Certificate Integration', () => {
|
||||
// ===========================================================================
|
||||
// Group A: Certificate Assignment (4 tests)
|
||||
// ===========================================================================
|
||||
test.describe('Group A: Certificate Assignment', () => {
|
||||
test('should assign custom certificate to proxy host', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
// Create a proxy host first
|
||||
const proxyInput = generateProxyHost({ scheme: 'https' });
|
||||
let createdProxy: { domain: string };
|
||||
|
||||
await test.step('Create proxy host via API', async () => {
|
||||
createdProxy = await testData.createProxyHost({
|
||||
domain: proxyInput.domain,
|
||||
forwardHost: proxyInput.forwardHost,
|
||||
forwardPort: proxyInput.forwardPort,
|
||||
scheme: 'https',
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to certificates page', async () => {
|
||||
await page.goto('/certificates');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify certificates page loads', async () => {
|
||||
const heading = page.locator(SELECTORS.certPageTitle).first();
|
||||
await expect(heading).toContainText(/certificate/i);
|
||||
});
|
||||
|
||||
await test.step('Navigate to proxy hosts and verify', async () => {
|
||||
await page.goto('/proxy-hosts');
|
||||
await waitForLoadingComplete(page);
|
||||
await expect(page.getByText(createdProxy.domain)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should assign Let\'s Encrypt certificate to proxy host', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const proxyInput = generateProxyHost({ scheme: 'https' });
|
||||
let createdProxy: { domain: string };
|
||||
|
||||
await test.step('Create proxy host', async () => {
|
||||
createdProxy = await testData.createProxyHost({
|
||||
domain: proxyInput.domain,
|
||||
forwardHost: proxyInput.forwardHost,
|
||||
forwardPort: proxyInput.forwardPort,
|
||||
scheme: 'https',
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to proxy hosts', async () => {
|
||||
await page.goto('/proxy-hosts');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify proxy host is visible with HTTPS scheme', async () => {
|
||||
await expect(page.getByText(createdProxy.domain)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should display SSL status indicator on proxy host', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const proxyInput = generateProxyHost({ scheme: 'https', forceSSL: true });
|
||||
let createdProxy: { domain: string };
|
||||
|
||||
await test.step('Create HTTPS proxy host', async () => {
|
||||
createdProxy = await testData.createProxyHost({
|
||||
domain: proxyInput.domain,
|
||||
forwardHost: proxyInput.forwardHost,
|
||||
forwardPort: proxyInput.forwardPort,
|
||||
scheme: 'https',
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to proxy hosts', async () => {
|
||||
await page.goto('/proxy-hosts');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify SSL indicator is shown', async () => {
|
||||
const proxyRow = page.locator(SELECTORS.proxyRow).filter({
|
||||
hasText: createdProxy.domain,
|
||||
});
|
||||
await expect(proxyRow).toBeVisible();
|
||||
|
||||
// Look for HTTPS or SSL indicator (lock icon, badge, etc.)
|
||||
const sslIndicator = proxyRow.locator('svg[data-testid*="lock"], .ssl-indicator, [aria-label*="SSL"], [aria-label*="HTTPS"]');
|
||||
// This may or may not be present depending on UI implementation
|
||||
});
|
||||
});
|
||||
|
||||
test('should unassign certificate from proxy host', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const proxyInput = generateProxyHost({ scheme: 'http' });
|
||||
let createdProxy: { domain: string };
|
||||
|
||||
await test.step('Create HTTP proxy host', async () => {
|
||||
createdProxy = await testData.createProxyHost({
|
||||
domain: proxyInput.domain,
|
||||
forwardHost: proxyInput.forwardHost,
|
||||
forwardPort: proxyInput.forwardPort,
|
||||
scheme: 'http',
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to proxy hosts', async () => {
|
||||
await page.goto('/proxy-hosts');
|
||||
await waitForLoadingComplete(page);
|
||||
await expect(page.getByText(createdProxy.domain)).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===========================================================================
|
||||
// Group B: ACME Flow Integration (4 tests)
|
||||
// ===========================================================================
|
||||
test.describe('Group B: ACME Flow Integration', () => {
|
||||
test('should trigger HTTP-01 challenge for new certificate request', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const proxyInput = generateProxyHost({ scheme: 'https' });
|
||||
|
||||
await test.step('Create proxy host for ACME challenge', async () => {
|
||||
await testData.createProxyHost({
|
||||
domain: proxyInput.domain,
|
||||
forwardHost: proxyInput.forwardHost,
|
||||
forwardPort: proxyInput.forwardPort,
|
||||
scheme: 'https',
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to certificates page', async () => {
|
||||
await page.goto('/certificates');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify certificates page is accessible', async () => {
|
||||
const heading = page.locator('h1, h2').first();
|
||||
await expect(heading).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle DNS-01 challenge for wildcard certificate', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
// DNS provider is required for DNS-01 challenges
|
||||
await test.step('Create DNS provider', async () => {
|
||||
await testData.createDNSProvider({
|
||||
providerType: 'manual',
|
||||
name: 'Wildcard-DNS-Provider',
|
||||
credentials: {},
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to certificates', async () => {
|
||||
await page.goto('/certificates');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify page loads', async () => {
|
||||
const heading = page.locator('h1, h2').first();
|
||||
await expect(heading).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should show ACME challenge status during certificate issuance', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
await test.step('Navigate to certificates page', async () => {
|
||||
await page.goto('/certificates');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify page structure', async () => {
|
||||
// Check for certificate list or empty state
|
||||
const content = page.locator('main, .content, [role="main"]').first();
|
||||
await expect(content).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should link DNS provider for automated DNS-01 challenges', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
await test.step('Create DNS provider', async () => {
|
||||
await testData.createDNSProvider({
|
||||
providerType: 'cloudflare',
|
||||
name: 'Cloudflare-DNS-Test',
|
||||
credentials: {
|
||||
api_token: 'test-token-placeholder',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to DNS providers', async () => {
|
||||
await page.goto('/dns-providers');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify DNS provider exists', async () => {
|
||||
// The provider name contains namespace prefix
|
||||
const content = page.locator('main, table, .content').first();
|
||||
await expect(content).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===========================================================================
|
||||
// Group C: Certificate Lifecycle (4 tests)
|
||||
// ===========================================================================
|
||||
test.describe('Group C: Certificate Lifecycle', () => {
|
||||
test('should display certificate expiry warning', async ({
|
||||
page,
|
||||
adminUser,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
await test.step('Navigate to certificates page', async () => {
|
||||
await page.goto('/certificates');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify certificates page loads', async () => {
|
||||
const heading = page.locator('h1, h2').first();
|
||||
await expect(heading).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should show certificate renewal option', async ({
|
||||
page,
|
||||
adminUser,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
await test.step('Navigate to certificates', async () => {
|
||||
await page.goto('/certificates');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
// The renewal option would be visible on a Let's Encrypt certificate row
|
||||
await test.step('Verify page is accessible', async () => {
|
||||
const content = page.locator('main, .content').first();
|
||||
await expect(content).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle certificate deletion with proxy host fallback', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const proxyInput = generateProxyHost({ scheme: 'http' });
|
||||
let createdProxy: { domain: string };
|
||||
|
||||
await test.step('Create proxy host', async () => {
|
||||
createdProxy = await testData.createProxyHost({
|
||||
domain: proxyInput.domain,
|
||||
forwardHost: proxyInput.forwardHost,
|
||||
forwardPort: proxyInput.forwardPort,
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Verify proxy host works without certificate', async () => {
|
||||
await page.goto('/proxy-hosts');
|
||||
await waitForLoadingComplete(page);
|
||||
await expect(page.getByText(createdProxy.domain)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should auto-renew expiring certificates', async ({
|
||||
page,
|
||||
adminUser,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
// This test verifies the auto-renewal configuration is visible
|
||||
await test.step('Navigate to settings', async () => {
|
||||
await page.goto('/settings');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify settings page loads', async () => {
|
||||
const heading = page.locator('h1, h2').first();
|
||||
await expect(heading).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===========================================================================
|
||||
// Group D: Error Handling & Edge Cases (4 tests)
|
||||
// ===========================================================================
|
||||
test.describe('Group D: Error Handling & Edge Cases', () => {
|
||||
test('should handle invalid certificate upload gracefully', async ({
|
||||
page,
|
||||
adminUser,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
await test.step('Navigate to certificates', async () => {
|
||||
await page.goto('/certificates');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify page loads without errors', async () => {
|
||||
const content = page.locator('main, .content').first();
|
||||
await expect(content).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle mismatched certificate and private key', async ({
|
||||
page,
|
||||
adminUser,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
await test.step('Navigate to certificates', async () => {
|
||||
await page.goto('/certificates');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify certificates page', async () => {
|
||||
const heading = page.locator('h1, h2').first();
|
||||
await expect(heading).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should prevent assigning expired certificate to proxy host', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const proxyInput = generateProxyHost();
|
||||
let createdProxy: { domain: string };
|
||||
|
||||
await test.step('Create proxy host', async () => {
|
||||
createdProxy = await testData.createProxyHost({
|
||||
domain: proxyInput.domain,
|
||||
forwardHost: proxyInput.forwardHost,
|
||||
forwardPort: proxyInput.forwardPort,
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to proxy hosts', async () => {
|
||||
await page.goto('/proxy-hosts');
|
||||
await waitForLoadingComplete(page);
|
||||
await expect(page.getByText(createdProxy.domain)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle domain mismatch between certificate and proxy host', async ({
|
||||
page,
|
||||
adminUser,
|
||||
testData,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const proxyInput = generateProxyHost();
|
||||
let createdProxy: { domain: string };
|
||||
|
||||
await test.step('Create proxy host', async () => {
|
||||
createdProxy = await testData.createProxyHost({
|
||||
domain: proxyInput.domain,
|
||||
forwardHost: proxyInput.forwardHost,
|
||||
forwardPort: proxyInput.forwardPort,
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to proxy hosts', async () => {
|
||||
await page.goto('/proxy-hosts');
|
||||
await waitForLoadingComplete(page);
|
||||
});
|
||||
|
||||
await test.step('Verify proxy host exists', async () => {
|
||||
await expect(page.getByText(createdProxy.domain)).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user