chore: publish clean community baseline
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { login, localized } = require('./helpers');
|
||||
|
||||
test('agents page shows demo cards and edit drawer opens', async ({ page }) => {
|
||||
await login(page);
|
||||
await page.goto('/agents');
|
||||
await expect(page.locator('.page-heading')).toHaveText(localized('Agents', 'Агенты'));
|
||||
await expect(page.locator('.agents-info-callout')).toContainText(localized('static agent keys', 'статические ключи агента'));
|
||||
await expect(page.locator('.agent-card-date').first()).toContainText(
|
||||
localized('API Keys page', 'API Keys')
|
||||
);
|
||||
await page.getByRole('button', { name: localized('New agent', 'Новый агент') }).click();
|
||||
await expect(page.locator('.drawer-title')).toHaveText(localized('New agent', 'Новый агент'));
|
||||
await expect(page.locator('.drawer-subtitle')).toContainText(localized('static agent-key boundary', 'статических ключах агента'));
|
||||
await expect(page.locator('[data-i18n="agents.drawer.slug_hint"]')).toContainText(
|
||||
localized('Keep the slug stable', 'Сохраняйте slug стабильным')
|
||||
);
|
||||
await expect(page.locator('.drawer')).toContainText(/mcp/i);
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { createAgent, getCurrentWorkspace, login, localized, uniqueName } = require('./helpers');
|
||||
|
||||
test('api keys page opens create key flow', async ({ page }) => {
|
||||
await login(page);
|
||||
const workspace = await getCurrentWorkspace(page);
|
||||
await createAgent(page, workspace.id, {
|
||||
slug: uniqueName('playwright_keys_agent'),
|
||||
display_name: 'Playwright Keys Agent',
|
||||
description: 'Agent for API key page smoke test.',
|
||||
instructions: {},
|
||||
tool_selection_policy: {},
|
||||
});
|
||||
await page.goto('/api-keys');
|
||||
await expect(page.locator('.page-title')).toHaveText(localized('Agent Keys', 'Ключи агентов'));
|
||||
await expect(page.locator('[data-testid="machine-access-summary"]')).toContainText(
|
||||
localized('static agent keys', 'статические ключи')
|
||||
);
|
||||
await expect(page.locator('#machine-access-note')).toContainText(
|
||||
localized('commercial edition', 'коммерческую редакцию')
|
||||
);
|
||||
await expect(page.locator('#agent-select')).not.toBeDisabled();
|
||||
await expect(page.locator('#btn-create-key')).toBeEnabled();
|
||||
await page.locator('#btn-create-key').click();
|
||||
await expect(page.locator('#modal-create')).toHaveClass(/open/);
|
||||
await expect(page.locator('.modal-title')).toHaveText(localized('Create agent key', 'Создать ключ агента'));
|
||||
await page.locator('#new-key-name').fill(`playwright-${Date.now()}`);
|
||||
await page.locator('#modal-confirm-btn').click();
|
||||
await expect(page.locator('#modal-reveal-body')).toContainText(localized('Copy this key now', 'Скопируйте этот ключ сейчас'));
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
const { expect } = require('@playwright/test');
|
||||
|
||||
const ADMIN_EMAIL = process.env.CRANK_E2E_ADMIN_EMAIL || 'owner@crank.local';
|
||||
const ADMIN_PASSWORD = process.env.CRANK_E2E_ADMIN_PASSWORD || 'change-me-admin-password';
|
||||
|
||||
function localized(en, ru) {
|
||||
return new RegExp(`(?:${en}|${ru})`, 'i');
|
||||
}
|
||||
|
||||
async function login(page) {
|
||||
await page.goto('/login');
|
||||
await expect(page.locator('#login-form')).toBeVisible();
|
||||
await page.locator('#email').fill(ADMIN_EMAIL);
|
||||
await page.locator('#password').fill(ADMIN_PASSWORD);
|
||||
await page.locator('.btn-signin').click();
|
||||
await expect(page).toHaveURL(/\/$/);
|
||||
await expect(page.locator('.page-title, .page-heading').first()).toBeVisible();
|
||||
}
|
||||
|
||||
async function browserJson(page, method, urlPath, body, okStatuses = [200]) {
|
||||
const response = await page.evaluate(async ({ method, urlPath, body }) => {
|
||||
const request = {
|
||||
method,
|
||||
credentials: 'same-origin',
|
||||
headers: {},
|
||||
};
|
||||
if (body !== undefined) {
|
||||
request.headers['Content-Type'] = 'application/json';
|
||||
request.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const result = await fetch(urlPath, request);
|
||||
const text = await result.text();
|
||||
let json = null;
|
||||
try {
|
||||
json = text ? JSON.parse(text) : null;
|
||||
} catch (_error) {
|
||||
json = null;
|
||||
}
|
||||
|
||||
return {
|
||||
status: result.status,
|
||||
text,
|
||||
json,
|
||||
};
|
||||
}, { method, urlPath, body });
|
||||
|
||||
if (!okStatuses.includes(response.status)) {
|
||||
throw new Error(`HTTP ${response.status} ${urlPath}: ${response.text}`);
|
||||
}
|
||||
|
||||
return response.json;
|
||||
}
|
||||
|
||||
async function getSession(page) {
|
||||
return browserJson(page, 'GET', '/api/auth/session');
|
||||
}
|
||||
|
||||
async function getCurrentWorkspace(page) {
|
||||
const session = await getSession(page);
|
||||
const workspaceId = session.current_workspace_id
|
||||
|| (session.memberships && session.memberships[0] && session.memberships[0].workspace.id);
|
||||
const membership = (session.memberships || []).find((item) => item.workspace.id === workspaceId)
|
||||
|| (session.memberships || [])[0];
|
||||
return membership ? membership.workspace : null;
|
||||
}
|
||||
|
||||
async function createAgent(page, workspaceId, payload) {
|
||||
return browserJson(
|
||||
page,
|
||||
'POST',
|
||||
`/api/admin/workspaces/${encodeURIComponent(workspaceId)}/agents`,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
|
||||
function uniqueName(prefix) {
|
||||
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ADMIN_EMAIL,
|
||||
ADMIN_PASSWORD,
|
||||
browserJson,
|
||||
createAgent,
|
||||
getCurrentWorkspace,
|
||||
getSession,
|
||||
localized,
|
||||
login,
|
||||
uniqueName,
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { ADMIN_EMAIL, ADMIN_PASSWORD, localized } = require('./helpers');
|
||||
|
||||
test('login page rejects invalid credentials', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.locator('#email').fill(ADMIN_EMAIL);
|
||||
await page.locator('#password').fill('wrong-password');
|
||||
await page.locator('.btn-signin').click();
|
||||
await expect(page.locator('#login-error')).toBeVisible();
|
||||
});
|
||||
|
||||
test('login page signs in and redirects to operations', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.locator('#email').fill(ADMIN_EMAIL);
|
||||
await page.locator('#password').fill(ADMIN_PASSWORD);
|
||||
await page.locator('.btn-signin').click();
|
||||
await expect(page).toHaveURL(/\/$/);
|
||||
await expect(page.locator('.page-title, .page-heading').first()).toHaveText(localized('Operations', 'Операции'));
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { login, localized } = require('./helpers');
|
||||
|
||||
test('logs and usage pages show seeded data', async ({ page }) => {
|
||||
await login(page);
|
||||
|
||||
await page.goto('/logs');
|
||||
await expect(page.locator('.page-title')).toHaveText(localized('Logs', 'Логи'));
|
||||
await expect(page.locator('#log-list')).toBeVisible();
|
||||
await expect(page.locator('#log-list').locator('.empty-state, .log-entry, .log-row, .log-item').first()).toBeVisible();
|
||||
|
||||
await page.goto('/usage');
|
||||
await expect(page.locator('.page-title')).toHaveText(localized('Usage', 'Использование'));
|
||||
await expect(page.locator('#chart-bars .chart-col')).toHaveCount(7);
|
||||
await expect(page.locator('#usage-tbody tr')).toHaveCount(1);
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { login } = require('./helpers');
|
||||
|
||||
test('shell and wizard expose stable diagnostics hooks', async ({ page }) => {
|
||||
await login(page);
|
||||
|
||||
await page.goto('/');
|
||||
await expect(page.locator('[data-testid="shell-avatar"]')).toBeVisible();
|
||||
await page.locator('[data-testid="shell-avatar"]').click();
|
||||
await expect(page.locator('[data-testid="shell-user-name"]').first()).toBeVisible();
|
||||
await expect(page.locator('[data-testid="shell-user-role"]').first()).toBeVisible();
|
||||
|
||||
await page.goto('/wizard/');
|
||||
await expect(page.locator('[data-testid="wizard-protocol-grid"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="wizard-protocol-rest"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="wizard-protocol-graphql"]')).toBeHidden();
|
||||
await expect(page.locator('[data-testid="wizard-protocol-grpc"]')).toBeHidden();
|
||||
await expect(page.locator('[data-testid="wizard-protocol-websocket"]')).toBeHidden();
|
||||
await expect(page.locator('[data-testid="wizard-protocol-soap"]')).toBeHidden();
|
||||
await expect(page.locator('html')).toHaveAttribute('data-crank-bootstrap-state', 'ready');
|
||||
});
|
||||
|
||||
test('secrets page exposes stable secret management hooks', async ({ page }) => {
|
||||
await login(page);
|
||||
await page.goto('/secrets');
|
||||
|
||||
await expect(page.locator('[data-testid="secret-create-button"]')).toBeVisible();
|
||||
await page.locator('[data-testid="secret-create-button"]').click();
|
||||
await expect(page.locator('[data-testid="secret-create-modal"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="secret-name-input"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="secret-kind-select"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="secret-submit-button"]')).toBeVisible();
|
||||
await expect(page.locator('html')).toHaveAttribute('data-crank-bootstrap-state', 'ready');
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { login, localized } = require('./helpers');
|
||||
|
||||
test('operations page shows demo catalog and filter works', async ({ page }) => {
|
||||
await login(page);
|
||||
await expect(page.locator('.page-heading')).toHaveText(localized('Operations', 'Операции'));
|
||||
await expect(page.locator('tbody tr')).toHaveCount(2);
|
||||
await page.getByPlaceholder(localized('Search operations', 'Поиск операций')).fill('crm');
|
||||
await expect(page.locator('tbody tr')).toHaveCount(1);
|
||||
await expect(page.locator('tbody tr').first()).toContainText(/crm_create_lead/i);
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { getSession, localized, login } = require('./helpers');
|
||||
|
||||
const CORE_PAGES = [
|
||||
{ path: '/', heading: localized('Operations', 'Операции') },
|
||||
{ path: '/agents', heading: localized('Agents', 'Агенты') },
|
||||
{ path: '/api-keys', heading: localized('API Keys', 'API ключи') },
|
||||
{ path: '/secrets', heading: localized('Secrets', 'Секреты') },
|
||||
{ path: '/logs', heading: localized('Logs', 'Логи') },
|
||||
{ path: '/usage', heading: localized('Usage', 'Использование') },
|
||||
{ path: '/workspace-setup', heading: localized('Workspace', 'Воркспейс') },
|
||||
{ path: '/settings', heading: localized('Settings', 'Настройки') },
|
||||
{ path: '/wizard/', heading: localized('Create', 'Создать') },
|
||||
];
|
||||
|
||||
test('authenticated staging smoke logs in and exposes session json', async ({ page }) => {
|
||||
await login(page);
|
||||
|
||||
const session = await getSession(page);
|
||||
expect(session.user).toBeTruthy();
|
||||
expect(Array.isArray(session.memberships)).toBe(true);
|
||||
expect(session.memberships.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('authenticated staging smoke opens core pages without console errors', async ({ page }) => {
|
||||
const pageErrors = [];
|
||||
page.on('pageerror', (error) => {
|
||||
pageErrors.push(error.message);
|
||||
});
|
||||
|
||||
await login(page);
|
||||
|
||||
for (const pageSpec of CORE_PAGES) {
|
||||
await page.goto(pageSpec.path);
|
||||
await expect(page.locator('#login-form')).toHaveCount(0);
|
||||
await expect(page.locator('body')).toContainText(pageSpec.heading);
|
||||
}
|
||||
|
||||
expect(pageErrors).toEqual([]);
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { login, localized } = require('./helpers');
|
||||
|
||||
test('wizard loads and protocol selection updates flow', async ({ page }) => {
|
||||
await login(page);
|
||||
await page.goto('/wizard/');
|
||||
await expect(page.locator('[data-step-counter]').first()).toContainText(localized('Step', 'Шаг'));
|
||||
await expect(page.locator('#step-panel-1 .step-panel-title')).toContainText(localized('Choose a protocol', 'Выберите протокол'));
|
||||
await page.locator('[data-testid="wizard-protocol-rest"]').click();
|
||||
await page.locator('#btn-continue').click();
|
||||
await expect(page.locator('#step-panel-2 .step-panel-title')).toContainText(localized('Select upstream', 'Выберите upstream'));
|
||||
});
|
||||
|
||||
test('community wizard hides premium protocols and explains edition scope', async ({ page }) => {
|
||||
await login(page);
|
||||
await page.goto('/wizard/');
|
||||
await expect(page.locator('[data-testid="wizard-protocol-rest"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="wizard-protocol-graphql"]')).toBeHidden();
|
||||
await expect(page.locator('[data-testid="wizard-protocol-grpc"]')).toBeHidden();
|
||||
await expect(page.locator('[data-testid="wizard-protocol-websocket"]')).toBeHidden();
|
||||
await expect(page.locator('[data-testid="wizard-protocol-soap"]')).toBeHidden();
|
||||
await expect(page.locator('#wizard-edition-protocol-note')).toContainText(
|
||||
localized('Commercial editions unlock', 'коммерческих редакциях')
|
||||
);
|
||||
|
||||
await page.locator('[data-testid="wizard-protocol-rest"]').click();
|
||||
await page.locator('#btn-continue').click();
|
||||
await page.locator('#btn-continue').click();
|
||||
await page.locator('#btn-continue').click();
|
||||
await page.locator('#btn-continue').click();
|
||||
|
||||
await expect(page.locator('#wizard-streaming-config-card')).toBeHidden();
|
||||
await expect(page.locator('#wizard-streaming-capability-note')).toHaveCount(0);
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { login, localized } = require('./helpers');
|
||||
|
||||
test('workspace and settings pages show live session data', async ({ page }) => {
|
||||
await login(page);
|
||||
|
||||
await page.goto('/workspace-setup');
|
||||
await expect(page.locator('#section-members')).toBeVisible();
|
||||
await expect(page.locator('#members-count-label')).toBeVisible();
|
||||
await expect(page.locator('#pending-invites')).toBeVisible();
|
||||
|
||||
await page.goto('/settings');
|
||||
await expect(page.locator('.page-title')).toHaveText(localized('Account settings', 'Настройки аккаунта'));
|
||||
await expect(page.locator('#settings-session-summary')).toBeVisible();
|
||||
await expect(page.locator('#section-security')).toBeVisible();
|
||||
await expect(page.locator('#settings-security-capability-title')).toContainText(localized('Community build', 'Редакция Community'));
|
||||
await expect(page.locator('#settings-security-capability-body')).toContainText(localized('static agent keys', 'статические ключи агентов'));
|
||||
await expect(page.locator('#settings-ws-protocol-hint')).toContainText(localized('REST / HTTP', 'REST / HTTP'));
|
||||
await expect(page.locator('#settings-notifications-capability-title')).toContainText(localized('Community build boundary', 'Граница редакции Community'));
|
||||
});
|
||||
Reference in New Issue
Block a user