Files
wehub-resource-sync 9b395f5cc3
E2E Headed Chrome / e2e-headed (macos-15) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (ubuntu-latest) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (windows-latest) (push) Has been cancelled
CI / build (macos-latest) (push) Has been cancelled
CI / build (ubuntu-latest) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / bun-test (push) Has been cancelled
CI / adapter-test (push) Has been cancelled
CI / smoke-test (macos-latest) (push) Has been cancelled
CI / smoke-test (ubuntu-latest) (push) Has been cancelled
Security Audit / audit (push) Has been cancelled
Build Chrome Extension / build (push) Has been cancelled
Trigger Website Rebuild (Docs Updated) / dispatch (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:48 +08:00

125 lines
6.4 KiB
JavaScript

import { describe, expect, it, vi } from 'vitest';
import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@jackwener/opencli/errors';
import { __test__ } from './ask.js';
import { askCommand } from './ask.js';
describe('yuanbao ask helpers', () => {
describe('isOnYuanbao', () => {
const fakePage = (url) => ({ evaluate: () => url instanceof Error ? Promise.reject(url) : Promise.resolve(url) });
it('returns true for yuanbao.tencent.com URLs', async () => {
expect(await __test__.isOnYuanbao(fakePage('https://yuanbao.tencent.com/'))).toBe(true);
expect(await __test__.isOnYuanbao(fakePage('https://yuanbao.tencent.com/chat/abc'))).toBe(true);
});
it('returns false for non-yuanbao domains', async () => {
expect(await __test__.isOnYuanbao(fakePage('https://example.com/?next=yuanbao.tencent.com'))).toBe(false);
expect(await __test__.isOnYuanbao(fakePage('about:blank'))).toBe(false);
});
it('returns false when evaluate throws', async () => {
expect(await __test__.isOnYuanbao(fakePage(new Error('detached')))).toBe(false);
});
});
it('removes echoed prompt prefixes from transcript additions', () => {
expect(__test__.sanitizeYuanbaoResponseText('你好\n你好,我是元宝。', '你好')).toBe('你好,我是元宝。');
});
it('filters transient in-progress assistant placeholders', () => {
expect(__test__.sanitizeYuanbaoResponseText('正在搜索资料', '张雪机车相关的股票有哪些?')).toBe('');
});
it('normalizes boolean flags with explicit defaults', () => {
expect(__test__.normalizeBooleanFlag(undefined, true)).toBe(true);
expect(__test__.normalizeBooleanFlag(undefined, false)).toBe(false);
expect(__test__.normalizeBooleanFlag('true', false)).toBe(true);
expect(__test__.normalizeBooleanFlag('1', false)).toBe(true);
expect(__test__.normalizeBooleanFlag('yes', false)).toBe(true);
expect(__test__.normalizeBooleanFlag('false', true)).toBe(false);
});
it('ignores baseline lines and echoed prompts when collecting additions', () => {
const response = __test__.collectYuanbaoTranscriptAdditions(['旧消息'], ['旧消息', '你好', '你好\n你好,我是元宝。'], '你好');
expect(response).toBe('你好,我是元宝。');
});
it('prefers fresh assistant messages over echoed prompts and older messages', () => {
const response = __test__.pickLatestYuanbaoAssistantCandidate(['旧回复', '你好', '你好!我是元宝,由腾讯推出的AI助手。'], 1, '你好');
expect(response).toBe('你好!我是元宝,由腾讯推出的AI助手。');
});
it('converts assistant html tables to markdown tables via turndown', () => {
const markdown = __test__.convertYuanbaoHtmlToMarkdown(`
<h3>核心产业链概念股一览</h3>
<table>
<thead>
<tr><th>细分赛道</th><th>核心标的</th></tr>
</thead>
<tbody>
<tr><td>光模块</td><td>中际旭创</td></tr>
</tbody>
</table>
`);
expect(markdown).toContain('### 核心产业链概念股一览');
expect(markdown).toContain('| 细分赛道 | 核心标的 |');
expect(markdown).toContain('| --- | --- |');
expect(markdown).toContain('| 光模块 | 中际旭创 |');
});
it('tracks stabilization by incrementing repeats and resetting on changes', () => {
expect(__test__.updateStableState('', 0, '第一段')).toEqual({
previousText: '第一段',
stableCount: 0,
});
expect(__test__.updateStableState('第一段', 0, '第一段')).toEqual({
previousText: '第一段',
stableCount: 1,
});
expect(__test__.updateStableState('第一段', 1, '第二段')).toEqual({
previousText: '第二段',
stableCount: 0,
});
});
});
function createAskPageMock(overrides = {}) {
const currentUrl = overrides.currentUrl ?? 'https://yuanbao.tencent.com/';
const hasLoginGate = overrides.hasLoginGate ?? false;
const sendResult = overrides.sendResult;
return {
goto: vi.fn().mockResolvedValue(undefined),
wait: vi.fn().mockResolvedValue(undefined),
evaluate: vi.fn().mockImplementation(async (script) => {
if (script === 'window.location.href')
return currentUrl;
if (script.includes('微信扫码登录'))
return hasLoginGate;
if (script.includes('[dt-button-id="internet_search"]'))
return { found: false, enabled: false };
if (script.includes('[dt-button-id="deep_think"]'))
return { found: false, enabled: false };
if (script.includes('.agent-chat__list__item--ai'))
return [];
if (script.includes('const stopLines = new Set(['))
return [];
if (script.includes('Failed to insert the prompt into the Yuanbao composer.')) {
return sendResult ?? { ok: true, action: 'click' };
}
throw new Error(`Unexpected evaluate script in test: ${script.slice(0, 80)}`);
}),
};
}
describe('yuanbao ask command', () => {
it('throws AuthRequiredError when Yuanbao shows a login gate before sending', async () => {
const page = createAskPageMock({ hasLoginGate: true });
await expect(askCommand.func(page, { prompt: '你好', timeout: 60, search: true, think: false }))
.rejects.toBeInstanceOf(AuthRequiredError);
});
it('throws CommandExecutionError when the prompt cannot be sent', async () => {
const page = createAskPageMock({
sendResult: {
ok: false,
reason: 'Yuanbao composer was not found.',
},
});
await expect(askCommand.func(page, { prompt: '你好', timeout: 60, search: true, think: false }))
.rejects.toBeInstanceOf(CommandExecutionError);
});
it('throws TimeoutError when no response arrives before timeout', async () => {
const page = createAskPageMock({
sendResult: { ok: true, action: 'click' },
});
await expect(askCommand.func(page, { prompt: '你好', timeout: -1, search: true, think: false }))
.rejects.toBeInstanceOf(ArgumentError);
});
});