diff --git a/src/tools/automation-framework/core/base-action.js b/src/tools/automation-framework/core/base-action.js index 7022244..aa8e57b 100644 --- a/src/tools/automation-framework/core/base-action.js +++ b/src/tools/automation-framework/core/base-action.js @@ -17,29 +17,96 @@ class BaseAction { } /** - * 替换配置中的变量 - * @param {string} value - 包含变量的字符串 (如 "{{account.email}}") + * 替换配置中的变量(增强版) + * + * 支持特性: + * - 多数据源:{{account.email}}, {{site.url}}, {{config.timeout}} + * - 默认值:{{var|default}}, {{user.name|Guest}} + * - 变量不存在时警告 + * + * @param {string} value - 包含变量的字符串 * @returns {string} - 替换后的值 */ replaceVariables(value) { if (typeof value !== 'string') return value; - return value.replace(/\{\{(.+?)\}\}/g, (match, path) => { - const keys = path.trim().split('.'); - let result = this.context.data; + return value.replace(/\{\{(.+?)\}\}/g, (match, expression) => { + // 解析默认值:{{var|default}} + const [path, defaultValue] = expression.split('|').map(s => s.trim()); - for (const key of keys) { - if (result && typeof result === 'object') { - result = result[key]; - } else { - return match; // 无法解析,返回原始值 - } + // 获取变量值 + const result = this.resolveVariablePath(path); + + // 如果找到值,返回 + if (result !== undefined && result !== null) { + return result; } - return result !== undefined ? result : match; + // 如果有默认值,使用默认值 + if (defaultValue !== undefined) { + this.log('debug', `变量 "${path}" 不存在,使用默认值: "${defaultValue}"`); + return defaultValue; + } + + // 变量不存在且无默认值,发出警告 + this.log('warn', `⚠️ 变量 "${path}" 不存在,返回原始值: ${match}`); + return match; }); } + /** + * 解析变量路径,支持多个数据源 + * @param {string} path - 变量路径,如 "account.email" 或 "site.url" + * @returns {*} - 解析后的值 + */ + resolveVariablePath(path) { + const keys = path.split('.'); + const rootKey = keys[0]; + + // 确定数据源 + let dataSource; + let startIndex = 1; // 从第二个key开始 + + switch (rootKey) { + case 'site': + // {{site.url}} -> context.siteConfig.url + dataSource = this.context.siteConfig; + break; + + case 'config': + // {{config.timeout}} -> context.config + dataSource = this.context.config; + break; + + case 'env': + // {{env.API_KEY}} -> process.env + dataSource = process.env; + break; + + default: + // 默认从 context.data 读取 + // {{account.email}} -> context.data.account.email + dataSource = this.context.data; + startIndex = 0; // 从第一个key开始 + } + + if (!dataSource) { + return undefined; + } + + // 遍历路径获取值 + let result = dataSource; + for (let i = startIndex; i < keys.length; i++) { + if (result && typeof result === 'object') { + result = result[keys[i]]; + } else { + return undefined; + } + } + + return result; + } + /** * 记录日志 */ diff --git a/src/tools/automation-framework/core/config-validator.js b/src/tools/automation-framework/core/config-validator.js index a3aa7ef..6d8ff35 100644 --- a/src/tools/automation-framework/core/config-validator.js +++ b/src/tools/automation-framework/core/config-validator.js @@ -106,6 +106,9 @@ class ConfigValidator { } }); + // 类型检查 + errors.push(...this.validateFieldTypes(step, stepLabel)); + // 检查未知字段(警告) const allFields = [...schema.required, ...schema.optional, 'action']; Object.keys(step).forEach(field => { @@ -135,6 +138,47 @@ class ConfigValidator { return errors; } + /** + * 类型检查 + */ + validateFieldTypes(step, stepLabel) { + const errors = []; + + // fillForm 的 fields 应该是对象 + if (step.action === 'fillForm' && step.fields) { + if (typeof step.fields !== 'object' || Array.isArray(step.fields)) { + errors.push(`${stepLabel}: fields 必须是对象类型`); + } + } + + // 数字类型字段检查 + const numberFields = ['timeout', 'maxRetries', 'retryDelay', 'duration', 'totalTimeout', 'pollInterval']; + numberFields.forEach(field => { + if (step[field] !== undefined && typeof step[field] !== 'number') { + errors.push(`${stepLabel}: ${field} 必须是数字类型,当前为 ${typeof step[field]}`); + } + }); + + // 布尔类型字段检查 + const booleanFields = ['humanLike', 'optional', 'waitForEnabled']; + booleanFields.forEach(field => { + if (step[field] !== undefined && typeof step[field] !== 'boolean') { + errors.push(`${stepLabel}: ${field} 必须是布尔类型,当前为 ${typeof step[field]}`); + } + }); + + // 数组类型字段检查 + if (step.steps && !Array.isArray(step.steps)) { + errors.push(`${stepLabel}: steps 必须是数组类型`); + } + + if (step.verifyElements && !Array.isArray(step.verifyElements)) { + errors.push(`${stepLabel}: verifyElements 必须是数组类型`); + } + + return errors; + } + /** * 验证并打印结果 */