mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-27 07:01:02 +03:00
Два задокументированных ассерта бросали ВСЕГДА, то есть были мертвы:
- formTitle читал state.title, которого не заполнял никто: getFormStateScript
собирал форму без заголовка. Единственным носителем оставалась панель
открытых окон (activeTab), а она отключается в настройках 1С — на такой базе
заголовок был недоступен ничем.
- formHasField читал state.fields[name], хотя fields — массив объектов
{name, value, …}. На массиве это всегда undefined.
getFormState теперь отдаёт title. Берётся он из шапки самой формы: заголовок
лежит в атрибуте (title у .toplineBoxTitle, data-title у родителя), сам элемент
пустой — поэтому поиском по тексту он и не находился.
Выбор шапки — не «первая видимая»: при открытом всплывающем окне видимы ДВЕ,
родителя и окна, и наивное правило отдавало заголовок родителя — правдоподобный
неверный ответ, при котором тест «окно выбора открылось» зеленел бы по
документу. Приоритет взят тот же, что уже отлажен для крестика закрытия в
closeCrossScript: плавающее окно ps<N> с наибольшим индексом → собственная шапка
формы → и только потом панель открытых окон. Привязка к id, а не к тексту — не
ломается на другой локали. Панель осталась последним звеном: она отключаема, а
при всплывающем окне ещё и показывает родителя.
Диагностика раннера (resetState) тоже переведена на title с прежним activeTab
как запасным.
formHasField ищет по массиву и перечисляет доступные имена в ошибке (раньше
Object.keys по массиву давал индексы). formTitle отличает «заголовок недоступен»
(title === null) от несовпадения.
Почему не поймали раньше: из 12 ассертов сюита вызывала 8, и оба сломанных были
среди четырёх невызываемых. Теперь все четыре задействованы на настоящем выводе
getFormState — formTitle/formHasField/noErrors в 12-formstate (включая случай
всплывающего окна), tableRowCount в 09-filter. Отдельного юнит-теста намеренно
нет: состояние для него пришлось бы писать руками, а именно неверное
представление о форме состояния и породило оба дефекта.
Доки приведены к массиву: примеры вида s.fields['X']?.value в regress.md и в
спеке заменены на fields.find(f => f.name === 'X').
Проверено: заголовок на списке, форме элемента и всплывающем окне; каскад
разведён по значениям (шапка выигрывает у панели, при пустой шапке — откат);
позитив и негатив всех четырёх ассертов на реальном состоянии формы; полный
регресс 29/29 до и после, file/name/status идентичны.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
2.9 KiB
JavaScript
55 lines
2.9 KiB
JavaScript
// web-test dom/form-state v1.1 — combined detectForm + readForm + open tabs + form caption
|
|
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
|
import { DETECT_FORM_FN, DETECT_FORMS_FN, READ_FORM_FN } from './_shared.mjs';
|
|
|
|
/**
|
|
* Combined: detect form + read form + read open tabs.
|
|
* Single evaluate call instead of 3. Used by browser.getFormState().
|
|
*/
|
|
export function getFormStateScript() {
|
|
return `(() => {
|
|
${DETECT_FORM_FN}
|
|
${DETECT_FORMS_FN}
|
|
${READ_FORM_FN}
|
|
const formNum = detectForm();
|
|
const meta = detectForms();
|
|
if (formNum === null) return { form: null, formCount: 0, message: 'No form detected' };
|
|
const p = 'form' + formNum + '_';
|
|
const formData = readForm(p);
|
|
// Open tabs bar (present only when tab panel is enabled in 1C settings)
|
|
const openTabs = [];
|
|
document.querySelectorAll('[id^="openedCell_cmd_"]').forEach(el => {
|
|
const text = el.innerText?.trim();
|
|
if (!text) return;
|
|
const entry = { name: text };
|
|
if (el.classList.contains('select')) entry.active = true;
|
|
openTabs.push(entry);
|
|
});
|
|
const activeTab = openTabs.find(t => t.active)?.name || null;
|
|
// Caption of the ACTIVE form. Lives in an attribute, not in text — the div itself is empty:
|
|
// <div class="toplineBox" data-title="Контрагенты">
|
|
// <div id="VW_page1headerTopLine_title" class="toplineBoxTitle" title="Контрагенты"></div>
|
|
// Header numbering (VW_page<M>) does not match form numbering (form<N>), so the header cannot
|
|
// be picked by form number. Several headers can be visible at once — with a selection form up,
|
|
// BOTH the parent form's header and the pop-up's are visible — so "first visible" would report
|
|
// the parent's caption for the pop-up: a plausible, wrong answer.
|
|
// Priority is therefore the one already measured for the close cross (dom/forms.mjs
|
|
// closeCrossScript): floating window (ps<N>, highest index = topmost) → the form's own header →
|
|
// and only then the open-windows tab, which the user can switch off in 1C settings.
|
|
// Anchored on ids, not on the visible text, so a non-Russian locale keeps working.
|
|
const heads = [...document.querySelectorAll('[id*="headerTopLine_title"]')]
|
|
.filter(e => e.offsetWidth > 0 && e.offsetHeight > 0);
|
|
const floating = heads.filter(e => /ps\\d+headerTopLine_title$/.test(e.id));
|
|
const own = heads.filter(e => /^VW_page\\d+headerTopLine_title$/.test(e.id));
|
|
const head = floating.pop() || own.pop() || null;
|
|
let title = head
|
|
? (head.getAttribute('title') || head.parentElement?.getAttribute('data-title') || null)
|
|
: null;
|
|
if (!title) title = activeTab;
|
|
const result = { form: formNum, activeTab, title, openForms: meta.allForms, formCount: meta.formCount, ...formData };
|
|
if (meta.modal) result.modal = true;
|
|
if (openTabs.length) result.openTabs = openTabs;
|
|
return result;
|
|
})()`;
|
|
}
|