277 lines
9.1 KiB
JavaScript
277 lines
9.1 KiB
JavaScript
/**
|
||
* 前端功能集成测试
|
||
* 用于验证 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} 个暴露端口指示区域`)
|
||
}
|
||
|
||
// 检查节点中的 Handle(React 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() 运行全部测试')
|