Files
git.stella-ops.org/src/Web/StellaOps.Web/tests/e2e/integrations/gitlab-integration.e2e.spec.ts
master 9402f1a558 Fix 22 UI tests: auto-retry assertions instead of point-in-time checks
Problem: After waitForAngular, content assertions ran before Angular's
XHR data loaded. Tests checked textContent('body') at a point when
the table/heading hadn't rendered yet.

Fix: Replace point-in-time checks with Playwright auto-retry assertions:
- expect(locator).toBeVisible({ timeout: 15_000 }) — retries until visible
- expect(locator).toContainText('X', { timeout: 15_000 }) — retries until text appears
- expect(rows.first()).toBeVisible() — retries until table has data

Also: landing page test now uses waitForFunction to detect Angular redirect.

10 files changed, net -45 lines (simpler, more robust assertions).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:04:52 +03:00

133 lines
4.7 KiB
TypeScript

/**
* GitLab Integration — End-to-End Tests
*
* Validates the GitLab SCM connector lifecycle against the real GitLab CE instance:
* 1. Container health + direct probe
* 2. Connector CRUD via API (create, test-connection, health, delete)
* 3. UI: SCM tab shows GitLab row
*
* Workaround for "heavy profile": Instead of checking Docker CLI (which may
* not be available from Playwright workers), we probe GitLab's HTTP endpoint
* directly. If it responds, the tests run; otherwise they skip gracefully.
*/
import { execSync } from 'child_process';
import { test, expect } from './live-auth.fixture';
import {
INTEGRATION_CONFIGS,
createIntegrationViaApi,
cleanupIntegrations,
snap,
waitForAngular,
} from './helpers';
const BASE = process.env['PLAYWRIGHT_BASE_URL'] || 'https://stella-ops.local';
const runId = process.env['E2E_RUN_ID'] || 'run1';
const GITLAB_URL = 'http://127.1.2.7:8929';
/**
* Probe GitLab via HTTP instead of Docker CLI.
* Returns true if GitLab responds (any status < 500) within 3 seconds.
*/
function gitlabReachable(): boolean {
try {
// Use curl (available on both Windows and Linux) with a short timeout
const out = execSync(
`curl -sf -o /dev/null -w "%{http_code}" --connect-timeout 3 ${GITLAB_URL}/`,
{ encoding: 'utf-8', timeout: 5_000 },
).trim();
const code = parseInt(out, 10);
return code > 0 && code < 500;
} catch {
return false;
}
}
const gitlabRunning = gitlabReachable();
// ---------------------------------------------------------------------------
// 1. Reachability
// ---------------------------------------------------------------------------
test.describe('GitLab Integration — Reachability', () => {
test.skip(!gitlabRunning, 'GitLab not reachable at 127.1.2.7:8929');
test('GitLab responds to HTTP probe', async ({ playwright }) => {
const ctx = await playwright.request.newContext({ ignoreHTTPSErrors: true });
try {
const resp = await ctx.get(`${GITLAB_URL}/`, { timeout: 10_000 });
// 302 redirect to login = GitLab is running
expect(resp.status()).toBeLessThan(500);
} finally {
await ctx.dispose();
}
});
});
// ---------------------------------------------------------------------------
// 2. Connector Lifecycle (API)
// ---------------------------------------------------------------------------
test.describe('GitLab Integration — Connector Lifecycle', () => {
test.skip(!gitlabRunning, 'GitLab not reachable');
test('create GitLab integration and verify CRUD operations', async ({ apiRequest }) => {
// Create
const id = await createIntegrationViaApi(apiRequest, INTEGRATION_CONFIGS.gitlab, runId);
expect(id).toBeTruthy();
try {
// Verify created with correct fields
const getResp = await apiRequest.get(`/api/v1/integrations/${id}`);
expect(getResp.status()).toBe(200);
const integration = await getResp.json();
expect(integration.type).toBe(2); // Scm
expect(integration.provider).toBe(201); // GitLabServer
expect(integration.name).toContain('GitLab');
expect(integration.endpoint).toContain('gitlab');
// Test connection — returns structured response
// May return success=false if GitLab requires auth token for /api/v4/version
const testResp = await apiRequest.post(`/api/v1/integrations/${id}/test`);
expect(testResp.status()).toBe(200);
const testBody = await testResp.json();
expect(typeof testBody.success).toBe('boolean');
expect(testBody.message).toBeTruthy();
// Health check — returns structured response
const healthResp = await apiRequest.get(`/api/v1/integrations/${id}/health`);
expect(healthResp.status()).toBe(200);
const healthBody = await healthResp.json();
expect(typeof healthBody.status).toBe('number');
} finally {
await cleanupIntegrations(apiRequest, [id]);
}
});
});
// ---------------------------------------------------------------------------
// 3. UI: SCM Tab
// ---------------------------------------------------------------------------
test.describe('GitLab Integration — UI Verification', () => {
test.skip(!gitlabRunning, 'GitLab not reachable');
test('SCM tab shows GitLab integration', async ({ apiRequest, liveAuthPage: page }) => {
const id = await createIntegrationViaApi(apiRequest, INTEGRATION_CONFIGS.gitlab, `ui-${runId}`);
try {
await page.goto(`${BASE}/setup/integrations/scm`, {
waitUntil: 'domcontentloaded',
timeout: 45_000,
});
await waitForAngular(page);
await expect(page.locator('text=GitLab').first()).toBeVisible({ timeout: 15_000 });
await snap(page, 'gitlab-scm-tab');
} finally {
await cleanupIntegrations(apiRequest, [id]);
}
});
});