@zombie-mermaid/ascii-renderer 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +146 -0
- package/dist/index.d.ts +146 -0
- package/dist/index.js +5392 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -0
- package/src/__tests__/ascii-arrowhead-direction-1083.test.ts +51 -0
- package/src/__tests__/ascii-canvas-first-claim-wins-1093.test.ts +89 -0
- package/src/__tests__/ascii-canvas-size-offset-1093.test.ts +95 -0
- package/src/__tests__/ascii-canvas-write.test.ts +132 -0
- package/src/__tests__/ascii-chain-edge-overlap-1067.test.ts +100 -0
- package/src/__tests__/ascii-charset-border-junctions.test.ts +63 -0
- package/src/__tests__/ascii-cjk-width.test.ts +150 -0
- package/src/__tests__/ascii-class-box-occupancy.test.ts +462 -0
- package/src/__tests__/ascii-class-column-width-488-489.test.ts +639 -0
- package/src/__tests__/ascii-class-cross-level-jog-corruption.test.ts +187 -0
- package/src/__tests__/ascii-class-detour-label-routing-487.test.ts +114 -0
- package/src/__tests__/ascii-class-diagram-compartments.test.ts +70 -0
- package/src/__tests__/ascii-class-label-box-collision.test.ts +60 -0
- package/src/__tests__/ascii-class-label-row-collision-531.test.ts +135 -0
- package/src/__tests__/ascii-class-label-territory-row-awareness.test.ts +57 -0
- package/src/__tests__/ascii-class-padding.test.ts +104 -0
- package/src/__tests__/ascii-class-parent-alignment-971.test.ts +139 -0
- package/src/__tests__/ascii-class-parent-alignment-972.test.ts +208 -0
- package/src/__tests__/ascii-class-reciprocal-relationships-448.test.ts +169 -0
- package/src/__tests__/ascii-combining-mark-width.test.ts +70 -0
- package/src/__tests__/ascii-coords-overlay.test.ts +64 -0
- package/src/__tests__/ascii-decision-lr-box-start.test.ts +106 -0
- package/src/__tests__/ascii-display-width-unit.test.ts +154 -0
- package/src/__tests__/ascii-draw-arrows-coverage.test.ts +224 -0
- package/src/__tests__/ascii-draw-arrows-single-point-path.test.ts +105 -0
- package/src/__tests__/ascii-edge-bundling-rank-violation-454.test.ts +72 -0
- package/src/__tests__/ascii-edge-ending-glyphs.test.ts +206 -0
- package/src/__tests__/ascii-edge-label-diagonal-fallback-418.test.ts +95 -0
- package/src/__tests__/ascii-edge-routing-fixes.test.ts +178 -0
- package/src/__tests__/ascii-edge-routing-single-point-path.test.ts +103 -0
- package/src/__tests__/ascii-edge-style-consistency-1067.test.ts +89 -0
- package/src/__tests__/ascii-edge-styles.test.ts +149 -0
- package/src/__tests__/ascii-emoji-cluster-width.test.ts +101 -0
- package/src/__tests__/ascii-er-box-occupancy.test.ts +251 -0
- package/src/__tests__/ascii-er-cardinality.test.ts +40 -0
- package/src/__tests__/ascii-er-corner-glyphs.test.ts +249 -0
- package/src/__tests__/ascii-er-jog-stray-line.test.ts +111 -0
- package/src/__tests__/ascii-er-label-padding.test.ts +118 -0
- package/src/__tests__/ascii-er-padding.test.ts +114 -0
- package/src/__tests__/ascii-er-relationship-label-corruption-350.test.ts +458 -0
- package/src/__tests__/ascii-er-relationship-overwrite.test.ts +325 -0
- package/src/__tests__/ascii-er-stray-connectors.test.ts +344 -0
- package/src/__tests__/ascii-er-unrelated-stem-separation-411.test.ts +98 -0
- package/src/__tests__/ascii-er-vertical-one-marker.test.ts +110 -0
- package/src/__tests__/ascii-label-line-terminal-fallback.test.ts +168 -0
- package/src/__tests__/ascii-lane-search.test.ts +207 -0
- package/src/__tests__/ascii-multibox-cjk-width.test.ts +184 -0
- package/src/__tests__/ascii-multiline.test.ts +288 -0
- package/src/__tests__/ascii-padding-edge-cases.test.ts +154 -0
- package/src/__tests__/ascii-pathfinder-route-edge.test.ts +184 -0
- package/src/__tests__/ascii-sequence-alt-else-label.test.ts +236 -0
- package/src/__tests__/ascii-sequence-block-wall-clearance.test.ts +217 -0
- package/src/__tests__/ascii-sequence-box-group.test.ts +159 -0
- package/src/__tests__/ascii-sequence-cjk-width.test.ts +235 -0
- package/src/__tests__/ascii-sequence-create-destroy.test.ts +114 -0
- package/src/__tests__/ascii-sequence-form-invariants.test.ts +432 -0
- package/src/__tests__/ascii-sequence-mermaid-parity.test.ts +219 -0
- package/src/__tests__/ascii-sequence-notes.test.ts +61 -0
- package/src/__tests__/ascii-sequence-padding.test.ts +119 -0
- package/src/__tests__/ascii-sequence-self-arrow.test.ts +206 -0
- package/src/__tests__/ascii-shape-diamond.test.ts +38 -0
- package/src/__tests__/ascii-shape-rectangle.test.ts +258 -0
- package/src/__tests__/ascii-shape-rounded.test.ts +36 -0
- package/src/__tests__/ascii-shapes-circle.test.ts +41 -0
- package/src/__tests__/ascii-shapes-hexagon.test.ts +42 -0
- package/src/__tests__/ascii-shapes-special.test.ts +344 -0
- package/src/__tests__/ascii-shapes-stadium.test.ts +217 -0
- package/src/__tests__/ascii-shapes-state.test.ts +224 -0
- package/src/__tests__/ascii-state-bidirectional-label-swap-530.test.ts +130 -0
- package/src/__tests__/ascii-subgraph-direction-honored-445.test.ts +90 -0
- package/src/__tests__/ascii-subgraph-label-border-clip.test.ts +152 -0
- package/src/__tests__/ascii-subgraph-title-padding.test.ts +77 -0
- package/src/__tests__/ascii-territory-unit.test.ts +219 -0
- package/src/__tests__/ascii-validate.test.ts +220 -0
- package/src/__tests__/ascii.test.ts +325 -0
- package/src/__tests__/class-arrow-directions.test.ts +505 -0
- package/src/__tests__/draw-lines.test.ts +93 -0
- package/src/__tests__/edge-cell-styles.test.ts +278 -0
- package/src/__tests__/grid-occupancy.test.ts +240 -0
- package/src/__tests__/helpers/ascii-form.ts +142 -0
- package/src/__tests__/helpers/terminal-display-width.ts +74 -0
- package/src/__tests__/pathfinder.test.ts +239 -0
- package/src/__tests__/testdata/ascii/ampersand_lhs.txt +18 -0
- package/src/__tests__/testdata/ascii/ampersand_lhs_and_rhs.txt +18 -0
- package/src/__tests__/testdata/ascii/ampersand_rhs.txt +18 -0
- package/src/__tests__/testdata/ascii/ampersand_td_fanin.txt +18 -0
- package/src/__tests__/testdata/ascii/ampersand_td_fanout.txt +18 -0
- package/src/__tests__/testdata/ascii/ampersand_without_edge.txt +18 -0
- package/src/__tests__/testdata/ascii/back_reference_from_child.txt +10 -0
- package/src/__tests__/testdata/ascii/backlink_from_bottom.txt +22 -0
- package/src/__tests__/testdata/ascii/backlink_from_top.txt +22 -0
- package/src/__tests__/testdata/ascii/backlink_with_short_y_padding.txt +20 -0
- package/src/__tests__/testdata/ascii/cls_all_relationships.txt +19 -0
- package/src/__tests__/testdata/ascii/cls_annotation.txt +29 -0
- package/src/__tests__/testdata/ascii/cls_association.txt +14 -0
- package/src/__tests__/testdata/ascii/cls_basic.txt +15 -0
- package/src/__tests__/testdata/ascii/cls_dependency.txt +14 -0
- package/src/__tests__/testdata/ascii/cls_inheritance.txt +20 -0
- package/src/__tests__/testdata/ascii/cls_methods.txt +21 -0
- package/src/__tests__/testdata/ascii/comments.txt +23 -0
- package/src/__tests__/testdata/ascii/custom_padding.txt +10 -0
- package/src/__tests__/testdata/ascii/duplicate_labels.txt +19 -0
- package/src/__tests__/testdata/ascii/er_attributes.txt +21 -0
- package/src/__tests__/testdata/ascii/er_basic.txt +8 -0
- package/src/__tests__/testdata/ascii/er_identifying.txt +18 -0
- package/src/__tests__/testdata/ascii/flowchart_tb_simple.txt +29 -0
- package/src/__tests__/testdata/ascii/graph_bt_direction.txt +28 -0
- package/src/__tests__/testdata/ascii/graph_tb_direction.txt +26 -0
- package/src/__tests__/testdata/ascii/nested_subgraphs_with_labels.txt +36 -0
- package/src/__tests__/testdata/ascii/preserve_order_of_definition.txt +23 -0
- package/src/__tests__/testdata/ascii/self_reference.txt +10 -0
- package/src/__tests__/testdata/ascii/self_reference_with_edge.txt +10 -0
- package/src/__tests__/testdata/ascii/seq_basic.txt +17 -0
- package/src/__tests__/testdata/ascii/seq_multiple_messages.txt +25 -0
- package/src/__tests__/testdata/ascii/seq_self_message.txt +18 -0
- package/src/__tests__/testdata/ascii/single_node.txt +8 -0
- package/src/__tests__/testdata/ascii/single_node_longer_name.txt +8 -0
- package/src/__tests__/testdata/ascii/subgraph_complex_mixed.txt +38 -0
- package/src/__tests__/testdata/ascii/subgraph_complex_nested.txt +49 -0
- package/src/__tests__/testdata/ascii/subgraph_direction_override.txt +47 -0
- package/src/__tests__/testdata/ascii/subgraph_empty.txt +10 -0
- package/src/__tests__/testdata/ascii/subgraph_mixed_nodes.txt +20 -0
- package/src/__tests__/testdata/ascii/subgraph_mixed_nodes_td.txt +48 -0
- package/src/__tests__/testdata/ascii/subgraph_multiple_edges.txt +32 -0
- package/src/__tests__/testdata/ascii/subgraph_multiple_nodes.txt +16 -0
- package/src/__tests__/testdata/ascii/subgraph_nested.txt +24 -0
- package/src/__tests__/testdata/ascii/subgraph_nested_with_external.txt +30 -0
- package/src/__tests__/testdata/ascii/subgraph_node_outside_lr.txt +17 -0
- package/src/__tests__/testdata/ascii/subgraph_single_node.txt +16 -0
- package/src/__tests__/testdata/ascii/subgraph_td_direction.txt +26 -0
- package/src/__tests__/testdata/ascii/subgraph_td_multiple.txt +44 -0
- package/src/__tests__/testdata/ascii/subgraph_td_multiple_paddingy.txt +42 -0
- package/src/__tests__/testdata/ascii/subgraph_three_levels_nested.txt +32 -0
- package/src/__tests__/testdata/ascii/subgraph_three_separate.txt +24 -0
- package/src/__tests__/testdata/ascii/subgraph_two_separate.txt +20 -0
- package/src/__tests__/testdata/ascii/subgraph_with_labels.txt +20 -0
- package/src/__tests__/testdata/ascii/three_nodes.txt +9 -0
- package/src/__tests__/testdata/ascii/three_nodes_single_line.txt +8 -0
- package/src/__tests__/testdata/ascii/two_layer_single_graph.txt +19 -0
- package/src/__tests__/testdata/ascii/two_layer_single_graph_longer_names.txt +19 -0
- package/src/__tests__/testdata/ascii/two_nodes_linked.txt +8 -0
- package/src/__tests__/testdata/ascii/two_nodes_longer_names.txt +8 -0
- package/src/__tests__/testdata/ascii/two_root_nodes.txt +19 -0
- package/src/__tests__/testdata/ascii/two_root_nodes_longer_names.txt +19 -0
- package/src/__tests__/testdata/ascii/two_single_root_nodes.txt +19 -0
- package/src/__tests__/testdata/unicode/ampersand_lhs.txt +18 -0
- package/src/__tests__/testdata/unicode/ampersand_lhs_and_rhs.txt +18 -0
- package/src/__tests__/testdata/unicode/ampersand_rhs.txt +18 -0
- package/src/__tests__/testdata/unicode/ampersand_without_edge.txt +18 -0
- package/src/__tests__/testdata/unicode/back_reference_from_child.txt +10 -0
- package/src/__tests__/testdata/unicode/backlink_from_bottom.txt +22 -0
- package/src/__tests__/testdata/unicode/backlink_from_top.txt +22 -0
- package/src/__tests__/testdata/unicode/cls_all_relationships.txt +19 -0
- package/src/__tests__/testdata/unicode/cls_annotation.txt +29 -0
- package/src/__tests__/testdata/unicode/cls_association.txt +14 -0
- package/src/__tests__/testdata/unicode/cls_basic.txt +15 -0
- package/src/__tests__/testdata/unicode/cls_dependency.txt +14 -0
- package/src/__tests__/testdata/unicode/cls_inheritance.txt +20 -0
- package/src/__tests__/testdata/unicode/cls_methods.txt +21 -0
- package/src/__tests__/testdata/unicode/comments.txt +23 -0
- package/src/__tests__/testdata/unicode/duplicate_labels.txt +19 -0
- package/src/__tests__/testdata/unicode/er_attributes.txt +21 -0
- package/src/__tests__/testdata/unicode/er_basic.txt +8 -0
- package/src/__tests__/testdata/unicode/er_identifying.txt +18 -0
- package/src/__tests__/testdata/unicode/graph_bt_direction.txt +28 -0
- package/src/__tests__/testdata/unicode/preserve_order_of_definition.txt +23 -0
- package/src/__tests__/testdata/unicode/self_reference.txt +10 -0
- package/src/__tests__/testdata/unicode/self_reference_with_edge.txt +10 -0
- package/src/__tests__/testdata/unicode/seq_basic.txt +17 -0
- package/src/__tests__/testdata/unicode/seq_multiple_messages.txt +25 -0
- package/src/__tests__/testdata/unicode/seq_self_message.txt +18 -0
- package/src/__tests__/testdata/unicode/single_node.txt +8 -0
- package/src/__tests__/testdata/unicode/single_node_longer_name.txt +8 -0
- package/src/__tests__/testdata/unicode/three_nodes.txt +9 -0
- package/src/__tests__/testdata/unicode/three_nodes_single_line.txt +8 -0
- package/src/__tests__/testdata/unicode/two_layer_single_graph.txt +19 -0
- package/src/__tests__/testdata/unicode/two_layer_single_graph_longer_names.txt +19 -0
- package/src/__tests__/testdata/unicode/two_nodes_linked.txt +8 -0
- package/src/__tests__/testdata/unicode/two_nodes_longer_names.txt +8 -0
- package/src/__tests__/testdata/unicode/two_root_nodes.txt +19 -0
- package/src/__tests__/testdata/unicode/two_root_nodes_longer_names.txt +19 -0
- package/src/__tests__/testdata/unicode/two_single_root_nodes.txt +19 -0
- package/src/__tests__/xychart-ascii.test.ts +376 -0
- package/src/ansi.ts +490 -0
- package/src/canvas.ts +757 -0
- package/src/class-diagram.ts +2001 -0
- package/src/converter.ts +446 -0
- package/src/coords.ts +58 -0
- package/src/display-width.ts +151 -0
- package/src/draw-arrows.ts +593 -0
- package/src/draw-boxes.ts +267 -0
- package/src/draw-bundles.ts +611 -0
- package/src/draw-lines.ts +174 -0
- package/src/draw-subgraphs.ts +108 -0
- package/src/draw.ts +350 -0
- package/src/edge-bundling.ts +435 -0
- package/src/edge-cell-styles.ts +209 -0
- package/src/edge-routing.ts +1070 -0
- package/src/er-diagram.ts +1488 -0
- package/src/flowchart.ts +94 -0
- package/src/grid-occupancy.ts +234 -0
- package/src/grid.ts +1309 -0
- package/src/hyperlinks.ts +248 -0
- package/src/index.ts +163 -0
- package/src/lane-search.ts +68 -0
- package/src/multiline-utils.ts +82 -0
- package/src/pathfinder.ts +448 -0
- package/src/registry.ts +88 -0
- package/src/sequence.ts +1318 -0
- package/src/shapes/circle.ts +31 -0
- package/src/shapes/corners.ts +273 -0
- package/src/shapes/diamond.ts +31 -0
- package/src/shapes/hexagon.ts +35 -0
- package/src/shapes/index.ts +123 -0
- package/src/shapes/rectangle.ts +199 -0
- package/src/shapes/rounded.ts +31 -0
- package/src/shapes/special.ts +360 -0
- package/src/shapes/stadium.ts +122 -0
- package/src/shapes/state.ts +204 -0
- package/src/shapes/types.ts +78 -0
- package/src/territory.ts +136 -0
- package/src/types.ts +454 -0
- package/src/validate.ts +189 -0
- package/src/xychart.ts +1085 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Golden-file tests for the ASCII/Unicode renderer.
|
|
3
|
+
*
|
|
4
|
+
* Ported from AlexanderGrooff/mermaid-ascii cmd/graph_test.go.
|
|
5
|
+
* Each .txt file contains mermaid input above a `---` separator
|
|
6
|
+
* and the expected ASCII/Unicode output below it.
|
|
7
|
+
*
|
|
8
|
+
* Test data: 44 ASCII files + 22 Unicode files = 66 total.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect } from 'vitest'
|
|
11
|
+
import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer'
|
|
12
|
+
import { hasDiagonalLines, DIAGONAL_CHARS } from '../validate.ts'
|
|
13
|
+
import { readdirSync, readFileSync } from 'node:fs'
|
|
14
|
+
import { join, dirname } from 'node:path'
|
|
15
|
+
import { fileURLToPath } from 'node:url'
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Test case parser — matches Go's testutil.ReadTestCase format
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
interface TestCase {
|
|
22
|
+
mermaid: string
|
|
23
|
+
expected: string
|
|
24
|
+
paddingX: number
|
|
25
|
+
paddingY: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parse a golden test file into its components.
|
|
30
|
+
* Format:
|
|
31
|
+
* [paddingX=N] (optional)
|
|
32
|
+
* [paddingY=N] (optional)
|
|
33
|
+
* <mermaid code>
|
|
34
|
+
* ---
|
|
35
|
+
* <expected output>
|
|
36
|
+
*/
|
|
37
|
+
function parseTestCase(content: string): TestCase {
|
|
38
|
+
const tc: TestCase = { mermaid: '', expected: '', paddingX: 5, paddingY: 5 }
|
|
39
|
+
const lines = content.split('\n')
|
|
40
|
+
const paddingRegex = /^(?:padding([xy]))\s*=\s*(\d+)\s*$/i
|
|
41
|
+
|
|
42
|
+
let inMermaid = true
|
|
43
|
+
let mermaidStarted = false
|
|
44
|
+
const mermaidLines: string[] = []
|
|
45
|
+
const expectedLines: string[] = []
|
|
46
|
+
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
if (line === '---') {
|
|
49
|
+
inMermaid = false
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (inMermaid) {
|
|
54
|
+
const trimmed = line.trim()
|
|
55
|
+
|
|
56
|
+
// Before mermaid code starts, parse padding directives and skip blanks
|
|
57
|
+
if (!mermaidStarted) {
|
|
58
|
+
if (trimmed === '') continue
|
|
59
|
+
const match = trimmed.match(paddingRegex)
|
|
60
|
+
if (match) {
|
|
61
|
+
const value = parseInt(match[2]!, 10)
|
|
62
|
+
if (match[1]!.toLowerCase() === 'x') {
|
|
63
|
+
tc.paddingX = value
|
|
64
|
+
} else {
|
|
65
|
+
tc.paddingY = value
|
|
66
|
+
}
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
mermaidStarted = true
|
|
72
|
+
mermaidLines.push(line)
|
|
73
|
+
} else {
|
|
74
|
+
expectedLines.push(line)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
tc.mermaid = mermaidLines.join('\n') + '\n'
|
|
79
|
+
|
|
80
|
+
// Strip final trailing newline (matches Go's strings.TrimSuffix(expected, "\n"))
|
|
81
|
+
let expected = expectedLines.join('\n')
|
|
82
|
+
if (expected.endsWith('\n')) {
|
|
83
|
+
expected = expected.slice(0, -1)
|
|
84
|
+
}
|
|
85
|
+
tc.expected = expected
|
|
86
|
+
|
|
87
|
+
return tc
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Whitespace normalization — matches Go's testutil.NormalizeWhitespace
|
|
92
|
+
// ============================================================================
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Normalize whitespace for comparison:
|
|
96
|
+
* - Trim trailing spaces from each line
|
|
97
|
+
* - Remove leading/trailing blank lines
|
|
98
|
+
*/
|
|
99
|
+
function normalizeWhitespace(s: string): string {
|
|
100
|
+
const lines = s.split('\n')
|
|
101
|
+
const normalized = lines.map((l) => l.trimEnd())
|
|
102
|
+
|
|
103
|
+
// Remove leading blank lines
|
|
104
|
+
while (normalized.length > 0 && normalized[0] === '') {
|
|
105
|
+
normalized.shift()
|
|
106
|
+
}
|
|
107
|
+
// Remove trailing blank lines
|
|
108
|
+
while (normalized.length > 0 && normalized[normalized.length - 1] === '') {
|
|
109
|
+
normalized.pop()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return normalized.join('\n')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Replace spaces with middle dots for clearer diff output. */
|
|
116
|
+
function visualizeWhitespace(s: string): string {
|
|
117
|
+
return s.replaceAll(' ', '·')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ============================================================================
|
|
121
|
+
// Test runner — dynamically loads all golden files from testdata directories
|
|
122
|
+
// ============================================================================
|
|
123
|
+
|
|
124
|
+
function runGoldenTests(dir: string, useAscii: boolean): void {
|
|
125
|
+
const files = readdirSync(dir)
|
|
126
|
+
.filter((f) => f.endsWith('.txt'))
|
|
127
|
+
.sort()
|
|
128
|
+
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
const testName = file.replace('.txt', '')
|
|
131
|
+
|
|
132
|
+
it(testName, () => {
|
|
133
|
+
const content = readFileSync(join(dir, file), 'utf-8')
|
|
134
|
+
const tc = parseTestCase(content)
|
|
135
|
+
|
|
136
|
+
const actual = renderMermaidASCII(tc.mermaid, {
|
|
137
|
+
useAscii,
|
|
138
|
+
paddingX: tc.paddingX,
|
|
139
|
+
paddingY: tc.paddingY,
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const normalizedExpected = normalizeWhitespace(tc.expected)
|
|
143
|
+
const normalizedActual = normalizeWhitespace(actual)
|
|
144
|
+
|
|
145
|
+
if (normalizedExpected !== normalizedActual) {
|
|
146
|
+
const expectedVis = visualizeWhitespace(normalizedExpected)
|
|
147
|
+
const actualVis = visualizeWhitespace(normalizedActual)
|
|
148
|
+
expect(actualVis).toBe(expectedVis)
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// Test suites
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
const testdataDir = join(dirname(fileURLToPath(import.meta.url)), 'testdata')
|
|
159
|
+
|
|
160
|
+
describe('ASCII rendering', () => {
|
|
161
|
+
runGoldenTests(join(testdataDir, 'ascii'), true)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
describe('Unicode rendering', () => {
|
|
165
|
+
runGoldenTests(join(testdataDir, 'unicode'), false)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// Config behavior tests — ported from Go's TestGraphUseAsciiConfig
|
|
170
|
+
// ============================================================================
|
|
171
|
+
|
|
172
|
+
describe('Config behavior', () => {
|
|
173
|
+
const mermaidInput = 'graph LR\nA --> B'
|
|
174
|
+
|
|
175
|
+
it('ASCII and Unicode outputs should differ', () => {
|
|
176
|
+
const asciiOutput = renderMermaidASCII(mermaidInput, { useAscii: true })
|
|
177
|
+
const unicodeOutput = renderMermaidASCII(mermaidInput, { useAscii: false })
|
|
178
|
+
expect(asciiOutput).not.toBe(unicodeOutput)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('ASCII output should not contain Unicode box-drawing characters', () => {
|
|
182
|
+
const output = renderMermaidASCII(mermaidInput, { useAscii: true })
|
|
183
|
+
expect(output).not.toContain('┌')
|
|
184
|
+
expect(output).not.toContain('─')
|
|
185
|
+
expect(output).not.toContain('│')
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('Unicode output should contain Unicode box-drawing characters', () => {
|
|
189
|
+
const output = renderMermaidASCII(mermaidInput, { useAscii: false })
|
|
190
|
+
const hasUnicode =
|
|
191
|
+
output.includes('┌') || output.includes('─') || output.includes('│')
|
|
192
|
+
expect(hasUnicode).toBe(true)
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// Diagonal validation — ensures all edges use orthogonal Manhattan routing
|
|
198
|
+
// ============================================================================
|
|
199
|
+
|
|
200
|
+
describe('Diagonal validation', () => {
|
|
201
|
+
const asciiDir = join(testdataDir, 'ascii')
|
|
202
|
+
const unicodeDir = join(testdataDir, 'unicode')
|
|
203
|
+
|
|
204
|
+
it('ASCII output should never contain diagonal characters', () => {
|
|
205
|
+
// Test all ASCII golden files
|
|
206
|
+
const files = readdirSync(asciiDir).filter((f) => f.endsWith('.txt'))
|
|
207
|
+
for (const file of files) {
|
|
208
|
+
const content = readFileSync(join(asciiDir, file), 'utf-8')
|
|
209
|
+
const { mermaid, paddingX, paddingY } = parseTestCase(content)
|
|
210
|
+
const output = renderMermaidASCII(mermaid, {
|
|
211
|
+
useAscii: true,
|
|
212
|
+
boxBorderPadding: paddingX,
|
|
213
|
+
paddingY: paddingY,
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
// Check for diagonal characters
|
|
217
|
+
for (const char of DIAGONAL_CHARS.ascii) {
|
|
218
|
+
expect(output).not.toContain(char)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('Unicode output should never contain diagonal characters', () => {
|
|
224
|
+
// Test all Unicode golden files
|
|
225
|
+
const files = readdirSync(unicodeDir).filter((f) => f.endsWith('.txt'))
|
|
226
|
+
for (const file of files) {
|
|
227
|
+
const content = readFileSync(join(unicodeDir, file), 'utf-8')
|
|
228
|
+
const { mermaid, paddingX, paddingY } = parseTestCase(content)
|
|
229
|
+
const output = renderMermaidASCII(mermaid, {
|
|
230
|
+
useAscii: false,
|
|
231
|
+
boxBorderPadding: paddingX,
|
|
232
|
+
paddingY: paddingY,
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// Check for diagonal characters
|
|
236
|
+
for (const char of DIAGONAL_CHARS.unicode) {
|
|
237
|
+
expect(output).not.toContain(char)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('hasDiagonalLines utility correctly detects diagonal characters', () => {
|
|
243
|
+
// Should detect ASCII diagonals
|
|
244
|
+
expect(hasDiagonalLines('A / B')).toBe(true)
|
|
245
|
+
expect(hasDiagonalLines('A \\ B')).toBe(true)
|
|
246
|
+
|
|
247
|
+
// Should detect Unicode diagonals
|
|
248
|
+
expect(hasDiagonalLines('A ╱ B')).toBe(true)
|
|
249
|
+
expect(hasDiagonalLines('A ╲ B')).toBe(true)
|
|
250
|
+
|
|
251
|
+
// Should not flag clean output
|
|
252
|
+
expect(hasDiagonalLines('┌───┐\n│ A │\n└───┘')).toBe(false)
|
|
253
|
+
expect(hasDiagonalLines('+---+\n| A |\n+---+')).toBe(false)
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
// ============================================================================
|
|
258
|
+
// Issue #65 regressions
|
|
259
|
+
// ============================================================================
|
|
260
|
+
|
|
261
|
+
describe('Issue #65 – --o/--x edges no longer drop the target node', () => {
|
|
262
|
+
it('renders both nodes and a connector for A --o B', () => {
|
|
263
|
+
const output = renderMermaidASCII('graph LR\n A --o B')
|
|
264
|
+
expect(output).toContain('A')
|
|
265
|
+
expect(output).toContain('B')
|
|
266
|
+
// The connecting line/arrow should be present, not just two bare boxes.
|
|
267
|
+
expect(output).toMatch(/[─▼►]/)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('renders both nodes and a connector for A --x B', () => {
|
|
271
|
+
const output = renderMermaidASCII('graph LR\n A --x B')
|
|
272
|
+
expect(output).toContain('A')
|
|
273
|
+
expect(output).toContain('B')
|
|
274
|
+
expect(output).toMatch(/[─▼►]/)
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
describe('Issue #65 – edges to a subgraph id no longer create phantom nodes', () => {
|
|
279
|
+
const mermaid = `flowchart TD
|
|
280
|
+
subgraph ONE["First"]
|
|
281
|
+
A1["a"] --> A2["b"]
|
|
282
|
+
end
|
|
283
|
+
subgraph TWO["Second"]
|
|
284
|
+
B1["c"] --> B2["d"]
|
|
285
|
+
end
|
|
286
|
+
ONE --> TWO`
|
|
287
|
+
|
|
288
|
+
it('does not render a standalone "ONE" or "TWO" box disconnected from the subgraphs', () => {
|
|
289
|
+
const output = renderMermaidASCII(mermaid)
|
|
290
|
+
// The subgraph titles ("First"/"Second") should render; the raw
|
|
291
|
+
// subgraph ids should not appear as their own node labels.
|
|
292
|
+
expect(output).toContain('First')
|
|
293
|
+
expect(output).toContain('Second')
|
|
294
|
+
expect(output).not.toMatch(/│\s*ONE\s*│/)
|
|
295
|
+
expect(output).not.toMatch(/│\s*TWO\s*│/)
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('draws a real connector between the two subgraph frames', () => {
|
|
299
|
+
const output = renderMermaidASCII(mermaid)
|
|
300
|
+
// A vertical connector should cross between the two subgraph boxes.
|
|
301
|
+
expect(output).toMatch(/[│▼]/)
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
describe('Issue #65 – inline <i>/<b>/<em>/<strong> tags are stripped', () => {
|
|
306
|
+
it('strips inline formatting tags from a node label', () => {
|
|
307
|
+
const output = renderMermaidASCII(
|
|
308
|
+
'flowchart TD\n A["italic <i>word</i> and bold <b>x</b>"]',
|
|
309
|
+
)
|
|
310
|
+
expect(output).toContain('italic word and bold x')
|
|
311
|
+
expect(output).not.toContain('<i>')
|
|
312
|
+
expect(output).not.toContain('</i>')
|
|
313
|
+
expect(output).not.toContain('<b>')
|
|
314
|
+
expect(output).not.toContain('</b>')
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
it('strips <em>/<strong> as well', () => {
|
|
318
|
+
const output = renderMermaidASCII(
|
|
319
|
+
'flowchart TD\n A["<em>emphasis</em> and <strong>strong</strong>"]',
|
|
320
|
+
)
|
|
321
|
+
expect(output).toContain('emphasis and strong')
|
|
322
|
+
expect(output).not.toContain('<em>')
|
|
323
|
+
expect(output).not.toContain('<strong>')
|
|
324
|
+
})
|
|
325
|
+
})
|