95 lines
4.3 KiB
Python
95 lines
4.3 KiB
Python
import time
|
||
import random
|
||
from framework.core.logger import get_logger
|
||
|
||
logger = get_logger("MonkeyTester")
|
||
|
||
def run_monkey_testing(page, action_count=1000):
|
||
"""
|
||
全屏幕 Monkey 稳定性测试 (随机流操作)
|
||
利用 JS 提取网页内所有的安全交互元素(避开退出、注销、删除等敏感操作),
|
||
然后执行随机规模的乱序点击和键盘输入,最后伴随鼠标抛掷。
|
||
"""
|
||
logger.info(f"🐒 开始执行全局 Monkey 测试,计划乱序攻击交互 {action_count} 次...")
|
||
|
||
# 增加捕捉页面底层级 JS 控制台报错的能力
|
||
def log_page_error(err):
|
||
logger.error(f"💥 [Monkey 探测到前端崩溃]: {err}")
|
||
|
||
# 监听未捕获异常
|
||
page.on("pageerror", log_page_error)
|
||
|
||
# 危险词汇,避免退出登录或产生破坏性删除导致平台主干链路被污染
|
||
forbidden_keywords = ['退出', '注销', 'logout', '删除', 'delete', '清理']
|
||
|
||
# JS 提取页面上的活动区域
|
||
js_get_random_element = """
|
||
(forbidden) => {
|
||
// 大面积收割所有可能挂载事件的锚点
|
||
const interactables = Array.from(document.querySelectorAll("button, a, input, textarea, [role='button'], div.p-dropdown, li.p-menuitem"));
|
||
|
||
const visible = interactables.filter(el => {
|
||
const rect = el.getBoundingClientRect();
|
||
const style = window.getComputedStyle(el);
|
||
return rect.width > 0 && rect.height > 0 && style.visibility !== 'hidden' && style.opacity !== '0';
|
||
});
|
||
|
||
const safeElements = visible.filter(el => {
|
||
const text = (el.innerText || el.value || el.placeholder || '').toLowerCase();
|
||
return !forbidden.some(kw => text.includes(kw));
|
||
});
|
||
|
||
if (safeElements.length > 0) {
|
||
const randomIndex = Math.floor(Math.random() * safeElements.length);
|
||
return safeElements[randomIndex];
|
||
}
|
||
return null;
|
||
}
|
||
"""
|
||
|
||
success_actions = 0
|
||
for i in range(action_count):
|
||
try:
|
||
# 高频发起,给予微小缓冲节奏
|
||
time.sleep(0.3)
|
||
|
||
# 使用 evaluate_handle 将 JS 节点句柄传回到 Python
|
||
element_handle = page.evaluate_handle(js_get_random_element, forbidden_keywords)
|
||
|
||
# null 在 Python 端 json_value 会被转换为 None,但在 Handle 里需谨慎评估
|
||
is_valid = page.evaluate("(el) => el !== null && typeof el === 'object' && el.tagName", element_handle)
|
||
if is_valid:
|
||
tag_name = page.evaluate("el => el.tagName", element_handle).lower()
|
||
|
||
# 开始随机交互分发
|
||
if tag_name in ['input', 'textarea']:
|
||
# 随机混沌输入
|
||
random_txt = str(random.randint(100, 99999))
|
||
element_handle.fill(random_txt, force=True, timeout=1000)
|
||
logger.debug(f"[Monkey] ⌨️ 乱序输入框写入: {random_txt}")
|
||
else:
|
||
# 强硬暴力点击 (抛弃边界验证强制触发底层事件)
|
||
element_handle.click(force=True, timeout=1000)
|
||
logger.debug(f"[Monkey] 🖱️ 盲击了元素节点: <{tag_name}>")
|
||
|
||
success_actions += 1
|
||
else:
|
||
# 找不到靶点则强制滚屏扰动页面布局重绘
|
||
distance = random.choice([-600, -300, 300, 600])
|
||
page.mouse.wheel(0, distance)
|
||
logger.debug(f"[Monkey] 📜 随机视差滚屏了 {distance} px")
|
||
|
||
except Exception as e:
|
||
# Monkey 理念:容忍动作失败,但如果抛出的是框架级失去连接则需汇报
|
||
if "Target page, context or browser has been closed" in str(e):
|
||
logger.error("🚨 浏览器实例被截断,Monkey 测试提前终止!")
|
||
break
|
||
# logger.debug(f"⚠️ Monkey 第 {i+1} 次挥拍落空: {e}")
|
||
pass
|
||
|
||
# 卸载挂载的全局异常监听,避免影响后续的常规测试
|
||
page.remove_listener("pageerror", log_page_error)
|
||
|
||
logger.info(f"✅ Monkey 测试执行完毕!成功施加了 {success_actions}/{action_count} 次有效随机压力。未探测到毁灭性 Crash。")
|
||
return True
|