dasdasd
This commit is contained in:
parent
acceddac7b
commit
02d58adbe2
@ -112,10 +112,10 @@ class RetryBlockAction extends BaseAction {
|
||||
// 动态加载对应的 Action
|
||||
const ActionClass = this.getActionClass(actionType);
|
||||
|
||||
// 修复:BaseAction 构造函数签名是 (context, config)
|
||||
const action = new ActionClass(
|
||||
this.page,
|
||||
stepConfig,
|
||||
this.context
|
||||
this.context, // 第一个参数:context
|
||||
stepConfig // 第二个参数:config
|
||||
);
|
||||
|
||||
return await action.execute();
|
||||
@ -130,7 +130,8 @@ class RetryBlockAction extends BaseAction {
|
||||
fillForm: require('./fill-form-action'),
|
||||
click: require('./click-action'),
|
||||
wait: require('./wait-action'),
|
||||
custom: require('./custom-action')
|
||||
custom: require('./custom-action'),
|
||||
scroll: require('./scroll-action')
|
||||
};
|
||||
|
||||
const ActionClass = actionMap[actionType];
|
||||
|
||||
128
src/tools/automation-framework/actions/scroll-action.js
Normal file
128
src/tools/automation-framework/actions/scroll-action.js
Normal file
@ -0,0 +1,128 @@
|
||||
const BaseAction = require('../core/base-action');
|
||||
|
||||
/**
|
||||
* 滚动动作 - 页面滚动操作
|
||||
*
|
||||
* 支持多种滚动方式:
|
||||
* 1. 滚动到底部
|
||||
* 2. 滚动到顶部
|
||||
* 3. 滚动到指定元素
|
||||
* 4. 滚动指定距离
|
||||
*
|
||||
* Example:
|
||||
* - action: scroll
|
||||
* type: bottom
|
||||
*
|
||||
* - action: scroll
|
||||
* type: element
|
||||
* selector: '#submit-button'
|
||||
*
|
||||
* - action: scroll
|
||||
* type: distance
|
||||
* x: 0
|
||||
* y: 500
|
||||
*/
|
||||
class ScrollAction extends BaseAction {
|
||||
async execute() {
|
||||
const {
|
||||
type = 'bottom',
|
||||
selector,
|
||||
x = 0,
|
||||
y = 0,
|
||||
behavior = 'smooth'
|
||||
} = this.config;
|
||||
|
||||
this.log('debug', `执行滚动: ${type}`);
|
||||
|
||||
switch (type) {
|
||||
case 'bottom':
|
||||
await this.scrollToBottom(behavior);
|
||||
break;
|
||||
|
||||
case 'top':
|
||||
await this.scrollToTop(behavior);
|
||||
break;
|
||||
|
||||
case 'element':
|
||||
if (!selector) {
|
||||
throw new Error('滚动到元素需要提供 selector');
|
||||
}
|
||||
await this.scrollToElement(selector, behavior);
|
||||
break;
|
||||
|
||||
case 'distance':
|
||||
await this.scrollByDistance(x, y, behavior);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`不支持的滚动类型: ${type}`);
|
||||
}
|
||||
|
||||
// 等待滚动完成
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
this.log('debug', '✓ 滚动完成');
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到页面底部
|
||||
*/
|
||||
async scrollToBottom(behavior) {
|
||||
await this.page.evaluate((b) => {
|
||||
window.scrollTo({
|
||||
top: document.body.scrollHeight,
|
||||
left: 0,
|
||||
behavior: b
|
||||
});
|
||||
}, behavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到页面顶部
|
||||
*/
|
||||
async scrollToTop(behavior) {
|
||||
await this.page.evaluate((b) => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: b
|
||||
});
|
||||
}, behavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到指定元素
|
||||
*/
|
||||
async scrollToElement(selector, behavior) {
|
||||
const element = await this.page.$(selector);
|
||||
|
||||
if (!element) {
|
||||
throw new Error(`元素不存在: ${selector}`);
|
||||
}
|
||||
|
||||
await element.evaluate((el, b) => {
|
||||
el.scrollIntoView({
|
||||
behavior: b,
|
||||
block: 'center',
|
||||
inline: 'nearest'
|
||||
});
|
||||
}, behavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动指定距离
|
||||
*/
|
||||
async scrollByDistance(x, y, behavior) {
|
||||
await this.page.evaluate((dx, dy, b) => {
|
||||
window.scrollBy({
|
||||
top: dy,
|
||||
left: dx,
|
||||
behavior: b
|
||||
});
|
||||
}, x, y, behavior);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ScrollAction;
|
||||
@ -137,9 +137,7 @@ workflow:
|
||||
- action: click
|
||||
name: "选择计划"
|
||||
selector:
|
||||
- text: 'Select'
|
||||
selector: 'button, a'
|
||||
- text: 'Continue'
|
||||
- text: 'Select plan'
|
||||
selector: 'button, a'
|
||||
timeout: 30000
|
||||
waitAfter: 2000
|
||||
@ -152,22 +150,7 @@ workflow:
|
||||
- css: 'input[type="radio"][value="card"]'
|
||||
waitAfter: 3000
|
||||
|
||||
# 6.2 等待支付表单加载
|
||||
- action: wait
|
||||
name: "等待支付表单"
|
||||
type: element
|
||||
find:
|
||||
- css: '#cardNumber'
|
||||
timeout: 30000
|
||||
|
||||
# 6.3 处理 hCaptcha(只需处理一次)
|
||||
- action: custom
|
||||
name: "hCaptcha 验证"
|
||||
handler: "handleHCaptcha"
|
||||
params:
|
||||
timeout: 120000
|
||||
|
||||
# ==================== 步骤 6.4: 提交支付(带重试) ====================
|
||||
# ==================== 步骤 6.2: 提交支付(带重试) ====================
|
||||
- action: retryBlock
|
||||
name: "提交支付并验证"
|
||||
maxRetries: 5
|
||||
@ -178,6 +161,14 @@ workflow:
|
||||
handler: "regenerateCard"
|
||||
|
||||
steps:
|
||||
# 等待支付表单加载(每次重试都需要等待)
|
||||
- action: wait
|
||||
name: "等待支付表单"
|
||||
type: element
|
||||
find:
|
||||
- css: '#cardNumber'
|
||||
timeout: 30000
|
||||
|
||||
# 填写银行卡信息(使用重新生成的卡号)
|
||||
- action: fillForm
|
||||
name: "填写银行卡信息"
|
||||
@ -208,15 +199,26 @@ workflow:
|
||||
name: "选择澳门地址"
|
||||
handler: "selectBillingAddress"
|
||||
|
||||
# 滚动到页面底部(确保订阅按钮可见)
|
||||
- action: scroll
|
||||
name: "滚动到订阅按钮"
|
||||
type: bottom
|
||||
|
||||
# 提交支付
|
||||
- action: click
|
||||
name: "点击提交支付"
|
||||
selector:
|
||||
- text: 'Subscribe'
|
||||
- text: '订阅'
|
||||
- css: 'button[type="submit"]' # Stripe 订阅按钮
|
||||
timeout: 15000
|
||||
waitAfter: 2000
|
||||
|
||||
# 处理 hCaptcha(点击订阅后出现)
|
||||
- action: custom
|
||||
name: "hCaptcha 验证"
|
||||
handler: "handleHCaptcha"
|
||||
params:
|
||||
timeout: 120000
|
||||
|
||||
# 验证支付结果(轮询检测成功或失败)
|
||||
- action: verify
|
||||
name: "验证支付结果"
|
||||
|
||||
@ -11,6 +11,7 @@ const CustomAction = require('../actions/custom-action');
|
||||
const RetryBlockAction = require('../actions/retry-block-action');
|
||||
const ExtractAction = require('../actions/extract-action');
|
||||
const VerifyAction = require('../actions/verify-action');
|
||||
const ScrollAction = require('../actions/scroll-action');
|
||||
|
||||
/**
|
||||
* 工作流引擎 - 执行配置驱动的自动化流程
|
||||
@ -51,6 +52,7 @@ class WorkflowEngine {
|
||||
this.actionRegistry.register('retryBlock', RetryBlockAction);
|
||||
this.actionRegistry.register('extract', ExtractAction);
|
||||
this.actionRegistry.register('verify', VerifyAction);
|
||||
this.actionRegistry.register('scroll', ScrollAction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user