diff --git a/.claude/skills/skd-validate/SKILL.md b/.claude/skills/skd-validate/SKILL.md index 902fb49fd..f5ae14cbc 100644 --- a/.claude/skills/skd-validate/SKILL.md +++ b/.claude/skills/skd-validate/SKILL.md @@ -12,7 +12,6 @@ allowed-tools: Проверяет структурную корректность Template.xml схемы компоновки данных. Выявляет ошибки формата, битые ссылки, дубликаты имён. -Если в проекте есть XSD-схемы платформы (`/v8-xsd-fetch`), дополнительно проверяет порядок элементов, типы и значения по схеме версии формата выгрузки. ## Параметры @@ -29,3 +28,12 @@ allowed-tools: powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/skd-validate.ps1" -TemplatePath "src/МойОтчёт/Templates/ОсновнаяСхема" powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/skd-validate.ps1" -TemplatePath "Catalogs/Номенклатура/Templates/СКД/Ext/Template.xml" ``` + +## Проверка по XSD + +Если в проекте есть XSD-схемы платформы (`/v8-xsd-fetch`), дополнительно проверяет порядок элементов, типы и значения по схеме версии формата выгрузки. Строки вида `XSD 2.20, line N: <родитель>/<элемент>: …`: + +- `must come before ` — элемент не на своём месте: перенести его перед ``, содержимое не менять. `duplicate` — лишний повтор. `not allowed in

