- Showcase UI Reorganization: * Move web UI dashboard from root directory into apps/showcase_ui/ for cleaner project structure * Add 'artemis ui' CLI command (artemis/interfaces/cli/commands/ui.py) and launcher scripts for the interactive showcase console - Standalone MCP Server & Notifiers (mcp_server/): * Implement dedicated MCP server package with mobile automation tools (device_state, inspect_trace, task_manager, task_runner) * Add multi-channel notifiers (AgentAPI, Desktop notifications, Webhooks, File output, and Composite routing) * Introduce background task runner and trace store for autonomous mobile execution monitoring - Cross-Platform Runtime & Toolchain Support: * Add artemis/platform/ with OS-specific implementations for Linux, macOS (Darwin), and Windows * Add artemis/runtime/supervisor.py for resilient process and agent runtime supervision * Add artemis/toolchain/ with dependency resolution and SDK descriptors * Add cross-platform launcher scripts (start.sh, start.bat) and installer scripts (scripts/install_deps.*) - Core Enhancements & Diagnostics: * Enhance coordinate normalization, OCR API fallback, and perception pipeline * Add credential validation (credentials_validator.py) and system diagnostics * Update bilingual documentation (README.md, README_CN.md) and architecture diagrams - Test Suite Expansion: * Add integration tests for cloud gateway, remote ADB, and Artemis login * Add unit tests covering core diagnostics, platform detection, toolchain, credentials validator, optional OCR, and MCP tools
113 lines
4.0 KiB
TypeScript
113 lines
4.0 KiB
TypeScript
import {
|
|
getStepPreImageUrl,
|
|
getStepPostImageUrl
|
|
} from './action-formatter.util';
|
|
|
|
describe('action-formatter.util screenshot chaining', () => {
|
|
it('should chain pre/post images correctly in FailureAnalyzer multi-step recovery', () => {
|
|
const stepData = {
|
|
step_id: 'step-06',
|
|
pre_image_name: '01a0e8d8cddbeb6a352e3bef1afbee0f0476a05b0fc62abb78e59c7b6aa16079',
|
|
post_image_name: '484361b1ab4b340415e01042e3496f0edc0549c837c897ece242835d12d7d71d',
|
|
action_taken: {
|
|
action: 'tap',
|
|
coordinates: [540, 720],
|
|
timestamp: 1787077538.0
|
|
},
|
|
last_execution_result: {
|
|
status: 'success',
|
|
repair_status: 'fixed',
|
|
execution: [
|
|
{
|
|
attempts: ['Pre-execution validation failed']
|
|
}
|
|
]
|
|
},
|
|
generic_tools: [
|
|
{
|
|
trace_id: 'tool-save-note',
|
|
name: 'save_note',
|
|
type: 'tool',
|
|
timestamp: 1787077537.0,
|
|
payload: { args: { title: 'Note 1' } }
|
|
},
|
|
{
|
|
trace_id: 'agent-failure-analyzer',
|
|
name: 'failure_analyzer',
|
|
agent_name: 'failure_analyzer',
|
|
type: 'agent',
|
|
timestamp: 1787077540.0,
|
|
payload: {
|
|
args: {
|
|
pre_screenshot_name: '01a0e8d8cddbeb6a352e3bef1afbee0f0476a05b0fc62abb78e59c7b6aa16079',
|
|
post_screenshot_name: 'fbdf17fa7a970d2c37aa52ae4336119e76a7d13ea457811cf8aaab19b3f53843'
|
|
}
|
|
}
|
|
},
|
|
{
|
|
trace_id: 'action-click-1',
|
|
name: 'click',
|
|
type: 'action',
|
|
agent_name: 'failure_analyzer',
|
|
timestamp: 1787077607.0,
|
|
payload: {
|
|
args: { target: '[500, 300]' },
|
|
result: {
|
|
outcome: 'Clicked successfully',
|
|
post_image_name: '18bd2ac49e64976f1e74e828ddae76e54da1587044b1d3fb01317dd202ff7c67'
|
|
}
|
|
}
|
|
},
|
|
{
|
|
trace_id: 'action-click-seq-2',
|
|
name: 'click_sequence',
|
|
type: 'action',
|
|
agent_name: 'failure_analyzer',
|
|
timestamp: 1787077631.0,
|
|
payload: {
|
|
args: { sequence: '[[500, 300], [876, 360]]' },
|
|
result: {
|
|
outcome: 'Sequence clicked successfully',
|
|
post_image_name: '484361b1ab4b340415e01042e3496f0edc0549c837c897ece242835d12d7d71d'
|
|
}
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
const action1 = stepData.generic_tools[2]; // click
|
|
const action2 = stepData.generic_tools[3]; // click_sequence
|
|
|
|
// Action 1: Pre should be the failed state screenshot (fbdf17fa...), Post should be Action 1's post (18bd2ac4...)
|
|
const act1Pre = getStepPreImageUrl(stepData, action1);
|
|
const act1Post = getStepPostImageUrl(stepData, action1);
|
|
expect(act1Pre).toBe('/images/fbdf17fa7a970d2c37aa52ae4336119e76a7d13ea457811cf8aaab19b3f53843');
|
|
expect(act1Post).toBe('/images/18bd2ac49e64976f1e74e828ddae76e54da1587044b1d3fb01317dd202ff7c67');
|
|
|
|
// Action 2: "下一次的决策前是上一次的动作后" -> Pre MUST be Action 1's post (18bd2ac4...), Post should be Action 2's post (484361b1...)
|
|
const act2Pre = getStepPreImageUrl(stepData, action2);
|
|
const act2Post = getStepPostImageUrl(stepData, action2);
|
|
expect(act2Pre).toBe('/images/18bd2ac49e64976f1e74e828ddae76e54da1587044b1d3fb01317dd202ff7c67');
|
|
expect(act2Post).toBe('/images/484361b1ab4b340415e01042e3496f0edc0549c837c897ece242835d12d7d71d');
|
|
});
|
|
|
|
it('should handle standard single-action step correctly', () => {
|
|
const stepData = {
|
|
step_id: 'step-01',
|
|
pre_image_name: 'pre123',
|
|
post_image_name: 'post456',
|
|
action_taken: {
|
|
action: 'tap',
|
|
coordinates: [100, 200],
|
|
timestamp: 1000
|
|
}
|
|
};
|
|
|
|
const preUrl = getStepPreImageUrl(stepData, stepData.action_taken);
|
|
const postUrl = getStepPostImageUrl(stepData, stepData.action_taken);
|
|
|
|
expect(preUrl).toBe('/images/pre123');
|
|
expect(postUrl).toBe('/images/post456');
|
|
});
|
|
});
|