This commit is contained in:
dengqichen 2025-11-19 15:33:56 +08:00
parent 8ebb7779e0
commit ae1720c866
3 changed files with 74 additions and 14 deletions

View File

@ -1,5 +1,6 @@
const BaseAction = require('../core/base-action');
const SmartSelector = require('../core/smart-selector');
const { ConfigurationError, ElementNotFoundError, ValidationError } = require('../core/errors');
/**
* 点击动作
@ -9,7 +10,11 @@ class ClickAction extends BaseAction {
const selector = this.config.selector || this.config.find;
if (!selector) {
throw new Error('缺少选择器配置');
throw new ConfigurationError(
'缺少选择器配置',
'selector',
{ action: 'click', config: this.config }
);
}
this.log('info', '执行点击');
@ -19,7 +24,10 @@ class ClickAction extends BaseAction {
const element = await smartSelector.find(this.config.timeout || 10000);
if (!element) {
throw new Error(`无法找到元素: ${JSON.stringify(selector)}`);
throw new ElementNotFoundError(selector, {
action: 'click',
timeout: this.config.timeout || 10000
});
}
// 等待元素变为可点击状态(参考旧框架)
@ -169,7 +177,11 @@ class ClickAction extends BaseAction {
const element = await smartSelector.find(5000);
if (!element) {
throw new Error(`重新查找元素失败: ${JSON.stringify(selectorConfig)}`);
throw new ElementNotFoundError(selectorConfig, {
action: 'click',
operation: 'humanClick',
reason: '重新定位失败'
});
}
this.log('debug', '✓ 已重新定位元素');
@ -250,7 +262,12 @@ class ClickAction extends BaseAction {
await this.page.waitForSelector(selector, { timeout, visible: true });
this.log('debug', `✓ 新元素已出现: ${selector}`);
} catch (error) {
throw new Error(`点击后验证失败: 元素 "${selector}" 未出现`);
throw new ValidationError(
`点击后验证失败: 元素未出现`,
'元素出现',
'元素未找到',
{ selector, timeout }
);
}
}
}
@ -263,7 +280,12 @@ class ClickAction extends BaseAction {
await this.page.waitForSelector(selector, { timeout, hidden: true });
this.log('debug', `✓ 旧元素已消失: ${selector}`);
} catch (error) {
throw new Error(`点击后验证失败: 元素 "${selector}" 未消失`);
throw new ValidationError(
`点击后验证失败: 元素未消失`,
'元素消失',
'元素仍存在',
{ selector, timeout }
);
}
}
}
@ -295,7 +317,11 @@ class ClickAction extends BaseAction {
}
if (!cssSelector) {
throw new Error('无法从选择器配置中提取 CSS 选择器');
throw new ConfigurationError(
'无法从选择器配置中提取 CSS 选择器',
'selector',
{ verifyAfter, selectorConfig }
);
}
const isChecked = await this.page.evaluate((sel) => {
@ -305,7 +331,12 @@ class ClickAction extends BaseAction {
const expectedState = checked === true;
if (isChecked !== expectedState) {
throw new Error(`点击后验证失败: 元素 checked 状态为 ${isChecked},期望 ${expectedState}`);
throw new ValidationError(
`点击后验证失败: checked 状态不符`,
expectedState,
isChecked,
{ cssSelector }
);
}
this.log('debug', `✓ checked 状态验证通过: ${isChecked}`);

View File

@ -1,5 +1,6 @@
const BaseAction = require('../core/base-action');
const SmartSelector = require('../core/smart-selector');
const { ConfigurationError, ElementNotFoundError, TimeoutError } = require('../core/errors');
/**
* 填充表单动作
@ -10,7 +11,11 @@ class FillFormAction extends BaseAction {
const humanLike = this.config.humanLike !== false; // 默认使用人类行为
if (!fields || typeof fields !== 'object') {
throw new Error('表单字段配置无效');
throw new ConfigurationError(
'表单字段配置无效',
'fields',
{ provided: typeof fields, config: this.config }
);
}
this.log('info', `填写表单,共 ${Object.keys(fields).length} 个字段`);
@ -71,7 +76,11 @@ class FillFormAction extends BaseAction {
const element = await smartSelector.find(10000);
if (!element) {
throw new Error(`无法找到字段: ${JSON.stringify(selector)}`);
throw new ElementNotFoundError(selector, {
action: 'fillForm',
field: key,
value
});
}
this.log('debug', ` → 填写字段: ${key}`);
@ -85,7 +94,11 @@ class FillFormAction extends BaseAction {
// 下拉框选择(需要 CSS 选择器)
const cssSelector = selector.css || selector[0]?.css;
if (!cssSelector) {
throw new Error(`select 类型字段需要 css 选择器: ${JSON.stringify(selector)}`);
throw new ConfigurationError(
`select 类型字段需要 css 选择器`,
'selector',
{ field: key, selector, fieldType }
);
}
await this.page.select(cssSelector, value);
this.log('debug', ` → 已选择: ${value}`);
@ -146,7 +159,10 @@ class FillFormAction extends BaseAction {
const button = await smartSelector.find(10000);
if (!button) {
throw new Error(`无法找到提交按钮: ${JSON.stringify(selector)}`);
throw new ElementNotFoundError(selector, {
action: 'fillForm',
operation: 'submitForm'
});
}
// 等待按钮可点击
@ -179,7 +195,10 @@ class FillFormAction extends BaseAction {
await new Promise(resolve => setTimeout(resolve, 500));
}
throw new Error('按钮未启用(超时)');
throw new TimeoutError('等待按钮启用', timeout, {
action: 'fillForm',
selector
});
}
}

View File

@ -1,4 +1,5 @@
const BaseAction = require('../core/base-action');
const { ValidationError, ElementNotFoundError } = require('../core/errors');
/**
* 导航动作 - 打开页面
@ -39,7 +40,12 @@ class NavigateAction extends BaseAction {
// 验证页面URL是否正确避免重定向到会员中心等
const currentUrl = this.page.url();
if (this.config.verifyUrl && !currentUrl.includes(this.config.verifyUrl)) {
throw new Error(`页面跳转异常: 期望包含 "${this.config.verifyUrl}", 实际为 "${currentUrl}"`);
throw new ValidationError(
`页面跳转异常`,
`URL包含: ${this.config.verifyUrl}`,
`实际URL: ${currentUrl}`,
{ expectedUrl: this.config.verifyUrl, actualUrl: currentUrl }
);
}
// 验证关键元素存在(确保页面加载正确)
@ -86,7 +92,11 @@ class NavigateAction extends BaseAction {
try {
await this.page.waitForSelector(selector, { timeout: 10000 });
} catch (error) {
throw new Error(`页面元素验证失败: 找不到 "${selector}"`);
throw new ElementNotFoundError(selector, {
action: 'navigate',
operation: 'verifyElements',
url: this.page.url()
});
}
}