feat: 增强2925邮箱登录流程,添加登录失败处理和超时机制,更新相关文档和测试用例

This commit is contained in:
QLHazyCoder
2026-04-20 23:10:01 +08:00
parent 20146ce3e9
commit 6c17ffc7f4
7 changed files with 392 additions and 27 deletions
+83 -21
View File
@@ -193,6 +193,12 @@ const MAIL2925_AGREEMENT_PATTERNS = [
/服务协议/,
/隐私政策/,
];
const MAIL2925_REMEMBER_LOGIN_PATTERNS = [
/30天内免登录/,
/免登录/,
/记住登录/,
/保持登录/,
];
function normalizeNodeText(value) {
return String(value || '').replace(/\s+/g, ' ').trim();
@@ -405,46 +411,102 @@ function findAgreementContainer() {
return null;
}
function findAgreementCheckbox() {
const agreementContainer = findAgreementContainer();
if (agreementContainer) {
const checkbox = agreementContainer.querySelector('input[type="checkbox"], [role="checkbox"], .ivu-checkbox, .el-checkbox');
if (checkbox) {
return resolveActionTarget(checkbox);
}
const nearbyCheckbox = agreementContainer.parentElement?.querySelector?.('input[type="checkbox"], [role="checkbox"], .ivu-checkbox, .el-checkbox');
if (nearbyCheckbox) {
return resolveActionTarget(nearbyCheckbox);
function isAgreementText(text = '') {
const normalizedText = normalizeNodeText(text);
if (!normalizedText || MAIL2925_REMEMBER_LOGIN_PATTERNS.some((pattern) => pattern.test(normalizedText))) {
return false;
}
return /我已阅读并同意/.test(normalizedText)
|| (/服务协议/.test(normalizedText) && /隐私政策/.test(normalizedText));
}
function getCheckboxContextText(target) {
if (!target) {
return '';
}
const textParts = [];
const candidates = [
target,
target.closest?.('label'),
target.parentElement,
target.parentElement?.parentElement,
target.nextElementSibling,
target.previousElementSibling,
target.closest?.('label, div, span, p, li, form'),
].filter(Boolean);
for (const candidate of candidates) {
const text = normalizeNodeText(candidate.innerText || candidate.textContent || '');
if (text) {
textParts.push(text);
}
}
return normalizeNodeText(textParts.join(' '));
}
function findAgreementCheckbox() {
const genericCheckboxes = document.querySelectorAll('input[type="checkbox"], [role="checkbox"], .ivu-checkbox, .el-checkbox');
for (const checkbox of genericCheckboxes) {
const target = resolveActionTarget(checkbox);
if (!isVisibleNode(target)) {
continue;
}
const wrapper = target.closest('label, div, span') || target.parentElement || target;
const text = normalizeNodeText(wrapper?.innerText || wrapper?.textContent || '');
if (/我已阅读并同意/.test(text) || /服务协议/.test(text) || /隐私政策/.test(text)) {
const contextText = getCheckboxContextText(target);
if (MAIL2925_REMEMBER_LOGIN_PATTERNS.some((pattern) => pattern.test(contextText))) {
continue;
}
if (isAgreementText(contextText)) {
return target;
}
}
const agreementContainer = findAgreementContainer();
if (agreementContainer) {
const checkbox = agreementContainer.querySelector('input[type="checkbox"], [role="checkbox"], .ivu-checkbox, .el-checkbox');
if (checkbox) {
const target = resolveActionTarget(checkbox);
const contextText = getCheckboxContextText(target);
if (!MAIL2925_REMEMBER_LOGIN_PATTERNS.some((pattern) => pattern.test(contextText))) {
return target;
}
}
const nearbyCheckbox = agreementContainer.parentElement?.querySelector?.('input[type="checkbox"], [role="checkbox"], .ivu-checkbox, .el-checkbox');
if (nearbyCheckbox) {
const target = resolveActionTarget(nearbyCheckbox);
const contextText = getCheckboxContextText(target);
if (!MAIL2925_REMEMBER_LOGIN_PATTERNS.some((pattern) => pattern.test(contextText))) {
return target;
}
}
}
return null;
}
async function ensureAgreementChecked() {
const agreementCheckbox = findAgreementCheckbox();
if (!agreementCheckbox) {
const checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"], [role="checkbox"], .ivu-checkbox, .el-checkbox'))
.map((checkbox) => resolveActionTarget(checkbox))
.filter((target, index, list) => target && isVisibleNode(target) && list.indexOf(target) === index);
if (!checkboxes.length) {
return false;
}
if (isCheckboxChecked(agreementCheckbox)) {
return true;
let changed = false;
for (const checkbox of checkboxes) {
if (isCheckboxChecked(checkbox)) {
continue;
}
simulateClick(checkbox);
changed = true;
await sleep(120);
}
simulateClick(agreementCheckbox);
await sleep(150);
return isCheckboxChecked(agreementCheckbox);
return changed || checkboxes.every((checkbox) => isCheckboxChecked(checkbox));
}
function detectMail2925ViewState() {
@@ -832,7 +894,7 @@ async function ensureMail2925Session(payload = {}) {
await sleep(200);
simulateClick(loginButton);
const finalState = await waitForMail2925View('mailbox', 60000);
const finalState = await waitForMail2925View('mailbox', 20000);
if (finalState.view !== 'mailbox') {
throw new Error('2925:提交账号密码后未进入收件箱。');
}