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>
133 lines
4.7 KiB
TypeScript
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]);
|
|
}
|
|
});
|
|
});
|