chore: publish clean community baseline
Deploy / deploy (push) Successful in 2m39s
CI / Rust Checks (push) Successful in 5m31s
CI / UI Checks (push) Successful in 5s
CI / Deployment Manifests (push) Successful in 3s
CI / Frontend E2E (push) Successful in 4m29s

This commit is contained in:
github-ops
2026-06-17 06:15:46 +00:00
commit 4bd54ecd5b
317 changed files with 73426 additions and 0 deletions
+19
View File
@@ -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);
});
+30
View File
@@ -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', 'Скопируйте этот ключ сейчас'));
});
+91
View File
@@ -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,
};
+19
View File
@@ -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', 'Операции'));
});
+16
View File
@@ -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);
});
+34
View File
@@ -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');
});
+11
View File
@@ -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([]);
});
+476
View File
@@ -0,0 +1,476 @@
const { test, expect } = require('@playwright/test');
const { getCurrentWorkspace, 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);
});
test('wizard edit mode hydrates fields from operation version snapshot', async ({ page }) => {
await login(page);
const workspace = await getCurrentWorkspace(page);
const operationId = 'frankfurter_monthly_rates';
const version = 7;
await page.route(
`/api/admin/workspaces/${encodeURIComponent(workspace.id)}/operations/${operationId}`,
async (route) => {
await route.fulfill({
contentType: 'application/json',
body: JSON.stringify({
id: operationId,
workspace_id: workspace.id,
name: operationId,
display_name: 'Frankfurter monthly rates',
category: 'finance',
protocol: 'rest',
security_level: 'standard',
status: 'draft',
current_draft_version: version,
latest_published_version: null,
created_at: '2026-06-19T00:00:00Z',
updated_at: '2026-06-19T00:00:00Z',
published_at: null,
draft_version_ref: { version, status: 'draft' },
published_version_ref: null,
agent_refs: [],
}),
});
},
);
await page.route(
`/api/admin/workspaces/${encodeURIComponent(workspace.id)}/operations/${operationId}/versions/${version}`,
async (route) => {
await route.fulfill({
contentType: 'application/json',
body: JSON.stringify({
operation_id: operationId,
workspace_id: workspace.id,
version,
status: 'draft',
change_note: null,
created_at: '2026-06-19T00:00:00Z',
created_by: null,
snapshot: {
id: operationId,
name: operationId,
display_name: 'Frankfurter monthly rates',
category: 'finance',
protocol: 'rest',
security_level: 'standard',
status: 'draft',
version,
target: {
kind: 'rest',
base_url: 'https://api.frankfurter.app',
method: 'GET',
path_template: '/2024-01..2024-03',
static_headers: {
Accept: 'application/json',
'X-Demo': 'frankfurter',
},
},
input_schema: {
type: 'object',
description: null,
required: false,
nullable: false,
default_value: null,
fields: {
from: {
type: 'string',
description: 'Base currency',
required: true,
nullable: false,
default_value: null,
fields: {},
items: null,
enum_values: [],
variants: [],
},
to: {
type: 'string',
description: 'Quote currency',
required: false,
nullable: false,
default_value: 'EUR',
fields: {},
items: null,
enum_values: [],
variants: [],
},
},
items: null,
enum_values: [],
variants: [],
},
output_schema: {
type: 'object',
description: null,
required: false,
nullable: false,
default_value: null,
fields: {
rates: {
type: 'object',
description: 'Rates by date',
required: true,
nullable: false,
default_value: null,
fields: {},
items: null,
enum_values: [],
variants: [],
},
},
items: null,
enum_values: [],
variants: [],
},
input_mapping: {
rules: [
{ source: '$.mcp.from', target: '$.request.query.from' },
],
},
output_mapping: {
rules: [
{ source: '$.response.body.rates', target: '$.output.rates' },
],
},
execution_config: {
timeout_ms: 9000,
retry_policy: { max_attempts: 2 },
auth_profile_ref: null,
headers: {},
protocol_options: null,
streaming: null,
},
tool_description: {
title: 'Получить историю курсов за месяц',
description: 'Возвращает историю курсов валют Frankfurter за заданный период.',
tags: [],
examples: [
{
input: {
from: 'USD',
to: 'EUR',
},
},
],
},
wizard_state: {
input_sample: {
from: 'USD',
to: 'EUR',
},
output_sample: {
rates: {
'2024-01': {
EUR: 0.91,
},
},
},
test_input: {
from: 'GBP',
to: 'USD',
},
},
samples: [],
generated_draft: null,
config_export: null,
},
}),
});
},
);
await page.goto(`/wizard/?mode=edit&operationId=${operationId}`);
await expect(page.locator('#tool-name')).toHaveValue(operationId);
await expect(page.locator('#tool-display-name')).toHaveValue('Frankfurter monthly rates');
await expect(page.locator('#new-upstream-url')).toHaveValue('https://api.frankfurter.app');
await expect(page.locator('#new-upstream-static-headers')).toHaveValue(/"Accept": "application\/json"/);
await expect(page.locator('#new-upstream-static-headers')).toHaveValue(/"X-Demo": "frankfurter"/);
await expect(page.locator('#endpoint-path')).toHaveValue('/2024-01..2024-03');
await expect(page.locator('.method-card.active')).toHaveAttribute('data-method', 'GET');
await expect(page.locator('#tool-title')).toHaveValue('Получить историю курсов за месяц');
await expect(page.locator('#tool-description')).toHaveValue('Возвращает историю курсов валют Frankfurter за заданный период.');
await expect(page.locator('#tool-input-schema')).toHaveValue(/"from"/);
await expect(page.locator('#tool-input-schema')).toHaveValue(/"to"/);
await expect(page.locator('#tool-input-schema')).not.toHaveValue(/first_name/);
await expect(page.locator('#tool-output-schema')).toHaveValue(/"rates"/);
await expect(page.locator('#tool-input-mapping')).toHaveValue(/query\.from/);
await expect(page.locator('#tool-output-mapping')).toHaveValue(/rates: \$\.response\.body\.rates/);
await expect(page.locator('#tool-exec-config')).toHaveValue(/9000/);
await expect(page.locator('#tool-exec-config')).toHaveValue(/max_attempts: 2/);
await expect(page.locator('#wizard-input-sample')).toHaveValue(/"from": "USD"/);
await expect(page.locator('#wizard-input-sample')).toHaveValue(/"to": "EUR"/);
await expect(page.locator('#wizard-input-sample')).not.toHaveValue(/Ada/);
await expect(page.locator('#wizard-output-sample')).toHaveValue(/"rates":/);
await expect(page.locator('#wizard-output-sample')).toHaveValue(/0\.91/);
await expect(page.locator('#wizard-test-input')).toHaveValue(/"from": "GBP"/);
await expect(page.locator('#wizard-test-input')).toHaveValue(/"to": "USD"/);
await expect(page.locator('#wizard-test-input')).not.toHaveValue(/Ada/);
});
test('wizard edit mode preserves explicit request mapping targets on save', async ({ page }) => {
await login(page);
const workspace = await getCurrentWorkspace(page);
const operationId = 'frankfurter_latest_rate';
const version = 3;
let updatePayload = null;
await page.route(
`/api/admin/workspaces/${encodeURIComponent(workspace.id)}/operations/${operationId}`,
async (route) => {
if (route.request().method() === 'PATCH') {
updatePayload = route.request().postDataJSON();
await route.fulfill({
contentType: 'application/json',
body: JSON.stringify({
operation_id: operationId,
workspace_id: workspace.id,
version,
status: 'draft',
updated_at: '2026-06-19T00:00:00Z',
}),
});
return;
}
await route.fulfill({
contentType: 'application/json',
body: JSON.stringify({
id: operationId,
workspace_id: workspace.id,
name: operationId,
display_name: 'Frankfurter latest rate',
category: 'finance',
protocol: 'rest',
security_level: 'standard',
status: 'draft',
current_draft_version: version,
latest_published_version: null,
created_at: '2026-06-19T00:00:00Z',
updated_at: '2026-06-19T00:00:00Z',
published_at: null,
draft_version_ref: { version, status: 'draft' },
published_version_ref: null,
agent_refs: [],
}),
});
},
);
await page.route(
`/api/admin/workspaces/${encodeURIComponent(workspace.id)}/operations/${operationId}/versions/${version}`,
async (route) => {
await route.fulfill({
contentType: 'application/json',
body: JSON.stringify({
operation_id: operationId,
workspace_id: workspace.id,
version,
status: 'draft',
change_note: null,
created_at: '2026-06-19T00:00:00Z',
created_by: null,
snapshot: {
id: operationId,
name: operationId,
display_name: 'Frankfurter latest rate',
category: 'finance',
protocol: 'rest',
security_level: 'standard',
status: 'draft',
version,
target: {
kind: 'rest',
base_url: 'https://api.frankfurter.app',
method: 'GET',
path_template: '/latest',
static_headers: {
Accept: 'application/json',
},
},
input_schema: {
type: 'object',
description: null,
required: false,
nullable: false,
default_value: null,
fields: {
base: {
type: 'string',
description: null,
required: true,
nullable: false,
default_value: null,
fields: {},
items: null,
enum_values: [],
variants: [],
},
symbols: {
type: 'string',
description: 'Quote symbols',
required: false,
nullable: false,
default_value: 'EUR',
fields: {},
items: null,
enum_values: [],
variants: [],
},
},
items: null,
enum_values: [],
variants: [],
},
output_schema: {
type: 'object',
description: null,
required: false,
nullable: false,
default_value: null,
fields: {},
items: null,
enum_values: [],
variants: [],
},
input_mapping: {
rules: [
{ source: '$.mcp.base', target: '$.request.query.base' },
{ source: '$.mcp.symbols', target: '$.request.query.symbols' },
{ source: '$.mcp.date', target: '$.request.path.date' },
],
},
output_mapping: {
rules: [
{ source: '$.response.body.base', target: '$.output.base' },
],
},
execution_config: {
timeout_ms: 9000,
retry_policy: null,
auth_profile_ref: null,
headers: {},
protocol_options: null,
streaming: null,
},
tool_description: {
title: 'Получить последний курс',
description: 'Возвращает последний курс.',
tags: ['finance'],
examples: [{ input: { base: 'USD', symbols: 'EUR' } }],
},
wizard_state: {
input_sample: {
base: 'USD',
symbols: 'EUR',
},
output_sample: {
base: 'USD',
rates: {
EUR: 0.91,
},
},
test_input: {
base: 'CHF',
symbols: 'JPY',
},
},
samples: [],
generated_draft: null,
config_export: null,
},
}),
});
},
);
await page.goto(`/wizard/?mode=edit&operationId=${operationId}`);
await expect(page.locator('#tool-input-mapping')).toHaveValue(/query\.base/);
await expect(page.locator('#tool-input-mapping')).toHaveValue(/path\.date/);
await page.locator('.btn-save-draft').click();
await expect.poll(() => updatePayload).not.toBeNull();
expect(updatePayload.display_name).toBe('Frankfurter latest rate');
expect(updatePayload.target).toEqual({
kind: 'rest',
base_url: 'https://api.frankfurter.app',
method: 'GET',
path_template: '/latest',
static_headers: {
Accept: 'application/json',
},
});
expect(updatePayload.input_schema.fields).toHaveProperty('base');
expect(updatePayload.input_schema.fields).toHaveProperty('symbols');
expect(updatePayload.output_mapping.rules).toEqual([
{ source: '$.response.body.base', target: '$.output.base' },
]);
expect(updatePayload.execution_config).toEqual({
timeout_ms: 9000,
retry_policy: null,
auth_profile_ref: null,
headers: {},
protocol_options: null,
streaming: null,
});
expect(updatePayload.tool_description).toEqual({
title: 'Получить последний курс',
description: 'Возвращает последний курс.',
tags: ['finance'],
examples: [{ input: { base: 'USD', symbols: 'EUR' } }],
});
expect(updatePayload.wizard_state).toEqual({
input_sample: {
base: 'USD',
symbols: 'EUR',
},
output_sample: {
base: 'USD',
rates: {
EUR: 0.91,
},
},
test_input: {
base: 'CHF',
symbols: 'JPY',
},
});
expect(updatePayload.input_mapping.rules).toEqual([
{ source: '$.mcp.base', target: '$.request.query.base' },
{ source: '$.mcp.symbols', target: '$.request.query.symbols' },
{ source: '$.mcp.date', target: '$.request.path.date' },
]);
});
@@ -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'));
});