Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

140 lines
5.1 KiB
JavaScript

/**
* Tests for the agent-thinking panel renderer in components/progress.js
* (the `updateAgentThinking` function).
*
* Regression coverage for the "Using web_search" display bug
* (PR #4470 / fix/display-actual-engine-name):
*
* The LangGraph strategy puts the friendly engine name in the progress
* event's `data.message` (e.g. `🔍 Searching DuckDuckGo: "..."`) while
* keeping the stable tool id in `data.tool` ("web_search"). The renderer
* must surface the friendly message, not "Using web_search".
*
* The MCP strategy puts the friendly label in `data.message`
* (`ACTION: Using DuckDuckGo - "..."`) AND also supplies `data.arguments`.
* The renderer must show the message verbatim without duplicating the
* query that's already embedded in it.
*
* When no message is supplied, the renderer falls back to
* `Using ${data.tool}` (+ args), so the panel never renders blank.
*/
let progressComponent;
beforeAll(async () => {
// progress.js is an IIFE; importing it runs the module and exposes
// window.progressComponent (which now includes updateAgentThinking).
await import('@js/components/progress.js');
progressComponent = window.progressComponent;
});
beforeEach(() => {
// updateAgentThinking queries these two ids and appends step nodes.
document.body.innerHTML = `
<div id="agent-thinking-panel" style="display: none;">
<div id="agent-thinking-content"></div>
</div>
`;
});
function renderToolCall(data) {
progressComponent.updateAgentThinking({ phase: 'tool_call', ...data });
const step = document
.getElementById('agent-thinking-content')
.querySelector('.ldr-agent-step-content');
return step ? step.textContent : null;
}
describe('updateAgentThinking - tool_call rendering', () => {
it('shows the LangGraph friendly message instead of "Using web_search"', () => {
const content = renderToolCall({
tool: 'web_search', // stable id, must NOT be the display source
message: '🔍 Searching DuckDuckGo: "climate policy"',
iteration: 1,
});
expect(content).toBe('🔍 Searching DuckDuckGo: "climate policy"');
expect(content).not.toContain('web_search');
expect(content).not.toContain('Using ');
});
it('shows the MCP friendly message without duplicating the query', () => {
const content = renderToolCall({
tool: 'DuckDuckGo',
message: 'ACTION: Using DuckDuckGo - "climate policy"',
arguments: { query: 'climate policy' },
});
expect(content).toBe('ACTION: Using DuckDuckGo - "climate policy"');
// The query is embedded in the message; it must appear exactly once
// (no extra `\nQuery: "..."` appended from data.arguments).
const occurrences = content.split('climate policy').length - 1;
expect(occurrences).toBe(1);
expect(content).not.toContain('Query:');
});
it('falls back to "Using <tool>" with query when no message is present', () => {
const content = renderToolCall({
tool: 'web_search',
arguments: { query: 'climate policy' },
});
expect(content).toBe('Using web_search\nQuery: "climate policy"');
});
it('falls back to "Using unknown" when neither message nor tool is present', () => {
const content = renderToolCall({});
expect(content).toBe('Using unknown');
});
});
function renderObservation(data) {
progressComponent.updateAgentThinking({ phase: 'observation', ...data });
const step = document
.getElementById('agent-thinking-content')
.querySelector('.ldr-agent-step-content');
return step ? step.textContent : null;
}
describe('updateAgentThinking - observation rendering', () => {
it('keeps the "From {engine}" attribution and appends data.content beneath it', () => {
const content = renderObservation({
message: '📄 From the web (SearXNG): [1] Some Title (http://a.com) Snip',
content: '[1] Some Title (http://a.com)\nThe full snippet body.',
});
expect(content).toBe(
'📄 From the web (SearXNG): [1] Some Title (http://a.com) Snip\n'
+ '[1] Some Title (http://a.com)\nThe full snippet body.'
);
});
it('shows the message alone when no content is attached (short outputs)', () => {
const content = renderObservation({
message: '📄 From PubMed: No results.',
});
expect(content).toBe('📄 From PubMed: No results.');
});
it('falls back to content when no message is present', () => {
const content = renderObservation({
content: 'raw tool output only',
});
expect(content).toBe('raw tool output only');
});
it('truncates the composed result to 800 chars', () => {
const content = renderObservation({
message: '📄 From arXiv: long',
content: 'z'.repeat(1000),
});
expect(content.length).toBe(803); // 800 + '...'
expect(content.endsWith('...')).toBe(true);
expect(content.startsWith('📄 From arXiv: long\n')).toBe(true);
});
});