// #region GitE2E [C:3] [TYPE Test] [SEMANTICS e2e, git, integration, config] // @BRIEF E2E tests for Git integration — config CRUD, connection test. // @RELATION BINDS_TO -> [GitSettingsPage] // @UX_STATE ConfigCreated -> Git server appears in configured list. // @UX_STATE ConnectionTested -> Success/failure toast feedback. import { test, expect } from '../fixtures/auth.fixture.js'; import { apiGet, apiPost, apiDelete } from '../helpers/api.helper.js'; const FRONTEND_URL = process.env.FRONTEND_URL || 'http://127.0.0.1:8102'; const GITEA_URL = process.env.GITEA_URL || 'https://git.bebesh.ru'; const GITEA_TOKEN = process.env.GITEA_TOKEN || 'd5e3a0bea62121dcafbf33070e85d65e8a383c20'; test.describe('Git Integration', () => { test('should have git config visible in settings UI', async ({ authPage }) => { await authPage.goto(`${FRONTEND_URL}/settings#/settings/git`); await authPage.waitForSelector('text=Интеграция Git', { timeout: 10_000 }); // Configured servers section visible await expect(authPage.getByText(/Настроенные серверы/)).toBeVisible(); // Add git server form visible await expect(authPage.getByText(/Добавить Git-сервер|Add Git server/)).toBeVisible(); }); test('should create git config via API and verify in UI', async ({ authPage }) => { const configName = `E2E-Gitea-${Date.now()}`; // Create via API const config = await apiPost('/api/git/config', { name: configName, provider: 'GITEA', url: GITEA_URL, pat: GITEA_TOKEN, default_repository: 'busya/superset-tools', default_branch: 'main', }); expect(config).toBeDefined(); expect(config.id).toBeTruthy(); expect(config.provider).toBe('GITEA'); // Verify in UI await authPage.goto(`${FRONTEND_URL}/settings#/settings/git`); await authPage.waitForSelector('text=Интеграция Git', { timeout: 10_000 }); await expect(authPage.getByText(configName).first()).toBeVisible({ timeout: 10_000 }); // Cleanup await apiDelete(`/api/git/config/${config.id}`); }); test('should test git connection successfully', async ({ authPage }) => { // Get existing configs const configs = await apiGet('/api/git/config'); if (configs.length === 0) { console.log('[E2E] No git configs to test, skipping'); return; } const config = configs[0]; const result = await apiPost('/api/git/config/test', { name: config.name, provider: config.provider, url: config.url, pat: GITEA_TOKEN, config_id: config.id, }); expect(result.status).toBe('success'); }); test('should list Gitea repositories', async ({ authPage }) => { const configs = await apiGet('/api/git/config'); if (configs.length === 0) { console.log('[E2E] No git configs to list repos, skipping'); return; } const config = configs[0]; if (config.provider !== 'GITEA') { console.log('[E2E] Provider is not GITEA, skipping'); return; } const repos = await apiGet(`/api/git/config/${config.id}/gitea/repos`); expect(Array.isArray(repos)).toBeTruthy(); expect(repos.length).toBeGreaterThan(0); // Should include the superset-tools repo const hasSsTools = repos.some(r => r.full_name?.includes('superset-tools')); expect(hasSsTools).toBeTruthy(); }); }); test.describe('Git dashboard recovery browser states', () => { test('checkout conflict state exposes scoped recovery actions', async ({ page }) => { await page.setContent(`

Cannot switch branch: uncommitted changes would be overwritten.

`); await expect(page.getByRole('alert', { name: 'checkout conflict' })).toBeVisible(); await expect(page.getByRole('button', { name: /Сохранить версию|Commit/ })).toBeVisible(); await expect(page.getByRole('button', { name: /Inspect files/ })).toBeVisible(); await expect(page.getByRole('button', { name: /Dismiss/ })).toBeVisible(); }); test('unfinished merge state exposes resolver, retry, save, and dismiss actions', async ({ page }) => { await page.setContent(`

Resolve conflicts before continuing.

`); await expect(page.getByRole('dialog', { name: /unfinished merge/ })).toBeVisible(); await expect(page.getByRole('button', { name: /Open conflict resolver/ })).toBeVisible(); await expect(page.getByRole('button', { name: /Refresh/ })).toBeVisible(); await expect(page.getByRole('button', { name: /Continue merge/ })).toBeVisible(); await expect(page.getByRole('button', { name: /Close/ })).toBeVisible(); }); test('push rejected state exposes pull retry guidance', async ({ page }) => { await page.setContent(`

Push rejected: remote branch contains newer commits.

`); await expect(page.getByRole('alert', { name: 'push rejected' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Pull' })).toBeVisible(); await expect(page.getByRole('button', { name: /Retry push/ })).toBeVisible(); }); test('no-repo setup state separates create and connect paths with URL validation', async ({ page }) => { await page.setContent(`

GitFlow setup

Default branch: prod

Remote URL must start with https://, ssh://, or git@.

`); await expect(page.getByRole('region', { name: 'Git repository setup' })).toBeVisible(); await expect(page.getByRole('button', { name: /Create new/ })).toBeVisible(); await expect(page.getByRole('button', { name: /Connect existing/ })).toBeVisible(); await expect(page.getByText(/Default branch: prod/)).toBeVisible(); await expect(page.getByRole('alert')).toContainText(/https:\/\/|ssh:\/\/|git@/); }); }); // #endregion GitE2E