204 lines
8.9 KiB
JavaScript
204 lines
8.9 KiB
JavaScript
// #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('release screen separates a validated publication from unsaved dashboard edits', async ({ authPage }) => {
|
|
await authPage.route('**/api/git/repositories/*/generate-message?*', async (route) => {
|
|
const requestUrl = new URL(route.request().url());
|
|
if (requestUrl.searchParams.get('purpose') !== 'summary') {
|
|
await route.continue();
|
|
return;
|
|
}
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
summary: '- Изменено **название** дашборда.\n- Разрешён параметр `allow_dml`.',
|
|
}),
|
|
});
|
|
});
|
|
await authPage.goto(`${FRONTEND_URL}/git`);
|
|
const dashboardRow = authPage.locator('tr').filter({ hasText: "World Bank's Data" }).first();
|
|
await expect(dashboardRow).toBeVisible();
|
|
await dashboardRow.getByRole('button', { name: /Управление Git|Manage Git/ }).click();
|
|
|
|
const dialog = authPage.getByRole('dialog', { name: /Управление Git|Git management/ });
|
|
await expect(dialog.getByText(/Кандидат на публикацию|Publication candidate/)).toBeVisible();
|
|
await expect(dialog.getByRole('button', { name: /Опубликовать проверенную версию|Publish validated version/ })).toBeVisible();
|
|
await expect(dialog.getByText(/Есть несохранённые изменения|There are unsaved changes/)).toBeVisible();
|
|
await expect(dialog.getByText(/в публикацию не войдут|will not be included in the current publication/)).toBeVisible();
|
|
await expect(dialog).not.toContainText('.superset-tools-fingerprint');
|
|
await expect(dialog).not.toContainText('metadata.yaml');
|
|
|
|
await dialog.getByRole('button', { name: /Перейти к сохранению|Go to saving/ }).click();
|
|
await expect(dialog.locator('#git-commit-message')).toBeFocused();
|
|
await expect(dialog.getByRole('button', { name: /^Сохранить версию$|^Save version$/ })).toBeVisible();
|
|
await expect(dialog.getByText(/Ключевые изменения|Key changes/)).toBeVisible();
|
|
const renderedSummary = dialog.getByTestId('workspace-summary-markdown');
|
|
await expect(renderedSummary.locator('li')).toHaveCount(2);
|
|
await expect(renderedSummary.locator('strong')).toHaveText('название');
|
|
await expect(renderedSummary.locator('code')).toHaveText('allow_dml');
|
|
await expect(renderedSummary).not.toContainText('**название**');
|
|
const technicalDiff = dialog.getByText(/Технический YAML diff|Technical YAML diff/).locator('..');
|
|
await expect(technicalDiff).not.toHaveAttribute('open', '');
|
|
});
|
|
|
|
test('checkout conflict state exposes scoped recovery actions', async ({ page }) => {
|
|
await page.setContent(`
|
|
<section role="alert" aria-label="checkout conflict">
|
|
<p>Cannot switch branch: uncommitted changes would be overwritten.</p>
|
|
<button>Сохранить версию (Commit)</button>
|
|
<button>Inspect files</button>
|
|
<button>Dismiss</button>
|
|
</section>
|
|
`);
|
|
|
|
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(`
|
|
<div role="dialog" aria-label="Repository has an unfinished merge">
|
|
<p>Resolve conflicts before continuing.</p>
|
|
<button>Refresh</button>
|
|
<button>Copy commands</button>
|
|
<button>Open conflict resolver</button>
|
|
<button>Abort merge</button>
|
|
<button>Continue merge</button>
|
|
<button>Close</button>
|
|
</div>
|
|
`);
|
|
|
|
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(`
|
|
<section role="alert" aria-label="push rejected">
|
|
<p>Push rejected: remote branch contains newer commits.</p>
|
|
<button>Pull</button>
|
|
<button>Open conflict resolver</button>
|
|
<button>Retry push</button>
|
|
</section>
|
|
`);
|
|
|
|
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(`
|
|
<section aria-label="Git repository setup">
|
|
<button>Create new</button>
|
|
<button>Connect existing</button>
|
|
<p>GitFlow setup</p>
|
|
<p>Default branch: prod</p>
|
|
<label>Remote URL <input aria-label="Remote URL" value="ftp://bad-url" /></label>
|
|
<p role="alert">Remote URL must start with https://, ssh://, or git@.</p>
|
|
</section>
|
|
`);
|
|
|
|
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
|