dasdasd
This commit is contained in:
parent
6d70376a26
commit
beca781934
@ -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} - 替换后的值
|
* @returns {string} - 替换后的值
|
||||||
*/
|
*/
|
||||||
replaceVariables(value) {
|
replaceVariables(value) {
|
||||||
if (typeof value !== 'string') return value;
|
if (typeof value !== 'string') return value;
|
||||||
|
|
||||||
return value.replace(/\{\{(.+?)\}\}/g, (match, path) => {
|
return value.replace(/\{\{(.+?)\}\}/g, (match, expression) => {
|
||||||
const keys = path.trim().split('.');
|
// 解析默认值:{{var|default}}
|
||||||
let result = this.context.data;
|
const [path, defaultValue] = expression.split('|').map(s => s.trim());
|
||||||
|
|
||||||
for (const key of keys) {
|
// 获取变量值
|
||||||
if (result && typeof result === 'object') {
|
const result = this.resolveVariablePath(path);
|
||||||
result = result[key];
|
|
||||||
} else {
|
// 如果找到值,返回
|
||||||
return match; // 无法解析,返回原始值
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 记录日志
|
* 记录日志
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -106,6 +106,9 @@ class ConfigValidator {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 类型检查
|
||||||
|
errors.push(...this.validateFieldTypes(step, stepLabel));
|
||||||
|
|
||||||
// 检查未知字段(警告)
|
// 检查未知字段(警告)
|
||||||
const allFields = [...schema.required, ...schema.optional, 'action'];
|
const allFields = [...schema.required, ...schema.optional, 'action'];
|
||||||
Object.keys(step).forEach(field => {
|
Object.keys(step).forEach(field => {
|
||||||
@ -135,6 +138,47 @@ class ConfigValidator {
|
|||||||
return errors;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证并打印结果
|
* 验证并打印结果
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user