test(mxl): покрыть настройки элемента управления кейсами и проверкой

Ключ control возился дословно, но проверялся только косвенно — через
платформенную фикстуру стенда. Теперь у него свои кейсы на обе стороны
(сборка блоба из DSL и раундтрип), строка в описании ячейки с пометкой
«раундтрип, не для ручного авторинга» и проверка в валидаторе: значение
и настройки элемента управления бывают только у ячейки-поля ввода —
на корпусе ни одного вхождения в обычной ячейке.

Проверки ячеек со значением заодно перестали зависеть от наличия таких
форматов в макете: раньше блок целиком пропускался, когда ни одного
формата с признаком значения нет, — то есть ровно в том случае, который
эта проверка и ловит.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-15 13:54:57 +03:00
co-authored by Claude Opus 5
parent 43d2f660da
commit 8770ef940b
11 changed files with 399 additions and 74 deletions
@@ -1,4 +1,4 @@
# mxl-validate v1.3 — Validate 1C spreadsheet
# mxl-validate v1.4 — Validate 1C spreadsheet
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Alias('Path')]
@@ -449,43 +449,53 @@ for ($i = 0; $i -lt $formatNodes.Count; $i++) {
}
}
foreach ($ri in $root.SelectNodes("d:rowsItem", $nsMgr)) {
if ($stopped) { break }
$row = $ri.SelectSingleNode("d:row", $nsMgr)
if (-not $row) { continue }
$idxNode = $ri.SelectSingleNode("d:index", $nsMgr)
$rn = if ($idxNode) { $idxNode.InnerText } else { '?' }
$rowFmt = $row.SelectSingleNode("d:formatIndex", $nsMgr)
if ($rowFmt -and $valueFormatIdx.ContainsKey([int]$rowFmt.InnerText)) {
Report-Warn "Row ${rn}: formatIndex points to a value format (cell-only property)"
}
foreach ($cGroup in $row.SelectNodes("d:c", $nsMgr)) {
$cell = $cGroup.SelectSingleNode("d:c", $nsMgr)
if (-not $cell) { continue }
$fNode = $cell.SelectSingleNode("d:f", $nsMgr)
if (-not $fNode -or -not $valueFormatIdx.ContainsKey([int]$fNode.InnerText)) {
# Значение и настройки элемента управления бывают только у ячейки-поля ввода:
# на корпусе ни одного <v> и ни одного <control> в обычной ячейке.
if ($cell.SelectSingleNode("d:v", $nsMgr)) {
Report-Error "Row ${rn}: cell carries value but its format has no containsValue"
}
if ($cell.SelectSingleNode("d:control", $nsMgr)) {
Report-Error "Row ${rn}: cell carries control settings but its format has no containsValue"
}
continue
}
if ($cell.SelectSingleNode("d:tl", $nsMgr)) {
Report-Error "Row ${rn}: cell contains a value and text at the same time"
}
}
}
foreach ($cols in $root.SelectNodes("d:columns", $nsMgr)) {
foreach ($ci in $cols.SelectNodes("d:columnsItem", $nsMgr)) {
$col = $ci.SelectSingleNode("d:column", $nsMgr)
if (-not $col) { continue }
$fmtNode = $col.SelectSingleNode("d:formatIndex", $nsMgr)
if ($fmtNode -and $valueFormatIdx.ContainsKey([int]$fmtNode.InnerText)) {
$colIdxNode = $ci.SelectSingleNode("d:index", $nsMgr)
$colIdxText = if ($colIdxNode) { $colIdxNode.InnerText } else { '?' }
Report-Warn "Column ${colIdxText}: formatIndex points to a value format (cell-only property)"
}
}
}
$dfi = $root.SelectSingleNode("d:defaultFormatIndex", $nsMgr)
if ($dfi -and $valueFormatIdx.ContainsKey([int]$dfi.InnerText)) {
Report-Warn "defaultFormatIndex points to a value format (cell-only property)"
}
if ($valueFormatIdx.Count -gt 0) {
foreach ($ri in $root.SelectNodes("d:rowsItem", $nsMgr)) {
if ($stopped) { break }
$row = $ri.SelectSingleNode("d:row", $nsMgr)
if (-not $row) { continue }
$idxNode = $ri.SelectSingleNode("d:index", $nsMgr)
$rn = if ($idxNode) { $idxNode.InnerText } else { '?' }
$rowFmt = $row.SelectSingleNode("d:formatIndex", $nsMgr)
if ($rowFmt -and $valueFormatIdx.ContainsKey([int]$rowFmt.InnerText)) {
Report-Warn "Row ${rn}: formatIndex points to a value format (cell-only property)"
}
foreach ($cGroup in $row.SelectNodes("d:c", $nsMgr)) {
$cell = $cGroup.SelectSingleNode("d:c", $nsMgr)
if (-not $cell) { continue }
$fNode = $cell.SelectSingleNode("d:f", $nsMgr)
if (-not $fNode -or -not $valueFormatIdx.ContainsKey([int]$fNode.InnerText)) { continue }
if ($cell.SelectSingleNode("d:tl", $nsMgr)) {
Report-Error "Row ${rn}: cell contains a value and text at the same time"
}
}
}
foreach ($cols in $root.SelectNodes("d:columns", $nsMgr)) {
foreach ($ci in $cols.SelectNodes("d:columnsItem", $nsMgr)) {
$col = $ci.SelectSingleNode("d:column", $nsMgr)
if (-not $col) { continue }
$fmtNode = $col.SelectSingleNode("d:formatIndex", $nsMgr)
if ($fmtNode -and $valueFormatIdx.ContainsKey([int]$fmtNode.InnerText)) {
$colIdxNode = $ci.SelectSingleNode("d:index", $nsMgr)
$colIdxText = if ($colIdxNode) { $colIdxNode.InnerText } else { '?' }
Report-Warn "Column ${colIdxText}: formatIndex points to a value format (cell-only property)"
}
}
}
$dfi = $root.SelectSingleNode("d:defaultFormatIndex", $nsMgr)
if ($dfi -and $valueFormatIdx.ContainsKey([int]$dfi.InnerText)) {
Report-Warn "defaultFormatIndex points to a value format (cell-only property)"
}
Report-OK "Value cells: $($valueFormatIdx.Count) value formats"
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# mxl-validate v1.3 — Validate 1C spreadsheet document Template.xml
#!/usr/bin/env python3
# mxl-validate v1.4 — Validate 1C spreadsheet document Template.xml
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Validates spreadsheet Template.xml: height, palette refs, column/row indices, areas, merges."""
import sys, os, argparse
@@ -430,40 +430,61 @@ def main():
if kinds != ['xs:boolean'] and kinds != ['xs:decimal']:
r.warn(f'Format {i}: checkbox control on a type other than Boolean or Number')
if value_format_idx:
for ri in root.findall(f'{{{NS_D}}}rowsItem'):
if r.stopped:
break
row = ri.find(f'{{{NS_D}}}row')
if row is None:
for ri in root.findall(f'{{{NS_D}}}rowsItem'):
if r.stopped:
break
row = ri.find(f'{{{NS_D}}}row')
if row is None:
continue
idx_node = ri.find(f'{{{NS_D}}}index')
rn = idx_node.text if idx_node is not None else '?'
row_fmt = row.find(f'{{{NS_D}}}formatIndex')
if row_fmt is not None and row_fmt.text and int(row_fmt.text) in value_format_idx:
r.warn(f'Row {rn}: formatIndex points to a value format (cell-only property)')
for c_group in row.findall(f'{{{NS_D}}}c'):
cell = c_group.find(f'{{{NS_D}}}c')
if cell is None:
continue
idx_node = ri.find(f'{{{NS_D}}}index')
rn = idx_node.text if idx_node is not None else '?'
row_fmt = row.find(f'{{{NS_D}}}formatIndex')
if row_fmt is not None and row_fmt.text and int(row_fmt.text) in value_format_idx:
r.warn(f'Row {rn}: formatIndex points to a value format (cell-only property)')
for c_group in row.findall(f'{{{NS_D}}}c'):
cell = c_group.find(f'{{{NS_D}}}c')
if cell is None:
continue
f_node = cell.find(f'{{{NS_D}}}f')
if f_node is None or not f_node.text or int(f_node.text) not in value_format_idx:
continue
if cell.find(f'{{{NS_D}}}tl') is not None:
r.error(f'Row {rn}: cell contains a value and text at the same time')
for cols in root.findall(f'{{{NS_D}}}columns'):
for ci in cols.findall(f'{{{NS_D}}}columnsItem'):
col = ci.find(f'{{{NS_D}}}column')
if col is None:
continue
fmt_node = col.find(f'{{{NS_D}}}formatIndex')
if fmt_node is not None and fmt_node.text and int(fmt_node.text) in value_format_idx:
col_idx_node = ci.find(f'{{{NS_D}}}index')
col_idx_text = col_idx_node.text if col_idx_node is not None else '?'
r.warn(f'Column {col_idx_text}: formatIndex points to a value format (cell-only property)')
dfi = root.find(f'{{{NS_D}}}defaultFormatIndex')
if dfi is not None and dfi.text and int(dfi.text) in value_format_idx:
r.warn('defaultFormatIndex points to a value format (cell-only property)')
f_node = cell.find(f'{{{NS_D}}}f')
if f_node is None or not f_node.text or int(f_node.text) not in value_format_idx:
continue
if cell.find(f'{{{NS_D}}}tl') is not None:
r.error(f'Row {rn}: cell contains a value and text at the same time')
# Значение и настройки элемента управления бывают только у ячейки-поля ввода: на корпусе
# ни одного <v> и ни одного <control> в обычной ячейке.
for ri in root.findall(f'{{{NS_D}}}rowsItem'):
if r.stopped:
break
row = ri.find(f'{{{NS_D}}}row')
if row is None:
continue
idx_node = ri.find(f'{{{NS_D}}}index')
rn = idx_node.text if idx_node is not None else '?'
for c_group in row.findall(f'{{{NS_D}}}c'):
cell = c_group.find(f'{{{NS_D}}}c')
if cell is None:
continue
f_node = cell.find(f'{{{NS_D}}}f')
is_value = f_node is not None and f_node.text and int(f_node.text) in value_format_idx
if is_value:
continue
for tag, what in (('v', 'value'), ('control', 'control settings')):
if cell.find(f'{{{NS_D}}}{tag}') is not None:
r.error(f'Row {rn}: cell carries {what} but its format has no containsValue')
for cols in root.findall(f'{{{NS_D}}}columns'):
for ci in cols.findall(f'{{{NS_D}}}columnsItem'):
col = ci.find(f'{{{NS_D}}}column')
if col is None:
continue
fmt_node = col.find(f'{{{NS_D}}}formatIndex')
if fmt_node is not None and fmt_node.text and int(fmt_node.text) in value_format_idx:
col_idx_node = ci.find(f'{{{NS_D}}}index')
col_idx_text = col_idx_node.text if col_idx_node is not None else '?'
r.warn(f'Column {col_idx_text}: formatIndex points to a value format (cell-only property)')
dfi = root.find(f'{{{NS_D}}}defaultFormatIndex')
if dfi is not None and dfi.text and int(dfi.text) in value_format_idx:
r.warn('defaultFormatIndex points to a value format (cell-only property)')
if value_format_idx:
r.ok(f'Value cells: {len(value_format_idx)} value formats')
# --- Finalize ---