(node)
| 131 | |
| 132 | // One-line representation of a node. Stable attr order so diff comparisons are deterministic. |
| 133 | function formatNode(node) { |
| 134 | let line = node.role |
| 135 | if (node.name && node.name.trim()) line += ` "${node.name.trim()}"` |
| 136 | const attrParts = [] |
| 137 | for (const k of Object.keys(node.attributes).sort()) { |
| 138 | const v = node.attributes[k] |
| 139 | if (v === undefined || v === null || v === '') continue |
| 140 | if (v === true) attrParts.push(k) |
| 141 | else attrParts.push(`${k}=${v}`) |
| 142 | } |
| 143 | if (attrParts.length > 0) line += ` [${attrParts.join(' ')}]` |
| 144 | if (node.value !== undefined && node.value !== null) { |
| 145 | const text = String(node.value).trim() |
| 146 | if (text) line += `: ${text}` |
| 147 | } |
| 148 | return line |
| 149 | } |
| 150 | |
| 151 | // Group consecutive same-role siblings. [a,a,b,a,a,a] → [[a,a],[b],[a,a,a]] |
| 152 | function groupByConsecutiveRole(nodes) { |
no test coverage detected