console: sync the registration wizard and issue real API tokens
Mirror the Console registration feature: native hook catalog with activation plans, one-command install that embeds both credentials in exclusively created owner-only files (mktemp on POSIX, icacls on Windows), sh/PowerShell tabs, never-expiring default token, focus-visible styles, merged i18n keys, and the matching e2e specs. The server previously answered /v1/auth/token with the 7-day Better Auth session token and ignored ttl_seconds. It now issues opaque 32-byte API tokens stored as SHA-256 hashes (migration 003), honours ttl_seconds with null meaning no expiry, and resolves bearer values as API tokens before session validation. Issuance requires a session credential. Claude-Session: https://claude.ai/code/session_01QoUjdc7HGKTsgj5GhG8HvF
This commit is contained in:
1 parent
1bb9d4d49d
commit
edf9d73c3e
21 files changed
+967
-163
No files matched your search
@@ -4,6 +4,8 @@ import {
|
||||
INTEGRATION_CATALOG,
|
||||
INTEGRATION_TYPES,
|
||||
} from '@/features/agent-registration/integrations';
|
||||
import enRegistration from '@/i18n/en.json';
|
||||
import zhRegistration from '@/i18n/zh.json';
|
||||
import {
|
||||
assertRegistrationResponse,
|
||||
RegistrationContractError,
|
||||
@@ -43,35 +45,161 @@ test('keeps the complete integration catalog in canonical order', () => {
|
||||
expect(INTEGRATION_TYPES).toEqual(EXPECTED_INTEGRATIONS);
|
||||
expect(INTEGRATION_CATALOG.map(({ id }) => id)).toEqual(EXPECTED_INTEGRATIONS);
|
||||
expect(new Set(INTEGRATION_CATALOG.map(({ id }) => id)).size).toBe(19);
|
||||
expect(INTEGRATION_CATALOG.filter(({ mode }) => mode === 'adapter')).toHaveLength(15);
|
||||
expect(INTEGRATION_CATALOG.filter(({ mode }) => mode === 'hooks')).toHaveLength(15);
|
||||
expect(INTEGRATION_CATALOG.filter(({ mode }) => mode === 'sdk')).toHaveLength(4);
|
||||
expect(Object.fromEntries(INTEGRATION_CATALOG.map(({ id, name }) => [id, name])))
|
||||
.toMatchObject({
|
||||
codex: 'Codex CLI',
|
||||
cursor: 'Cursor CLI',
|
||||
grok: 'Grok Build',
|
||||
kimi: 'Kimi Code',
|
||||
});
|
||||
});
|
||||
|
||||
test('generates every adapter command with the verified SDK flag contract', () => {
|
||||
for (const integration of INTEGRATION_CATALOG.filter(({ mode }) => mode === 'adapter')) {
|
||||
test('keeps every native hook activation plan aligned with its provider contract', () => {
|
||||
const plans = Object.fromEntries(
|
||||
INTEGRATION_CATALOG
|
||||
.filter(({ mode }) => mode === 'hooks')
|
||||
.map(({ id, postInstall }) => [id, postInstall]),
|
||||
);
|
||||
|
||||
expect(plans).toEqual({
|
||||
augment: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'startSession', command: 'auggie' }],
|
||||
},
|
||||
claudecode: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'openHooks', command: '/hooks' }],
|
||||
},
|
||||
cline: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'startTask' }],
|
||||
},
|
||||
codex: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'reviewAndTrustHooks', command: '/hooks' }],
|
||||
},
|
||||
copilot: {
|
||||
kind: 'sequence',
|
||||
steps: [
|
||||
{ key: 'restartSession', command: '/restart' },
|
||||
{ key: 'inspectEnvironment', command: '/env' },
|
||||
],
|
||||
},
|
||||
cursor: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'inspectCursorHooks' }],
|
||||
},
|
||||
droid: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'openHooks', command: '/hooks' }],
|
||||
},
|
||||
gemini: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'listHooks', command: '/hooks list' }],
|
||||
},
|
||||
grok: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'inspectGrokConfig', command: 'grok inspect --json' }],
|
||||
},
|
||||
kimi: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'startSession', command: 'kimi' }],
|
||||
},
|
||||
kirocli: {
|
||||
kind: 'alternatives',
|
||||
steps: [
|
||||
{ key: 'launchKiroV2', command: 'kiro-cli --agent elydora-audit', variant: 'V2' },
|
||||
{ key: 'launchKiroV3', command: 'kiro-cli --v3', variant: 'V3' },
|
||||
],
|
||||
},
|
||||
kiroide: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'inspectKiroIdeHooks' }],
|
||||
},
|
||||
letta: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'openHooks', command: '/hooks' }],
|
||||
},
|
||||
opencode: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'startSession', command: 'opencode' }],
|
||||
},
|
||||
qwen: {
|
||||
kind: 'sequence',
|
||||
steps: [{ key: 'listHooks', command: '/hooks list' }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('translates every activation step in English and Chinese', () => {
|
||||
const stepKeys = [...new Set(
|
||||
INTEGRATION_CATALOG.flatMap(({ postInstall }) => (
|
||||
postInstall?.steps.map(({ key }) => key) ?? []
|
||||
)),
|
||||
)].sort();
|
||||
|
||||
expect(Object.keys(enRegistration.agentRegistration.postInstallSteps).sort()).toEqual(stepKeys);
|
||||
expect(Object.keys(zhRegistration.agentRegistration.postInstallSteps).sort()).toEqual(stepKeys);
|
||||
});
|
||||
|
||||
test('generates every native hook command with the verified SDK flag contract', () => {
|
||||
for (const integration of INTEGRATION_CATALOG.filter(({ mode }) => mode === 'hooks')) {
|
||||
for (const language of SDK_LANGUAGES) {
|
||||
const instructions = buildInstallInstructions(language, {
|
||||
integration,
|
||||
identity: credentials,
|
||||
baseUrl: 'https://api.example.com',
|
||||
privateKey: 'private-contract-key',
|
||||
token: 'token-contract',
|
||||
});
|
||||
|
||||
expect(instructions.setup).toContain(`--agent ${integration.id}`);
|
||||
expect(instructions.setup).toContain("'https://api.example.com'");
|
||||
expect(instructions.setup).not.toContain('private-key');
|
||||
expect(instructions.setup).not.toContain('private_key');
|
||||
expect(instructions.setup).not.toContain('--token');
|
||||
expect(instructions.setup).not.toContain('token-contract');
|
||||
expect(instructions.secretDelivery).toBe('hidden-prompts');
|
||||
expect(instructions.setup).toContain("printf '%s' 'k=$(mktemp \"$HOME/.elydora-agent-contract.XXXXXX\") && trap '\\''rm -f \"$k\"'\\'' EXIT INT TERM && t=$(mktemp \"$HOME/.elydora-agent-contract.XXXXXX\") && trap '\\''rm -f \"$k\" \"$t\"'\\'' EXIT INT TERM && ");
|
||||
expect(instructions.setup).toContain("printf '\\''%s\\n'\\'' '\\''private-contract-key'\\'' > \"$k\"");
|
||||
expect(instructions.setup).toContain("printf '\\''%s\\n'\\'' '\\''token-contract'\\'' > \"$t\"");
|
||||
expect(instructions.setup).not.toContain('chmod');
|
||||
expect(instructions.setup.endsWith("' | sh")).toBe(true);
|
||||
expect(instructions.secretDelivery).toBe('embedded');
|
||||
expect(instructions.postInstall).toEqual(integration.postInstall);
|
||||
|
||||
if (language === 'go') {
|
||||
expect(instructions.setup).toContain("--org-id 'org-contract'");
|
||||
expect(instructions.setup).toContain("--agent-id 'agent-contract'");
|
||||
expect(instructions.setup).toContain("--base-url 'https://api.example.com'");
|
||||
expect(instructions.setup).toContain("--org-id '\\''org-contract'\\''");
|
||||
expect(instructions.setup).toContain("--agent-id '\\''agent-contract'\\''");
|
||||
expect(instructions.setup).toContain("--base-url '\\''https://api.example.com'\\''");
|
||||
expect(instructions.setup).toContain('--private-key-file "$k" --token-file "$t"');
|
||||
} else {
|
||||
expect(instructions.setup).toContain("--org_id 'org-contract'");
|
||||
expect(instructions.setup).toContain("--agent_id 'agent-contract'");
|
||||
expect(instructions.setup).toContain("--base_url 'https://api.example.com'");
|
||||
expect(instructions.setup).toContain("--org_id '\\''org-contract'\\''");
|
||||
expect(instructions.setup).toContain("--agent_id '\\''agent-contract'\\''");
|
||||
expect(instructions.setup).toContain("--base_url '\\''https://api.example.com'\\''");
|
||||
expect(instructions.setup).toContain('--private_key_file "$k" --token_file "$t"');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('generates PowerShell native hook commands with the same SDK flag contract', () => {
|
||||
for (const integration of INTEGRATION_CATALOG.filter(({ mode }) => mode === 'hooks')) {
|
||||
for (const language of SDK_LANGUAGES) {
|
||||
const instructions = buildInstallInstructions(language, {
|
||||
integration,
|
||||
identity: credentials,
|
||||
baseUrl: 'https://api.example.com',
|
||||
privateKey: 'private-contract-key',
|
||||
token: 'token-contract',
|
||||
}, 'powershell');
|
||||
|
||||
expect(instructions.setup).toContain(`--agent ${integration.id}`);
|
||||
expect(instructions.setup).toContain("$k = Join-Path $HOME '.elydora-agent-contract.key'; $t = Join-Path $HOME '.elydora-agent-contract.token'; try { New-Item -ItemType File -Force -Path $k, $t | Out-Null; if ($IsLinux -or $IsMacOS) { chmod 600 $k $t; if ($LASTEXITCODE -ne 0) { throw \"chmod failed\" } } else { $u = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value; foreach ($f in $k, $t) { icacls $f /inheritance:r /grant:r \"*$($u):F\" | Out-Null; if ($LASTEXITCODE -ne 0) { throw \"icacls failed for $f\" } } }; Set-Content -LiteralPath $k -Value 'private-contract-key'; Set-Content -LiteralPath $t -Value 'token-contract'; ");
|
||||
expect(instructions.setup.endsWith('; if ($LASTEXITCODE -ne 0) { throw "elydora install exited with code $LASTEXITCODE" } } finally { Remove-Item -LiteralPath $k, $t -Force -ErrorAction SilentlyContinue }')).toBe(true);
|
||||
expect(instructions.secretDelivery).toBe('embedded');
|
||||
|
||||
if (language === 'go') {
|
||||
expect(instructions.setup).toContain("--org-id 'org-contract' --agent-id 'agent-contract' --kid 'agent-contract-key-1' --base-url 'https://api.example.com' --private-key-file $k --token-file $t;");
|
||||
} else {
|
||||
expect(instructions.setup).toContain("--org_id 'org-contract' --agent_id 'agent-contract' --kid 'agent-contract-key-1' --base_url 'https://api.example.com' --private_key_file $k --token_file $t;");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,11 +214,13 @@ test('generates direct SDK setup and operation code for each language', () => {
|
||||
integration: integration!,
|
||||
identity: credentials,
|
||||
baseUrl: 'https://api.example.com',
|
||||
privateKey: 'private-contract-key',
|
||||
token: 'token-contract',
|
||||
});
|
||||
expect(instructions.setup).toContain('agent-contract');
|
||||
expect(instructions.setup).toContain('ELYDORA_PRIVATE_KEY');
|
||||
expect(instructions.setup).toContain('ELYDORA_API_TOKEN');
|
||||
expect(instructions.setup).not.toContain('private-key');
|
||||
expect(instructions.setup).not.toContain('private-contract-key');
|
||||
expect(instructions.setup).not.toContain('token-contract');
|
||||
expect(instructions.usage).toContain('ai.tool_use');
|
||||
expect(instructions.verify).toBeUndefined();
|
||||
@@ -110,6 +240,8 @@ test('quotes shell metacharacters and rejects unsupported command values', () =>
|
||||
orgId: '$(Write-Output injected)',
|
||||
},
|
||||
baseUrl: 'https://api.example.com/`whoami`',
|
||||
privateKey: 'private-contract-key',
|
||||
token: 'token-contract',
|
||||
});
|
||||
expect(instructions.setup).toContain("'$(Write-Output injected)'");
|
||||
expect(instructions.setup).toContain("'https://api.example.com/`whoami`'");
|
||||
@@ -118,7 +250,23 @@ test('quotes shell metacharacters and rejects unsupported command values', () =>
|
||||
integration: integration!,
|
||||
identity: { ...credentials, orgId: "org-'unsafe" },
|
||||
baseUrl: 'https://api.example.com',
|
||||
privateKey: 'private-contract-key',
|
||||
token: 'token-contract',
|
||||
})).toThrow('Organization ID cannot be represented');
|
||||
expect(() => buildInstallInstructions('node', {
|
||||
integration: integration!,
|
||||
identity: credentials,
|
||||
baseUrl: 'https://api.example.com',
|
||||
privateKey: "key-'unsafe",
|
||||
token: 'token-contract',
|
||||
})).toThrow('Private key cannot be represented');
|
||||
expect(() => buildInstallInstructions('node', {
|
||||
integration: integration!,
|
||||
identity: { ...credentials, agentId: 'agent contract' },
|
||||
baseUrl: 'https://api.example.com',
|
||||
privateKey: 'private-contract-key',
|
||||
token: 'token-contract',
|
||||
})).toThrow('Agent ID cannot be used as a credential file name.');
|
||||
});
|
||||
|
||||
test('classifies malformed successful responses as blocking contract failures', () => {
|
||||
|
||||
@@ -110,7 +110,7 @@ async function mockBackend(
|
||||
});
|
||||
}
|
||||
|
||||
test('registers Grok atomically and renders verified adapter commands', async ({ page }) => {
|
||||
test('registers Grok atomically and renders verified native hook commands', async ({ page }) => {
|
||||
const observed: ObservedRequest[] = [];
|
||||
const unexpected: string[] = [];
|
||||
const runtimeErrors: string[] = [];
|
||||
@@ -123,10 +123,26 @@ test('registers Grok atomically and renders verified adapter commands', async ({
|
||||
await page.goto('/agents');
|
||||
await page.getByRole('button', { name: 'Register Agent' }).click();
|
||||
await waitForRenderedDialog(page);
|
||||
await expect(page.getByRole('button', { name: 'Augment Code' })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'OpenAI Codex' })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Kimi CLI' })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Qwen Code' })).toBeVisible();
|
||||
const dialog = page.getByRole('dialog');
|
||||
await dialog.focus();
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('Tab');
|
||||
const firstIntegration = page.getByRole('button', { name: 'Augment Code' });
|
||||
await expect(firstIntegration).toBeFocused();
|
||||
expect(await firstIntegration.evaluate((element) => {
|
||||
const style = window.getComputedStyle(element);
|
||||
return { width: style.outlineWidth, style: style.outlineStyle };
|
||||
})).toEqual({ width: '2px', style: 'solid' });
|
||||
const hookGroup = page.getByRole('region', { name: 'Native hooks' });
|
||||
await expect(hookGroup.getByRole('button')).toHaveCount(15);
|
||||
await expect(hookGroup.getByText('HOOKS', { exact: true })).toHaveCount(15);
|
||||
await expect(hookGroup.getByRole('button', { name: 'Codex CLI' })).toBeVisible();
|
||||
await expect(hookGroup.getByRole('button', { name: 'Kimi Code' })).toBeVisible();
|
||||
await expect(hookGroup.getByRole('button', { name: 'Grok Build' })).toBeVisible();
|
||||
await expect(hookGroup.getByRole('button', { name: 'Cursor CLI' })).toBeVisible();
|
||||
const sdkGroup = page.getByRole('region', { name: 'Custom SDK' });
|
||||
await expect(sdkGroup.getByRole('button')).toHaveCount(4);
|
||||
await expect(sdkGroup.getByText('HOOKS', { exact: true })).toHaveCount(0);
|
||||
await page.screenshot({ path: 'test-results/agent-registration-catalog.png', fullPage: true });
|
||||
await page.getByRole('button', { name: 'Grok Build' }).click();
|
||||
await page.getByLabel('Display Name').fill('Production Grok');
|
||||
@@ -151,29 +167,58 @@ test('registers Grok atomically and renders verified adapter commands', async ({
|
||||
|
||||
const setup = page.locator('pre');
|
||||
await expect(setup).toContainText('npx @elydora/sdk install --agent grok');
|
||||
await expect(setup).toContainText(`--org_id '${ORG_ID}'`);
|
||||
await expect(setup).not.toContainText(TOKEN);
|
||||
await expect(setup).not.toContainText(privateKey);
|
||||
await expect(setup).not.toContainText('--token');
|
||||
await expect(setup).not.toContainText('--private_key');
|
||||
await expect(page.getByText('The CLI requests both values with terminal echo disabled.')).toBeVisible();
|
||||
await expect(setup).toContainText(`--org_id '\\''${ORG_ID}'\\''`);
|
||||
await expect(setup).toContainText(`'${TOKEN}'`);
|
||||
await expect(setup).toContainText(`'${privateKey}'`);
|
||||
await expect(setup).toContainText('mktemp "$HOME/.elydora-');
|
||||
await expect(setup).toContainText('--private_key_file "$k" --token_file "$t"');
|
||||
await expect(page.getByText(
|
||||
'The command stores both credentials in owner-only files, installs the hook, then deletes them.',
|
||||
)).toBeVisible();
|
||||
await expect(page.getByText(
|
||||
'Confirm the Elydora PreToolUse, PostToolUse, and PostToolUseFailure hooks.',
|
||||
)).toBeVisible();
|
||||
await expect(page.getByText('grok inspect --json', { exact: true })).toBeVisible();
|
||||
|
||||
await page.context().grantPermissions(['clipboard-read', 'clipboard-write']);
|
||||
await page.getByRole('button', { name: 'Copy setup command' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Copy setup command' })).toHaveText('Copied');
|
||||
const copiedSetup = await page.evaluate(() => navigator.clipboard.readText());
|
||||
expect(copiedSetup).not.toContain(TOKEN);
|
||||
expect(copiedSetup).not.toContain(privateKey);
|
||||
expect(copiedSetup).toContain(TOKEN);
|
||||
expect(copiedSetup).toContain(privateKey);
|
||||
const activationCopy = page.getByRole('button', {
|
||||
name: 'Copy activation command: grok inspect --json',
|
||||
});
|
||||
await activationCopy.click();
|
||||
await expect(activationCopy).toHaveText('Copied');
|
||||
expect(await page.evaluate(() => navigator.clipboard.readText())).toBe('grok inspect --json');
|
||||
|
||||
await page.getByRole('tab', { name: 'go' }).click();
|
||||
await expect(setup).toContainText('elydora install --agent grok');
|
||||
await expect(setup).toContainText(`--org-id '${ORG_ID}'`);
|
||||
await expect(setup).toContainText("--agent-id 'agent-");
|
||||
await expect(setup).not.toContainText('--private-key');
|
||||
await expect(setup).not.toContainText('--token');
|
||||
await expect(setup).toContainText(`--org-id '\\''${ORG_ID}'\\''`);
|
||||
await expect(setup).toContainText("--agent-id '\\''agent-");
|
||||
await expect(setup).toContainText('--private-key-file "$k" --token-file "$t"');
|
||||
await page.getByRole('tab', { name: 'PowerShell' }).click();
|
||||
await expect(setup).toContainText("$k = Join-Path $HOME '.elydora-agent-");
|
||||
await expect(setup).toContainText('--private-key-file $k --token-file $t;');
|
||||
await page.getByRole('tab', { name: 'sh', exact: true }).click();
|
||||
await expect(setup).toContainText("printf '%s' 'k=$(mktemp ");
|
||||
|
||||
await page.screenshot({ path: 'test-results/agent-registration-grok.png', fullPage: true });
|
||||
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.getByRole('dialog').locator(':scope > div').last().evaluate(
|
||||
(content) => content.scrollTo(0, content.scrollHeight),
|
||||
);
|
||||
await expect(page.getByText('grok inspect --json', { exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('button', {
|
||||
name: 'Copy activation command: grok inspect --json',
|
||||
})).toBeVisible();
|
||||
expect(await page.evaluate(
|
||||
() => document.documentElement.scrollWidth > window.innerWidth,
|
||||
)).toBe(false);
|
||||
await page.screenshot({ path: 'test-results/agent-registration-grok-mobile.png', fullPage: true });
|
||||
|
||||
const registrationRequests = observed.filter(({ path }) => path === '/v1/agents/register');
|
||||
expect(registrationRequests).toHaveLength(1);
|
||||
expect(registrationRequests[0]?.method).toBe('POST');
|
||||
@@ -249,6 +294,40 @@ test('renders custom SDK setup with runtime secret references', async ({ page })
|
||||
expect(runtimeErrors).toEqual([]);
|
||||
});
|
||||
|
||||
test('renders Kiro CLI versions as activation alternatives', async ({ page }) => {
|
||||
const observed: ObservedRequest[] = [];
|
||||
const unexpected: string[] = [];
|
||||
const runtimeErrors: string[] = [];
|
||||
page.on('pageerror', (error) => runtimeErrors.push(error.message));
|
||||
page.on('console', (message) => {
|
||||
if (message.type() === 'error') runtimeErrors.push(message.text());
|
||||
});
|
||||
await mockBackend(page, observed, unexpected);
|
||||
|
||||
await page.goto('/agents');
|
||||
await page.getByRole('button', { name: 'Register Agent' }).click();
|
||||
await page.getByRole('button', { name: 'Kiro CLI' }).click();
|
||||
await page.getByLabel('Display Name').fill('Production Kiro');
|
||||
await page.locator('form').getByRole('button', { name: 'Register Agent' }).click();
|
||||
await page.getByRole('button', { name: 'Issue API Token' }).click();
|
||||
await expect(page.getByText(TOKEN)).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
|
||||
const alternatives = page.getByRole('list', { name: 'Post-install steps' });
|
||||
await expect(alternatives).toHaveJSProperty('tagName', 'UL');
|
||||
await expect(alternatives.locator('li')).toHaveCount(2);
|
||||
await expect(alternatives.getByText('V2', { exact: true })).toBeVisible();
|
||||
await expect(alternatives.getByText('V3', { exact: true })).toBeVisible();
|
||||
await expect(alternatives.getByText('kiro-cli --agent elydora-audit', { exact: true })).toBeVisible();
|
||||
await expect(alternatives.getByText('kiro-cli --v3', { exact: true })).toBeVisible();
|
||||
await page.screenshot({ path: 'test-results/agent-registration-kiro.png', fullPage: true });
|
||||
|
||||
expect(observed.filter(({ path }) => path === '/v1/agents/register')).toHaveLength(1);
|
||||
expect(observed.filter(({ path }) => path === '/v1/auth/token')).toHaveLength(1);
|
||||
expect(unexpected).toEqual([]);
|
||||
expect(runtimeErrors).toEqual([]);
|
||||
});
|
||||
|
||||
test('loads the Chinese registration resources', async ({ page }) => {
|
||||
const observed: ObservedRequest[] = [];
|
||||
const unexpected: string[] = [];
|
||||
@@ -263,11 +342,21 @@ test('loads the Chinese registration resources', async ({ page }) => {
|
||||
await page.goto('/agents');
|
||||
await expect(page.locator('html')).toHaveAttribute('lang', 'zh');
|
||||
await page.getByRole('button', { name: '注册代理' }).click();
|
||||
await expect(page.getByText('CLI 适配器')).toBeVisible();
|
||||
await expect(page.getByText('原生 Hooks')).toBeVisible();
|
||||
await expect(page.getByText('自定义 SDK')).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'OpenAI Codex' })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Codex CLI' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Kimi Code' }).click();
|
||||
await page.getByLabel('显示名称').fill('生产 Kimi');
|
||||
await page.locator('form').getByRole('button', { name: '注册代理' }).click();
|
||||
await page.getByRole('button', { name: '签发 API 令牌' }).click();
|
||||
await expect(page.getByText(TOKEN)).toBeVisible();
|
||||
await page.getByRole('button', { name: '继续' }).click();
|
||||
await expect(page.getByText('启动新的 Kimi Code 会话。')).toBeVisible();
|
||||
await expect(page.getByText('kimi', { exact: true })).toBeVisible();
|
||||
|
||||
expect(observed.filter(({ path }) => path === '/v1/agents')).toHaveLength(1);
|
||||
expect(observed.filter(({ path }) => path === '/v1/agents')).toHaveLength(2);
|
||||
expect(observed.filter(({ path }) => path === '/v1/agents/register')).toHaveLength(1);
|
||||
expect(observed.filter(({ path }) => path === '/v1/auth/token')).toHaveLength(1);
|
||||
expect(unexpected).toEqual([]);
|
||||
expect(runtimeErrors).toEqual([]);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user