TraceStudio-dev/web_v0/tests/TEST_INTEGRATION.js

277 lines
9.1 KiB
JavaScript
Raw Normal View History

2026-01-12 03:32:51 +08:00
/**
* 前端功能集成测试
* 用于验证 Param 绑定EdgeType暴露端口等功能
*
* 使用方法
* 1. 在浏览器开发工具中粘贴此脚本
* 2. 调用测试函数 testParamBinding()
*/
// ============= 测试工具函数 =============
function log(...args) {
console.log(`[TraceStudio Test] ${new Date().toLocaleTimeString()}`, ...args)
}
function success(msg) {
console.log(`%c✅ ${msg}`, 'color: #22c55e; font-weight: bold')
}
function error(msg) {
console.error(`%c❌ ${msg}`, 'color: #ef4444; font-weight: bold')
}
function warn(msg) {
console.warn(`%c⚠ ${msg}`, 'color: #f59e0b; font-weight: bold')
}
// ============= 1. Param 绑定测试 =============
async function testParamBinding() {
log('开始测试Param 绑定模式')
// 从 DOM 查找 Inspector 并验证是否存在绑定模式按钮
const inspectorDiv = document.querySelector('[class*="Inspector"]') || document.body
const modeButtons = inspectorDiv.querySelectorAll('button')
const hasStaticBtn = Array.from(modeButtons).some(b => b.textContent.includes('静态值'))
const hasContextBtn = Array.from(modeButtons).some(b => b.textContent.includes('Context'))
const hasExposedBtn = Array.from(modeButtons).some(b => b.textContent.includes('暴露端口'))
if (hasStaticBtn && hasContextBtn && hasExposedBtn) {
success('发现三种 Param 绑定模式按钮')
} else {
error(`缺少绑定模式按钮: 静态=${hasStaticBtn}, Context=${hasContextBtn}, 暴露=${hasExposedBtn}`)
}
// 检查 Context 下拉列表
const contextSelects = inspectorDiv.querySelectorAll('select')
if (contextSelects.length > 0) {
const options = contextSelects[0].querySelectorAll('option')
const hasGlobalOption = Array.from(options).some(o => o.value.includes('$Global'))
if (hasGlobalOption) {
success(`Context 下拉列表已初始化,共 ${options.length} 个选项`)
} else {
warn('Context 下拉列表缺少 $Global 变量')
}
}
}
// ============= 2. EdgeType 测试 =============
async function testEdgeType() {
log('开始测试:连线 EdgeType 和 strokeWidth')
// 从 React Flow 的 edge 元素中提取 edge 数据
const svgEdges = document.querySelectorAll('svg path[stroke]')
const edgeData = {
arrayEdges: 0,
scalarEdges: 0,
strokeWidths: new Set()
}
svgEdges.forEach((path) => {
const strokeWidth = path.getAttribute('stroke-width')
if (strokeWidth) {
edgeData.strokeWidths.add(strokeWidth)
if (parseInt(strokeWidth) === 4) edgeData.arrayEdges++
else if (parseInt(strokeWidth) === 3) edgeData.scalarEdges++
}
})
if (edgeData.strokeWidths.size > 0) {
success(`检测到 ${edgeData.arrayEdges} 条数组边4px${edgeData.scalarEdges} 条标量边3px`)
log(`检测到的 strokeWidth 值:${Array.from(edgeData.strokeWidths).join(', ')}`)
} else {
warn('未检测到任何连线,请先创建节点连接')
}
}
// ============= 3. 暴露端口测试 =============
async function testExposedPorts() {
log('开始测试:暴露端口显示')
// 查找节点中的暴露端口指示符
const exposedIndicators = document.querySelectorAll('[style*="rgb(236, 72, 153)"]')
if (exposedIndicators.length > 0) {
success(`检测到 ${exposedIndicators.length} 个暴露端口指示区域`)
}
// 检查节点中的 HandleReact Flow 端口)
const handles = document.querySelectorAll('.react-flow__handle')
const exposedHandles = Array.from(handles).filter(h => {
const style = h.getAttribute('style') || ''
// 暴露端口应该是粉红色(#ec4899或绿色#22c55e
return style.includes('236') || style.includes('34, 197') || h.classList.contains('exposed')
})
if (exposedHandles.length > 0) {
success(`检测到 ${exposedHandles.length} 个暴露端口 Handle`)
} else {
warn('未检测到暴露端口,请先在 Inspector 中点击 ⚡ 暴露端口')
}
}
// ============= 4. 节点数据结构测试 =============
async function testNodeDataStructure() {
log('开始测试节点数据结构bindings, exposedPorts')
// 尝试从 window.__INITIAL_STATE__ 或其他全局状态中提取
let nodeData = null
if (window.__TRACE_STUDIO_STATE__) {
nodeData = window.__TRACE_STUDIO_STATE__.nodes
}
if (!nodeData) {
warn('无法访问全局状态,使用 React DevTools 检查')
log('请在 React DevTools 中查看 Store 的 nodes 属性')
return
}
const firstNode = nodeData[0]
if (firstNode) {
const hasBindings = 'bindings' in firstNode
const hasExposedPorts = 'exposedPorts' in firstNode
if (hasBindings) success(`节点包含 bindings 字段`)
else warn('节点缺少 bindings 字段')
if (hasExposedPorts) success(`节点包含 exposedPorts 字段`)
else warn('节点缺少 exposedPorts 字段')
log('第一个节点结构:', firstNode)
}
}
// ============= 5. API 响应测试 =============
async function testAPIResponse() {
log('开始测试API /api/plugins 响应')
try {
const response = await fetch('/api/plugins')
const data = await response.json()
if (response.ok) {
success('API /api/plugins 响应成功')
// 检查返回的插件是否包含扩展字段
const firstPlugin = Object.values(data.plugins || {})[0]
if (firstPlugin) {
const hasInputs = 'inputs' in firstPlugin
const hasOutputs = 'outputs' in firstPlugin
const hasParamSchema = 'param_schema' in firstPlugin
const hasContextVars = 'context_vars' in firstPlugin
log(`插件字段检查inputs=${hasInputs}, outputs=${hasOutputs}, param_schema=${hasParamSchema}, context_vars=${hasContextVars}`)
if (hasInputs && hasOutputs && hasParamSchema && hasContextVars) {
success('API 已返回四大属性inputs/outputs/param_schema/context_vars')
} else {
warn('API 缺少某些字段')
}
}
} else {
error(`API 响应错误:${response.status}`)
}
} catch (e) {
error(`API 请求失败:${e.message}`)
}
}
// ============= 6. 完整工作流测试 =============
async function testCompleteWorkflow() {
log('开始完整工作流测试')
const tests = [
{ name: '参数绑定', fn: testParamBinding },
{ name: '连线 EdgeType', fn: testEdgeType },
{ name: '暴露端口', fn: testExposedPorts },
{ name: '数据结构', fn: testNodeDataStructure },
{ name: 'API 响应', fn: testAPIResponse }
]
for (const test of tests) {
log(`\n--- 测试: ${test.name} ---`)
try {
await test.fn()
} catch (e) {
error(`测试失败:${e.message}`)
}
}
log('\n✨ 所有测试完成')
}
// ============= 7. 交互式检查器 =============
function inspectStore() {
log('检查 Zustand Store 状态')
// 尝试从多个位置访问 store
if (window.__ZUSTAND_DEBUG__) {
const store = window.__ZUSTAND_DEBUG__
log('Store 节点:', store.getState?.().nodes || 'N/A')
log('Store 边:', store.getState?.().edges || 'N/A')
} else {
log('Store 不可直接访问,请使用以下方法:')
log('1. 打开 React DevTools')
log('2. 选择 <Workspace /> 组件')
log('3. 在控制台中查看 $r.props 或 $r.context')
}
}
function inspectFirstNode() {
log('检查第一个节点的详细信息')
const nodeElement = document.querySelector('[data-id]')
if (nodeElement) {
const nodeId = nodeElement.getAttribute('data-id')
log(`找到节点:${nodeId}`)
// 提取节点的视觉信息
const handles = nodeElement.querySelectorAll('.react-flow__handle')
const title = nodeElement.querySelector('h4') || nodeElement.querySelector('div')
log(`节点标题:${title?.textContent || 'N/A'}`)
log(`Handle 数量:${handles.length}`)
// 分析 Handle 颜色
handles.forEach((h, i) => {
const bg = h.style.background
const position = h.classList.contains('target') ? 'LEFT' : 'RIGHT'
log(` Handle ${i}: 颜色=${bg}, 位置=${position}`)
})
} else {
warn('未找到任何节点,请先创建节点')
}
}
// ============= 导出测试函数 =============
window.testParamBinding = testParamBinding
window.testEdgeType = testEdgeType
window.testExposedPorts = testExposedPorts
window.testNodeDataStructure = testNodeDataStructure
window.testAPIResponse = testAPIResponse
window.testCompleteWorkflow = testCompleteWorkflow
window.inspectStore = inspectStore
window.inspectFirstNode = inspectFirstNode
// ============= 启动消息 =============
success('TraceStudio 测试工具已加载')
log('可用的测试函数:')
log(' testParamBinding() - Param 绑定模式')
log(' testEdgeType() - 连线 EdgeType')
log(' testExposedPorts() - 暴露端口')
log(' testNodeDataStructure() - 节点数据结构')
log(' testAPIResponse() - API 响应')
log(' testCompleteWorkflow() - 完整工作流测试')
log(' inspectStore() - 检查 Zustand Store')
log(' inspectFirstNode() - 检查第一个节点')
log('\n例如testCompleteWorkflow() 运行全部测试')