mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 08:04:56 +03:00
71607bef99
Внутренности movе в dom/: - _shared.mjs — HAS_VISIBLE_MODAL_FN, DETECT_FORM_FN, DETECT_FORMS_FN, READ_FORM_FN - forms.mjs — detectFormScript, readFormScript, findClickTargetScript, findFieldButtonScript, resolveFieldsScript - form-state.mjs — getFormStateScript - grid.mjs — resolveGridScript, readTableScript - nav.mjs — readSectionsScript, readTabsScript, switchTabScript, readCommandsScript, navigateSectionScript, openCommandScript - submenu.mjs — readSubmenuScript, clickPopupItemScript - errors.mjs — checkErrorsScript dom.mjs остался публичным entry-point с теми же 17 экспортами. Регресс tests/web-test/ зелёный (19/19, 9m 22s).
128 lines
6.4 KiB
JavaScript
128 lines
6.4 KiB
JavaScript
// web-test dom/errors v1.0 — error/diagnostic detection (balloon, messages, modal, stateWindow)
|
|
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
|
|
|
/**
|
|
* Check for validation errors / diagnostics after an action.
|
|
* Detects three patterns:
|
|
* 1. Inline balloon tooltip (div.balloon with .balloonMessage)
|
|
* 2. Messages panel (div.messages with msg0, msg1... grid rows)
|
|
* 3. Modal error dialog (high-numbered form with pressDefault + static texts)
|
|
* Returns { balloon, messages[], modal } or null if no errors.
|
|
*/
|
|
export function checkErrorsScript() {
|
|
return `(() => {
|
|
const result = {};
|
|
|
|
// 1. Inline balloon tooltip
|
|
const balloon = document.querySelector('.balloon');
|
|
if (balloon && balloon.offsetWidth > 0) {
|
|
const msg = balloon.querySelector('.balloonMessage');
|
|
const title = balloon.querySelector('.balloonTitle');
|
|
if (msg) {
|
|
result.balloon = {
|
|
title: title?.innerText?.trim() || 'Ошибка',
|
|
message: msg.innerText?.trim() || ''
|
|
};
|
|
// Count navigation arrows to indicate total errors
|
|
const fwd = balloon.querySelector('.balloonJumpFwd');
|
|
const back = balloon.querySelector('.balloonJumpBack');
|
|
const fwdDisabled = fwd?.classList.contains('disabled');
|
|
const backDisabled = back?.classList.contains('disabled');
|
|
if (fwd && !fwdDisabled) result.balloon.hasNext = true;
|
|
if (back && !backDisabled) result.balloon.hasPrev = true;
|
|
}
|
|
}
|
|
|
|
// 2. Messages panel (div.messages — pick visible one, multiple may exist across tabs)
|
|
const msgPanels = [...document.querySelectorAll('.messages')].filter(el => el.offsetWidth > 0);
|
|
for (const msgPanel of msgPanels) {
|
|
const msgs = [];
|
|
msgPanel.querySelectorAll('[id^="msg"]').forEach(line => {
|
|
if (line.offsetWidth === 0) return;
|
|
const textEl = line.querySelector('.gridBoxText');
|
|
const text = (textEl || line).innerText?.trim();
|
|
if (text) msgs.push(text);
|
|
});
|
|
if (msgs.length > 0) { result.messages = msgs; break; }
|
|
}
|
|
|
|
// 3+4. Modal dialogs: confirmation (multiple buttons) or error (single pressDefault)
|
|
// Uses form container ancestry to group buttons — pressButton elements often lack form-prefixed IDs
|
|
// Note: 1C shows some modals WITHOUT #modalSurface (e.g. "Не удалось записать" uses ps*win floating window)
|
|
// so we always scan for small forms with button patterns, regardless of modalSurface state
|
|
const formButtons = {};
|
|
[...document.querySelectorAll('a.press.pressButton')].forEach(btn => {
|
|
if (btn.offsetWidth === 0) return;
|
|
const container = btn.closest('[id$="_container"]');
|
|
const m = container?.id?.match(/^form(\\d+)_/);
|
|
if (!m) return;
|
|
const fn = m[1];
|
|
if (!formButtons[fn]) formButtons[fn] = [];
|
|
formButtons[fn].push(btn);
|
|
});
|
|
|
|
for (const [fn, buttons] of Object.entries(formButtons)) {
|
|
const p = 'form' + fn + '_';
|
|
const elCount = document.querySelectorAll('[id^="' + p + '"]').length;
|
|
if (elCount > 100) continue; // Skip large content forms
|
|
if (buttons.length > 1) {
|
|
// Confirmation dialog (multiple buttons: Да/Нет, OK/Отмена, etc.)
|
|
// Must have a Message element — real 1C confirmations always have form{N}_Message.
|
|
// Without it, this is just a regular form with multiple buttons (e.g. EPF form).
|
|
const msgEl = document.getElementById(p + 'Message');
|
|
if (!msgEl || msgEl.offsetWidth === 0) continue;
|
|
const message = msgEl.innerText?.trim() || '';
|
|
const btnNames = buttons.map(el => {
|
|
const b = { name: el.innerText?.trim() || '' };
|
|
if (el.classList.contains('pressDefault')) b.default = true;
|
|
return b;
|
|
}).filter(b => b.name);
|
|
result.confirmation = { message, buttons: btnNames.map(b => b.name), formNum: parseInt(fn) };
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Single-button modal: error dialog with pressDefault + staticText
|
|
// Skip forms with input fields — those are data entry forms (e.g. register record),
|
|
// not error dialogs. Real error modals only have staticText + buttons.
|
|
if (!result.confirmation) {
|
|
for (const [fn, buttons] of Object.entries(formButtons)) {
|
|
const p = 'form' + fn + '_';
|
|
const elCount = document.querySelectorAll('[id^="' + p + '"]').length;
|
|
if (elCount > 100) continue;
|
|
if (buttons.length !== 1 || !buttons[0].classList.contains('pressDefault')) continue;
|
|
const hasInputs = document.querySelectorAll('input.editInput[id^="' + p + '"], textarea[id^="' + p + '"]').length > 0;
|
|
if (hasInputs) continue;
|
|
const texts = [...document.querySelectorAll('[id^="' + p + '"].staticText')]
|
|
.filter(el => el.offsetWidth > 0)
|
|
.map(el => el.innerText?.trim())
|
|
.filter(Boolean);
|
|
if (texts.length > 0) {
|
|
result.modal = { message: texts.join(' '), formNum: parseInt(fn), button: buttons[0].innerText?.trim() || '' };
|
|
// Check if OpenReport link is available (platform exceptions have visible link text)
|
|
const reportLink = document.getElementById(p + 'OpenReport#text');
|
|
if (reportLink && reportLink.offsetWidth > 2 && reportLink.textContent.trim()) {
|
|
result.modal.hasReport = true;
|
|
}
|
|
// Grab AdditionalInfo/ServerText if filled (may contain extra error details)
|
|
const addInfo = document.getElementById(p + 'AdditionalInfo');
|
|
if (addInfo && addInfo.textContent && addInfo.textContent.trim()) result.modal.additionalInfo = addInfo.textContent.trim();
|
|
const srvText = document.getElementById(p + 'ServerText');
|
|
if (srvText && srvText.textContent && srvText.textContent.trim()) result.modal.serverText = srvText.textContent.trim();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. SpreadsheetDocument state window (info bar inside moxelContainer)
|
|
// Shows messages like "Не установлено значение параметра X" or "Отчет не сформирован"
|
|
const stateWins = [...document.querySelectorAll('.stateWindowSupportSurface')].filter(el => el.offsetWidth > 0);
|
|
if (stateWins.length) {
|
|
const texts = stateWins.map(el => el.innerText?.trim()).filter(Boolean);
|
|
if (texts.length) result.stateText = texts;
|
|
}
|
|
|
|
return (result.balloon || result.messages || result.modal || result.confirmation || result.stateText) ? result : null;
|
|
})()`;
|
|
}
|