` — в этом месте такого элемента быть не должно. Остальное — недопустимое значение, текст говорит какое. +- `[ERROR]` — по схемам своей версии формата. `[WARN]` с `(no schemas for X)` — по более новой версии, ориентир, а не окончательный вердикт. +- Шаблон собран `/skd-compile` или изменён `/skd-edit` — нарушение означает дефект навыка: сообщи пользователю, а не правь XML вручную молча. +- `no schemas for format X — not checked` — предложи `/v8-xsd-fetch -Versions X`. diff --git a/.claude/skills/skd-validate/scripts/skd-validate.ps1 b/.claude/skills/skd-validate/scripts/skd-validate.ps1 index 4b6235a4f..d0c6c8b29 100644 --- a/.claude/skills/skd-validate/scripts/skd-validate.ps1 +++ b/.claude/skills/skd-validate/scripts/skd-validate.ps1 @@ -1,4 +1,4 @@ -# skd-validate v1.5 — Validate 1C DCS structure +# skd-validate v1.6 — Validate 1C DCS structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills [CmdletBinding(PositionalBinding=$false)] param( @@ -1044,6 +1044,151 @@ function Test-XsdPlatformNoise($info) { return $false } +# --- Подсказка по схеме: что именно исправить --- +# Текст валидатора про порядок («недопустимый дочерний X, ожидается A, B») читается как «добавь A», +# а означает «X стоит слишком поздно». Поэтому для нарушений порядка строим свою подсказку: тип +# родителя и порядок его детей берём из XSD, сравниваем с соседями нарушителя в документе. Обход +# схемы — свой, по XSD-документам (не PSVI .NET): так подсказки портов совпадают. + +function Get-XsdModel([string]$dir) { + $m = @{ + types = New-Object 'System.Collections.Generic.Dictionary[string,object]' ([StringComparer]::Ordinal) + elems = New-Object 'System.Collections.Generic.Dictionary[string,object]' ([StringComparer]::Ordinal) + orders = New-Object 'System.Collections.Generic.Dictionary[string,object]' ([StringComparer]::Ordinal) + } + foreach ($f in Get-ChildItem -LiteralPath $dir -Filter *.xsd -File) { + $doc = New-Object System.Xml.XmlDocument + $doc.Load($f.FullName) + $tns = $doc.DocumentElement.GetAttribute("targetNamespace") + foreach ($n in $doc.DocumentElement.ChildNodes) { + if ($n.NodeType -ne 'Element' -or $n.NamespaceURI -cne 'http://www.w3.org/2001/XMLSchema') { continue } + $name = $n.GetAttribute("name") + if (-not $name) { continue } + if ($n.get_LocalName() -ceq 'complexType' -and -not $m.types.ContainsKey("$tns|$name")) { $m.types["$tns|$name"] = $n } + elseif ($n.get_LocalName() -ceq 'element' -and -not $m.elems.ContainsKey("$tns|$name")) { $m.elems["$tns|$name"] = $n } + } + } + return $m +} + +# QName из атрибута схемы → "ns|имя" по пространствам имён узла XSD +function Resolve-XsdQName($node, [string]$qn) { + if (-not $qn) { return $null } + $pp = $qn.Split(':') + $prefix = if ($pp.Count -gt 1) { $pp[0] } else { '' } + return "$($node.GetNamespaceOfPrefix($prefix))|$($pp[-1])" +} + +# Дети типа по порядку: @{ name; type; maxOne }. База расширения — первой. Порядок не определён +# (choice, all, any, group) или тип неизвестен — $null. +function Get-XsdChildOrder($model, [string]$typeKey) { + if (-not $typeKey) { return $null } + # Список отдаём через «,»: иначе PS развернёт его — пустой станет $null, одиночный — самим элементом + if ($model.orders.ContainsKey($typeKey)) { $v = $model.orders[$typeKey]; if ($null -eq $v) { return $null }; return ,$v } + $model.orders[$typeKey] = $null # защита от циклов + $ct = if ($model.types.ContainsKey($typeKey)) { $model.types[$typeKey] } else { $null } + if (-not $ct) { return $null } + $list = New-Object System.Collections.ArrayList + $holder = $ct + foreach ($c in $ct.ChildNodes) { + if ($c.NodeType -ne 'Element') { continue } + if ($c.get_LocalName() -ceq 'simpleContent') { $model.orders[$typeKey] = $list; return ,$list } + if ($c.get_LocalName() -ceq 'complexContent') { + $ext = $null + foreach ($x in $c.ChildNodes) { if ($x.NodeType -eq 'Element' -and $x.get_LocalName() -ceq 'extension') { $ext = $x } } + if (-not $ext) { return $null } + $baseKey = Resolve-XsdQName $ext $ext.GetAttribute("base") + $base = Get-XsdChildOrder $model $baseKey + # База известна, но порядок у неё не определён — не определён и здесь + if ($null -eq $base -and $model.types.ContainsKey($baseKey)) { return $null } + if ($base) { foreach ($b in $base) { [void]$list.Add($b) } } + $holder = $ext + } + } + foreach ($c in $holder.ChildNodes) { + if ($c.NodeType -ne 'Element') { continue } + $ln = $c.get_LocalName() + if ($ln -ceq 'sequence') { if (-not (Add-XsdSequence $model $c $list)) { return $null } } + elseif ($ln -ceq 'choice' -or $ln -ceq 'all' -or $ln -ceq 'group' -or $ln -ceq 'any') { return $null } + } + $model.orders[$typeKey] = $list + return ,$list +} + +function Add-XsdSequence($model, $seq, $list) { + foreach ($c in $seq.ChildNodes) { + if ($c.NodeType -ne 'Element') { continue } + $ln = $c.get_LocalName() + if ($ln -ceq 'element') { + $ref = $c.GetAttribute("ref") + if ($ref) { + $rk = Resolve-XsdQName $c $ref + $ge = if ($model.elems.ContainsKey($rk)) { $model.elems[$rk] } else { $null } + $name = $rk.Split('|')[-1] + $type = if ($ge -and $ge.GetAttribute("type")) { Resolve-XsdQName $ge $ge.GetAttribute("type") } else { $null } + } else { + $name = $c.GetAttribute("name") + $type = if ($c.GetAttribute("type")) { Resolve-XsdQName $c $c.GetAttribute("type") } else { $null } + } + $mx = $c.GetAttribute("maxOccurs") + [void]$list.Add(@{ name = $name; type = $type; maxOne = (-not $mx -or $mx -ceq '1') }) + } elseif ($ln -ceq 'sequence') { + if (-not (Add-XsdSequence $model $c $list)) { return $false } + } elseif ($ln -ceq 'annotation') { + continue + } else { + return $false + } + } + return $true +} + +# Тип родителя нарушителя: спуск от корня по цепочке предков (@{ name; ns; xsiKey }) +function Resolve-XsdParentType($model, $ancestors) { + if ($ancestors.Count -eq 0) { return $null } + $root = $ancestors[0] + $rk = "$($root.ns)|$($root.name)" + if ($root.xsiKey) { $type = $root.xsiKey } + elseif ($model.elems.ContainsKey($rk) -and $model.elems[$rk].GetAttribute("type")) { $type = Resolve-XsdQName $model.elems[$rk] $model.elems[$rk].GetAttribute("type") } + elseif ($model.types.ContainsKey($rk)) { $type = $rk } + else { return $null } + for ($i = 1; $i -lt $ancestors.Count; $i++) { + $a = $ancestors[$i] + if ($a.xsiKey) { $type = $a.xsiKey; continue } + $order = Get-XsdChildOrder $model $type + if ($null -eq $order) { return $null } + $hit = $null + foreach ($o in $order) { if ($o.name -ceq $a.name) { $hit = $o; break } } + if (-not $hit -or -not $hit.type) { return $null } + $type = $hit.type + } + return $type +} + +function Get-XsdOrderHint($model, $ancestors, [string]$name, $preceding) { + $pt = Resolve-XsdParentType $model $ancestors + $order = Get-XsdChildOrder $model $pt + if ($null -eq $order) { return $null } + $names = [string[]]@($order | ForEach-Object { $_.name }) + $idx = [array]::IndexOf($names, $name) + if ($idx -lt 0) { return "not allowed in <$($ancestors[$ancestors.Count - 1].name)>" } + if ($order[$idx].maxOne -and ([string[]]@($preceding) -ccontains $name)) { return "duplicate — only one <$name> allowed" } + foreach ($sib in $preceding) { + if ([array]::IndexOf($names, [string]$sib) -gt $idx) { return "must come before <$sib>" } + } + return $null +} + +# Текст валидатора без пространств имён: остаются имена и значения +function Compress-XsdMessage([string]$m) { + $m = $m -replace '[\r\n]+', ' ' + $m = $m -replace '\s(в пространстве имен|in namespace)\s"[^"]*"', '' + $m = $m -replace "\s(в пространстве имен|in namespace)\s'[^']*'", '' + $m = $m -replace '\{[^}]*\}', '' + $m = $m -replace '(["''])https?://[^"'']*:', '$1' + return $m.Trim() +} + function Invoke-XsdCheck { $xsdRoot = Resolve-XsdRoot ([System.IO.Path]::GetDirectoryName($resolvedPath)) if (-not $xsdRoot) { Report-OK "XSD: no schemas in project — not checked"; return } @@ -1085,16 +1230,24 @@ function Invoke-XsdCheck { $set.XmlResolver = $null foreach ($sc in $schemas) { [void]$set.Add($sc) } try { $set.Compile() } catch { Report-Warn "XSD: schemas in '$useDir' do not compile — not checked: $($_.Exception.Message)"; return } + $script:xsdModel = Get-XsdModel $useDir # Своя цепочка открытых элементов: в обработчике нужен родитель нарушителя $script:xsdStack = New-Object System.Collections.ArrayList $script:xsdFound = 0 + # Ошибку атрибута .NET сообщает, стоя на атрибуте: имени элемента-владельца в этот момент нет. + # Откладываем и выводим после Read(), когда читатель уже на элементе. + $script:xsdPending = New-Object System.Collections.ArrayList $script:xsdTag = if ($exact) { "XSD $useVer" } else { "XSD $useVer (no schemas for $ver)" } $script:xsdExact = $exact $handler = { param($sender, $e) if ($script:stopped) { return } $st = $script:xsdStack + if ($sender.NodeType -eq 'Attribute') { + [void]$script:xsdPending.Add(@{ line = $e.Exception.LineNumber; text = (Compress-XsdMessage $e.Message) }) + return + } if ($sender.NodeType -eq 'Element') { $xt = $sender.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance") $info = @{ elem = $sender.LocalName; parent = $(if ($st.Count -gt 0) { $st[$st.Count - 1].name } else { '' }); xsiNs = ''; xsiLocal = '' } @@ -1109,7 +1262,13 @@ function Invoke-XsdCheck { } if (Test-XsdPlatformNoise $info) { return } $script:xsdFound++ - $msg = "$($script:xsdTag), line $($e.Exception.LineNumber): $($info.parent)/$($info.elem): $($e.Message -replace '[\r\n]+', ' ')" + $hint = $null + if ($sender.NodeType -eq 'Element') { + $prec = if ($st.Count -gt 0) { $st[$st.Count - 1].children } else { @() } + $hint = Get-XsdOrderHint $script:xsdModel $st $sender.LocalName $prec + } + $text = if ($hint) { $hint } else { Compress-XsdMessage $e.Message } + $msg = "$($script:xsdTag), line $($e.Exception.LineNumber): $($info.parent)/$($info.elem): $text" if ($script:xsdExact) { Report-Error $msg } else { Report-Warn $msg } } $rs = New-Object System.Xml.XmlReaderSettings @@ -1120,13 +1279,32 @@ function Invoke-XsdCheck { $r = [System.Xml.XmlReader]::Create($resolvedPath, $rs) try { while ($r.Read()) { - if ($r.NodeType -eq 'Element' -and -not $r.IsEmptyElement) { + if ($r.NodeType -eq 'Element') { + # Имя — в дети родителя (и у пустого элемента: он тоже сосед для подсказки) + if ($script:xsdStack.Count -gt 0) { [void]$script:xsdStack[$script:xsdStack.Count - 1].children.Add($r.LocalName) } + foreach ($pa in $script:xsdPending) { + $xt = $r.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance") + $info = @{ elem = $r.LocalName; parent = $(if ($script:xsdStack.Count -gt 0) { $script:xsdStack[$script:xsdStack.Count - 1].name } else { '' }); xsiNs = ''; xsiLocal = '' } + if ($xt) { + $pp = $xt.Split(':') + $info.xsiLocal = $pp[-1] + if ($pp.Count -gt 1) { $info.xsiNs = "$($r.LookupNamespace($pp[0]))" } + } + if (Test-XsdPlatformNoise $info) { continue } + $script:xsdFound++ + $msg = "$($script:xsdTag), line $($pa.line): $($info.parent)/$($info.elem): $($pa.text)" + if ($script:xsdExact) { Report-Error $msg } else { Report-Warn $msg } + } + $script:xsdPending.Clear() + if ($script:stopped) { break } + if ($r.IsEmptyElement) { continue } $xt = $r.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance") - $entry = @{ name = $r.LocalName; xsiNs = ''; xsiLocal = '' } + $entry = @{ name = $r.LocalName; ns = $r.NamespaceURI; xsiNs = ''; xsiLocal = ''; xsiKey = $null; children = New-Object System.Collections.ArrayList } if ($xt) { $pp = $xt.Split(':') $entry.xsiLocal = $pp[-1] - if ($pp.Count -gt 1) { $entry.xsiNs = "$($r.LookupNamespace($pp[0]))" } + $entry.xsiNs = if ($pp.Count -gt 1) { "$($r.LookupNamespace($pp[0]))" } else { "$($r.LookupNamespace(''))" } + $entry.xsiKey = "$($entry.xsiNs)|$($entry.xsiLocal)" } [void]$script:xsdStack.Add($entry) } elseif ($r.NodeType -eq 'EndElement' -and $script:xsdStack.Count -gt 0) { diff --git a/.claude/skills/skd-validate/scripts/skd-validate.py b/.claude/skills/skd-validate/scripts/skd-validate.py index ce0ee9f6b..4c6289ef9 100644 --- a/.claude/skills/skd-validate/scripts/skd-validate.py +++ b/.claude/skills/skd-validate/scripts/skd-validate.py @@ -1,4 +1,4 @@ -# skd-validate v1.5 — Validate 1C DCS structure (Python port) +# skd-validate v1.6 — Validate 1C DCS structure (Python port) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import os @@ -1111,6 +1111,182 @@ def _xsd_error_element(err): return None +# ── Подсказка по схеме: что именно исправить ── +# Текст валидатора про порядок («недопустимый дочерний X, ожидается A, B») читается как «добавь A», +# а означает «X стоит слишком поздно». Поэтому для нарушений порядка строим свою подсказку: тип +# родителя и порядок его детей берём из XSD, сравниваем с соседями нарушителя в документе. Обход +# схемы — свой, по XSD-документам, как в PS-мастере: так подсказки портов совпадают. + +def xsd_model(vdir): + m = {"types": {}, "elems": {}, "orders": {}} + for name in sorted(os.listdir(vdir)): + fp = os.path.join(vdir, name) + if not (name.lower().endswith(".xsd") and os.path.isfile(fp)): + continue + root_el = etree.parse(fp).getroot() + tns = root_el.get("targetNamespace") or "" + for n in root_el: + if not isinstance(n.tag, str) or etree.QName(n).namespace != XS_NS or not n.get("name"): + continue + key = f"{tns}|{n.get('name')}" + ln = etree.QName(n).localname + if ln == "complexType": + m["types"].setdefault(key, n) + elif ln == "element": + m["elems"].setdefault(key, n) + return m + + +def xsd_resolve_qname(node, qn): + """QName из атрибута схемы → "ns|имя" по пространствам имён узла XSD.""" + if not qn: + return None + pp = qn.split(":") + prefix = pp[0] if len(pp) > 1 else None + return f"{node.nsmap.get(prefix) or ''}|{pp[-1]}" + + +def xsd_child_order(model, type_key): + """Дети типа по порядку: {name, type, maxOne}. База расширения — первой. Порядок не определён + (choice, all, any, group) или тип неизвестен — None.""" + if not type_key: + return None + if type_key in model["orders"]: + return model["orders"][type_key] + model["orders"][type_key] = None # защита от циклов + ct = model["types"].get(type_key) + if ct is None: + return None + lst = [] + holder = ct + for c in ct: + if not isinstance(c.tag, str): + continue + ln = etree.QName(c).localname + if ln == "simpleContent": + model["orders"][type_key] = lst + return lst + if ln == "complexContent": + ext = None + for x in c: + if isinstance(x.tag, str) and etree.QName(x).localname == "extension": + ext = x + if ext is None: + return None + base_key = xsd_resolve_qname(ext, ext.get("base")) + base = xsd_child_order(model, base_key) + # База известна, но порядок у неё не определён — не определён и здесь + if base is None and base_key in model["types"]: + return None + if base: + lst.extend(base) + holder = ext + for c in holder: + if not isinstance(c.tag, str): + continue + ln = etree.QName(c).localname + if ln == "sequence": + if not _xsd_add_sequence(model, c, lst): + return None + elif ln in ("choice", "all", "group", "any"): + return None + model["orders"][type_key] = lst + return lst + + +def _xsd_add_sequence(model, seq, lst): + for c in seq: + if not isinstance(c.tag, str): + continue + ln = etree.QName(c).localname + if ln == "element": + ref = c.get("ref") + if ref: + rk = xsd_resolve_qname(c, ref) + ge = model["elems"].get(rk) + name = rk.split("|")[-1] + typ = xsd_resolve_qname(ge, ge.get("type")) if ge is not None and ge.get("type") else None + else: + name = c.get("name") + typ = xsd_resolve_qname(c, c.get("type")) if c.get("type") else None + mx = c.get("maxOccurs") + lst.append({"name": name, "type": typ, "maxOne": (not mx or mx == "1")}) + elif ln == "sequence": + if not _xsd_add_sequence(model, c, lst): + return False + elif ln == "annotation": + continue + else: + return False + return True + + +def _xsd_ancestor(el): + """Предок для спуска по схеме: {name, ns, xsiKey}.""" + q = etree.QName(el) + e = {"name": q.localname, "ns": q.namespace or "", "xsiKey": None} + xt = el.get(XSI_TYPE) + if xt: + pp = xt.split(":") + prefix = pp[0] if len(pp) > 1 else None + e["xsiKey"] = f"{el.nsmap.get(prefix) or ''}|{pp[-1]}" + return e + + +def xsd_parent_type(model, ancestors): + """Тип родителя нарушителя: спуск от корня по цепочке предков.""" + if not ancestors: + return None + root = ancestors[0] + rk = f"{root['ns']}|{root['name']}" + if root["xsiKey"]: + typ = root["xsiKey"] + elif rk in model["elems"] and model["elems"][rk].get("type"): + typ = xsd_resolve_qname(model["elems"][rk], model["elems"][rk].get("type")) + elif rk in model["types"]: + typ = rk + else: + return None + for a in ancestors[1:]: + if a["xsiKey"]: + typ = a["xsiKey"] + continue + order = xsd_child_order(model, typ) + if order is None: + return None + hit = next((o for o in order if o["name"] == a["name"]), None) + if hit is None or not hit["type"]: + return None + typ = hit["type"] + return typ + + +def xsd_order_hint(model, ancestors, name, preceding): + order = xsd_child_order(model, xsd_parent_type(model, ancestors)) + if order is None: + return None + names = [o["name"] for o in order] + if name not in names: + return f"not allowed in <{ancestors[-1]['name']}>" + idx = names.index(name) + if order[idx]["maxOne"] and name in preceding: + return f"duplicate — only one <{name}> allowed" + for sib in preceding: + if sib in names and names.index(sib) > idx: + return f"must come before <{sib}>" + return None + + +def compress_xsd_message(m): + """Текст валидатора без пространств имён: остаются имена и значения.""" + m = re.sub(r'[\r\n]+', ' ', m) + m = re.sub(r'\s(в пространстве имен|in namespace)\s"[^"]*"', '', m) + m = re.sub(r"\s(в пространстве имен|in namespace)\s'[^']*'", '', m) + m = re.sub(r'\{[^}]*\}', '', m) + m = re.sub(r'(["\'])https?://[^"\']*:', r'\1', m) + return ' '.join(m.split()) + + def xsd_check(): xsd_root = resolve_xsd_root(os.path.dirname(resolved_path)) if not xsd_root: @@ -1133,6 +1309,7 @@ def xsd_check(): use_ver = cands[0] exact = use_ver == ver + model = xsd_model(os.path.join(xsd_root, use_ver)) tmp = tempfile.mkdtemp(prefix="skd-xsd-") try: try: @@ -1154,7 +1331,15 @@ def xsd_check(): if xsd_platform_noise(info): continue found += 1 - msg = f"{tag}, line {err.line}: {info['parent']}/{info['elem']}: {' '.join(err.message.split())}" + hint = None + # «Лишний/не на месте» у libxml2 — «This element is not expected» (текст не локализуется) + if el is not None and "This element is not expected" in err.message: + ancestors = [_xsd_ancestor(a) for a in reversed(list(el.iterancestors()))] + preceding = [etree.QName(x).localname for x in reversed(list(el.itersiblings(preceding=True))) + if isinstance(x.tag, str)] + hint = xsd_order_hint(model, ancestors, etree.QName(el).localname, preceding) + text = hint or compress_xsd_message(err.message) + msg = f"{tag}, line {err.line}: {info['parent']}/{info['elem']}: {text}" if exact: report_error(msg) else: diff --git a/tests/skills/cases/skd-validate/fixtures/xsd-hints-220/Configuration.xml b/tests/skills/cases/skd-validate/fixtures/xsd-hints-220/Configuration.xml new file mode 100644 index 000000000..398d6c6e9 --- /dev/null +++ b/tests/skills/cases/skd-validate/fixtures/xsd-hints-220/Configuration.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/skills/cases/skd-validate/fixtures/xsd-hints-220/Reports/Отчет/Templates/Схема/Ext/Template.xml b/tests/skills/cases/skd-validate/fixtures/xsd-hints-220/Reports/Отчет/Templates/Схема/Ext/Template.xml new file mode 100644 index 000000000..045e2fd17 --- /dev/null +++ b/tests/skills/cases/skd-validate/fixtures/xsd-hints-220/Reports/Отчет/Templates/Схема/Ext/Template.xml @@ -0,0 +1,106 @@ + + + + ИсточникДанных1 + Local + + + НаборДанных1 + + Число + Число + Формат(Число, "ЧГ=") + Формат(Число, "ЧГ=") + + xs:decimal + + 10 + 0 + Any + + + + + Флаг + Флаг + x + + xs:boolean + + + ИсточникДанных1 + ВЫБРАТЬ 1 КАК Число, ИСТИНА КАК Флаг + + + Удвоенное + Число * 2 + + true + + + xs:decimal + + 10 + 0 + Any + + + + + П + + xs:boolean + + false + false + + + Основной + + + ru + Основной + + + + + + Число + + + + + Флаг + Equal + true + + + + + П + false + 0eb70b37-3a01-40a0-81c1-98028ef06a05 + + + + + Заголовок + + + ru + Пример + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/skd-validate/xsd-exact-errors.json b/tests/skills/cases/skd-validate/xsd-exact-errors.json index 509421c7f..ef2bd1d58 100644 --- a/tests/skills/cases/skd-validate/xsd-exact-errors.json +++ b/tests/skills/cases/skd-validate/xsd-exact-errors.json @@ -1,5 +1,5 @@ { - "name": "XSD точной версии: нарушенный порядок и False — ошибки с местом нарушения", + "name": "XSD точной версии: подсказки по порядку и сжатый текст без пространств имён", "setup": "fixture:xsd-bad-220", "requires": ".v8-xsd/2.20", "preRun": [ @@ -18,11 +18,12 @@ "expectError": true, "expect": { "stdoutContains": [ - "[ERROR] XSD 2.20, line 20: field/presentationExpression", - "[ERROR] XSD 2.20, line 43: calculatedField/useRestriction", - "[ERROR] XSD 2.20, line 52: parameter/value", - "[ERROR] XSD 2.20, line 87: settings/dataParameters" - ] + "[ERROR] XSD 2.20, line 20: field/presentationExpression: must come before ", + "[ERROR] XSD 2.20, line 43: calculatedField/useRestriction: must come before ", + "[ERROR] XSD 2.20, line 52: parameter/value:", + "[ERROR] XSD 2.20, line 87: settings/dataParameters: must come before " + ], + "stdoutNotContains": "http://v8.1c.ru" }, "noSnapshot": "результат — только вывод; схемы 1С не должны попасть в эталон" } diff --git a/tests/skills/cases/skd-validate/xsd-hints-unknown-duplicate.json b/tests/skills/cases/skd-validate/xsd-hints-unknown-duplicate.json new file mode 100644 index 000000000..f5e4bb563 --- /dev/null +++ b/tests/skills/cases/skd-validate/xsd-hints-unknown-duplicate.json @@ -0,0 +1,15 @@ +{ + "name": "Подсказки XSD: повтор элемента и элемент, которого нет в модели родителя", + "setup": "fixture:xsd-hints-220", + "requires": ".v8-xsd/2.20", + "preRun": [{ "writeFile": { "path": ".v8-project.json", "content": { "xsdPath": "{repoRoot}/.v8-xsd" } } }], + "params": { "templatePath": "Reports/Отчет/Templates/Схема/Ext/Template.xml" }, + "expectError": true, + "expect": { + "stdoutContains": [ + "field/presentationExpression: duplicate — only one allowed", + "field/fooBar: not allowed in " + ] + }, + "noSnapshot": "результат — только вывод; схемы 1С не должны попасть в эталон" +} diff --git a/tests/skills/cases/skd-validate/xsd-newer-warns.json b/tests/skills/cases/skd-validate/xsd-newer-warns.json index 9c96e75b3..b20e11e66 100644 --- a/tests/skills/cases/skd-validate/xsd-newer-warns.json +++ b/tests/skills/cases/skd-validate/xsd-newer-warns.json @@ -20,7 +20,7 @@ }, "expect": { "stdoutContains": [ - "[WARN] XSD 2.12 (no schemas for 2.11), line 20: field/presentationExpression" + "[WARN] XSD 2.12 (no schemas for 2.11), line 20: field/presentationExpression: must come before " ], "stdoutNotContains": "[ERROR]" },