@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,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for xychart-beta ASCII rendering.
|
|
3
|
+
*
|
|
4
|
+
* Tests bar charts, line charts, mixed charts, horizontal orientation,
|
|
5
|
+
* multi-series support, staircase line routing, and edge cases.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect } from 'vitest'
|
|
8
|
+
import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer'
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Helper — render with no colors for easy string matching
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
function render(text: string, useAscii = false): string {
|
|
15
|
+
return renderMermaidASCII(text, { colorMode: 'none', useAscii })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Bar charts
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
describe('xychart ASCII – bar charts', () => {
|
|
23
|
+
it('renders a basic bar chart with block characters', () => {
|
|
24
|
+
const result = render(`xychart-beta
|
|
25
|
+
x-axis [A, B, C]
|
|
26
|
+
bar [10, 20, 30]`)
|
|
27
|
+
expect(result).toContain('█')
|
|
28
|
+
expect(result).toContain('A')
|
|
29
|
+
expect(result).toContain('B')
|
|
30
|
+
expect(result).toContain('C')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('renders bars with # in ASCII mode', () => {
|
|
34
|
+
const result = render(
|
|
35
|
+
`xychart-beta
|
|
36
|
+
x-axis [A, B, C]
|
|
37
|
+
bar [10, 20, 30]`,
|
|
38
|
+
true,
|
|
39
|
+
)
|
|
40
|
+
expect(result).toContain('#')
|
|
41
|
+
expect(result).not.toContain('█')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('renders a bar chart with title', () => {
|
|
45
|
+
const result = render(`xychart-beta
|
|
46
|
+
title "Sales Report"
|
|
47
|
+
x-axis [Q1, Q2, Q3, Q4]
|
|
48
|
+
bar [100, 200, 150, 250]`)
|
|
49
|
+
expect(result).toContain('Sales Report')
|
|
50
|
+
expect(result).toContain('Q1')
|
|
51
|
+
expect(result).toContain('Q4')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('renders y-axis tick labels', () => {
|
|
55
|
+
const result = render(`xychart-beta
|
|
56
|
+
x-axis [A, B]
|
|
57
|
+
y-axis 0 --> 100
|
|
58
|
+
bar [25, 75]`)
|
|
59
|
+
// Should have at least some tick values visible
|
|
60
|
+
expect(result).toContain('┤')
|
|
61
|
+
expect(result).toContain('┼')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('renders multi-series bars', () => {
|
|
65
|
+
const result = render(`xychart-beta
|
|
66
|
+
x-axis [A, B, C]
|
|
67
|
+
bar [10, 20, 30]
|
|
68
|
+
bar [15, 25, 35]`)
|
|
69
|
+
// Should contain legend for multi-series
|
|
70
|
+
expect(result).toContain('Bar 1')
|
|
71
|
+
expect(result).toContain('Bar 2')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('overlays (not groups side-by-side) multiple bar series in the same category', () => {
|
|
75
|
+
// Regression guard for issue #452: real mermaid.js draws every bar
|
|
76
|
+
// series in a category at the identical x-position/width (an
|
|
77
|
+
// overlaid/occluding layout, with a later series painted on top of an
|
|
78
|
+
// earlier one) rather than splitting them into adjacent grouped-bar
|
|
79
|
+
// columns. Each category's bar characters should therefore form a
|
|
80
|
+
// single contiguous run per row, never two runs separated by a gap.
|
|
81
|
+
const result = render(`xychart-beta
|
|
82
|
+
x-axis [A]
|
|
83
|
+
bar [10]
|
|
84
|
+
bar [20]`)
|
|
85
|
+
const lines = result
|
|
86
|
+
.split('\n')
|
|
87
|
+
// Exclude the legend row ("█ Bar 1 █ Bar 2") — its two swatches are
|
|
88
|
+
// expected to be separate runs; only plot rows matter here.
|
|
89
|
+
.filter((line) => !/[A-Za-z]/.test(line))
|
|
90
|
+
expect(lines.length).toBeGreaterThan(0)
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
const runs = line.match(/█+/g) ?? []
|
|
93
|
+
expect(runs.length).toBeLessThanOrEqual(1)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// Line charts
|
|
100
|
+
// ============================================================================
|
|
101
|
+
|
|
102
|
+
describe('xychart ASCII – line charts', () => {
|
|
103
|
+
it('renders a line chart with staircase routing', () => {
|
|
104
|
+
const result = render(`xychart-beta
|
|
105
|
+
x-axis [A, B, C, D]
|
|
106
|
+
line [10, 30, 20, 40]`)
|
|
107
|
+
// Should use box-drawing corners for staircase
|
|
108
|
+
expect(result).toContain('╭')
|
|
109
|
+
expect(result).toContain('─')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('does NOT use dot markers on line charts', () => {
|
|
113
|
+
const result = render(`xychart-beta
|
|
114
|
+
x-axis [A, B, C, D]
|
|
115
|
+
line [10, 30, 20, 40]`)
|
|
116
|
+
expect(result).not.toContain('●')
|
|
117
|
+
expect(result).not.toContain('*')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('uses vertical segments for row transitions', () => {
|
|
121
|
+
const result = render(`xychart-beta
|
|
122
|
+
x-axis [A, B, C]
|
|
123
|
+
line [10, 50, 20]`)
|
|
124
|
+
// Vertical segments connect rows
|
|
125
|
+
expect(result).toContain('│')
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('renders ascending line with ╯ and ╭ corners', () => {
|
|
129
|
+
const result = render(`xychart-beta
|
|
130
|
+
x-axis [A, B]
|
|
131
|
+
line [10, 50]`)
|
|
132
|
+
// Ascending: ╯ at bottom (left+up), ╭ at top (bottom+right)
|
|
133
|
+
expect(result).toContain('╯')
|
|
134
|
+
expect(result).toContain('╭')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('renders descending line with ╮ and ╰ corners', () => {
|
|
138
|
+
const result = render(`xychart-beta
|
|
139
|
+
x-axis [A, B]
|
|
140
|
+
line [50, 10]`)
|
|
141
|
+
// Descending: ╮ at top (left+down), ╰ at bottom (top+right)
|
|
142
|
+
expect(result).toContain('╮')
|
|
143
|
+
expect(result).toContain('╰')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('renders flat line with only ─', () => {
|
|
147
|
+
const result = render(`xychart-beta
|
|
148
|
+
x-axis [A, B, C]
|
|
149
|
+
line [50, 50, 50]`)
|
|
150
|
+
expect(result).toContain('─')
|
|
151
|
+
// No corners needed for flat lines
|
|
152
|
+
expect(result).not.toContain('╭')
|
|
153
|
+
expect(result).not.toContain('╮')
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('uses + for corners in ASCII mode', () => {
|
|
157
|
+
const result = render(
|
|
158
|
+
`xychart-beta
|
|
159
|
+
x-axis [A, B]
|
|
160
|
+
line [10, 50]`,
|
|
161
|
+
true,
|
|
162
|
+
)
|
|
163
|
+
expect(result).toContain('+')
|
|
164
|
+
expect(result).not.toContain('╭')
|
|
165
|
+
expect(result).not.toContain('╰')
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// ============================================================================
|
|
170
|
+
// Mixed bar + line
|
|
171
|
+
// ============================================================================
|
|
172
|
+
|
|
173
|
+
describe('xychart ASCII – mixed charts', () => {
|
|
174
|
+
it('renders bars and lines together', () => {
|
|
175
|
+
const result = render(`xychart-beta
|
|
176
|
+
x-axis [A, B, C, D]
|
|
177
|
+
bar [20, 40, 30, 50]
|
|
178
|
+
line [25, 35, 40, 45]`)
|
|
179
|
+
// Should have both bar blocks and line corners
|
|
180
|
+
expect(result).toContain('█')
|
|
181
|
+
expect(result).toContain('─')
|
|
182
|
+
// Legend for multi-series
|
|
183
|
+
expect(result).toContain('Bar 1')
|
|
184
|
+
expect(result).toContain('Line 1')
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// ============================================================================
|
|
189
|
+
// Horizontal orientation
|
|
190
|
+
// ============================================================================
|
|
191
|
+
|
|
192
|
+
describe('xychart ASCII – horizontal', () => {
|
|
193
|
+
it('renders horizontal bar chart', () => {
|
|
194
|
+
const result = render(`xychart-beta horizontal
|
|
195
|
+
x-axis [Python, JavaScript, Go]
|
|
196
|
+
bar [30, 25, 12]`)
|
|
197
|
+
// Category labels on left side
|
|
198
|
+
expect(result).toContain('Python')
|
|
199
|
+
expect(result).toContain('JavaScript')
|
|
200
|
+
expect(result).toContain('Go')
|
|
201
|
+
expect(result).toContain('█')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('renders horizontal line chart with staircase', () => {
|
|
205
|
+
const result = render(`xychart-beta horizontal
|
|
206
|
+
x-axis [A, B, C]
|
|
207
|
+
line [10, 30, 20]`)
|
|
208
|
+
// Should have horizontal staircase routing
|
|
209
|
+
expect(result).toContain('─')
|
|
210
|
+
expect(result).toContain('│')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('renders a y-axis title in horizontal orientation', () => {
|
|
214
|
+
const result = render(`xychart-beta horizontal
|
|
215
|
+
x-axis [A, B, C]
|
|
216
|
+
y-axis "Score" 0 --> 100
|
|
217
|
+
bar [10, 30, 20]`)
|
|
218
|
+
expect(result).toContain('Score')
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// ============================================================================
|
|
223
|
+
// Titles and axis labels
|
|
224
|
+
// ============================================================================
|
|
225
|
+
|
|
226
|
+
describe('xychart ASCII – titles and axes', () => {
|
|
227
|
+
it('renders chart title centered', () => {
|
|
228
|
+
const result = render(`xychart-beta
|
|
229
|
+
title "My Chart"
|
|
230
|
+
x-axis [A, B]
|
|
231
|
+
bar [10, 20]`)
|
|
232
|
+
const titleLine = result.split('\n').find((l) => l.includes('My Chart'))
|
|
233
|
+
expect(titleLine).toBeDefined()
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it('renders x-axis title', () => {
|
|
237
|
+
const result = render(`xychart-beta
|
|
238
|
+
x-axis "Category" [A, B, C]
|
|
239
|
+
bar [10, 20, 30]`)
|
|
240
|
+
expect(result).toContain('Category')
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('renders y-axis with explicit range', () => {
|
|
244
|
+
const result = render(`xychart-beta
|
|
245
|
+
x-axis [A, B]
|
|
246
|
+
y-axis "Score" 0 --> 100
|
|
247
|
+
bar [25, 75]`)
|
|
248
|
+
// Y-axis ticks should appear
|
|
249
|
+
expect(result).toContain('0')
|
|
250
|
+
expect(result).toContain('100')
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('renders a y-axis title vertically (one character per row) in vertical orientation', () => {
|
|
254
|
+
const result = render(`xychart-beta
|
|
255
|
+
x-axis [A, B]
|
|
256
|
+
y-axis "Score" 0 --> 100
|
|
257
|
+
bar [25, 75]`)
|
|
258
|
+
// The title should be spelled out top-to-bottom in the leftmost column,
|
|
259
|
+
// mirroring how mermaid.js rotates it 90deg along the left edge — not
|
|
260
|
+
// dropped entirely (issue #451).
|
|
261
|
+
const lines = result.split('\n')
|
|
262
|
+
const leftColumn = lines.map((l) => l[0] ?? ' ').join('')
|
|
263
|
+
expect(leftColumn).toContain('Score')
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('does not reserve a y-axis title column when no y-axis title is set', () => {
|
|
267
|
+
const result = render(`xychart-beta
|
|
268
|
+
x-axis [A, B]
|
|
269
|
+
y-axis 0 --> 100
|
|
270
|
+
bar [25, 75]`)
|
|
271
|
+
const lines = result.split('\n')
|
|
272
|
+
// Without a title, the leftmost column should be blank/whitespace on
|
|
273
|
+
// every line (no stray characters from title-column reservation).
|
|
274
|
+
for (const line of lines) {
|
|
275
|
+
expect(line[0] === undefined || line[0] === ' ').toBe(true)
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('renders without title when not specified', () => {
|
|
280
|
+
const result = render(`xychart-beta
|
|
281
|
+
x-axis [A, B]
|
|
282
|
+
bar [10, 20]`)
|
|
283
|
+
// First non-empty line should be chart content, not a title
|
|
284
|
+
const lines = result.split('\n').filter((l) => l.trim().length > 0)
|
|
285
|
+
expect(lines.length).toBeGreaterThan(0)
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
// ============================================================================
|
|
290
|
+
// Edge cases
|
|
291
|
+
// ============================================================================
|
|
292
|
+
|
|
293
|
+
describe('xychart ASCII – edge cases', () => {
|
|
294
|
+
it('handles single data point', () => {
|
|
295
|
+
const result = render(`xychart-beta
|
|
296
|
+
x-axis [Only]
|
|
297
|
+
bar [42]`)
|
|
298
|
+
expect(result).toContain('Only')
|
|
299
|
+
expect(result).toContain('█')
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it('handles two data points', () => {
|
|
303
|
+
const result = render(`xychart-beta
|
|
304
|
+
x-axis [A, B]
|
|
305
|
+
bar [10, 20]`)
|
|
306
|
+
expect(result).toContain('A')
|
|
307
|
+
expect(result).toContain('B')
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it('handles all zeros', () => {
|
|
311
|
+
const result = render(`xychart-beta
|
|
312
|
+
x-axis [A, B, C]
|
|
313
|
+
bar [0, 0, 0]`)
|
|
314
|
+
expect(result).toContain('A')
|
|
315
|
+
// Should still render axes
|
|
316
|
+
expect(result).toContain('┼')
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
it('handles large values', () => {
|
|
320
|
+
const result = render(`xychart-beta
|
|
321
|
+
x-axis [A, B]
|
|
322
|
+
bar [1000000, 2000000]`)
|
|
323
|
+
expect(result).toContain('A')
|
|
324
|
+
expect(result).toContain('B')
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
it('renders line chart with single data point', () => {
|
|
328
|
+
const result = render(`xychart-beta
|
|
329
|
+
x-axis [A]
|
|
330
|
+
line [50]`)
|
|
331
|
+
expect(result).toContain('A')
|
|
332
|
+
expect(result).toContain('─')
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
// ============================================================================
|
|
337
|
+
// Axis structure
|
|
338
|
+
// ============================================================================
|
|
339
|
+
|
|
340
|
+
describe('xychart ASCII – axis structure', () => {
|
|
341
|
+
it('has y-axis ticks (┤) at value positions', () => {
|
|
342
|
+
const result = render(`xychart-beta
|
|
343
|
+
x-axis [A, B, C]
|
|
344
|
+
y-axis 0 --> 100
|
|
345
|
+
bar [25, 50, 75]`)
|
|
346
|
+
expect(result).toContain('┤')
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
it('has x-axis ticks (┬) at category positions', () => {
|
|
350
|
+
const result = render(`xychart-beta
|
|
351
|
+
x-axis [A, B, C]
|
|
352
|
+
bar [25, 50, 75]`)
|
|
353
|
+
expect(result).toContain('┬')
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
it('has origin marker (┼)', () => {
|
|
357
|
+
const result = render(`xychart-beta
|
|
358
|
+
x-axis [A, B]
|
|
359
|
+
bar [10, 20]`)
|
|
360
|
+
expect(result).toContain('┼')
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
it('uses + for axis characters in ASCII mode', () => {
|
|
364
|
+
const result = render(
|
|
365
|
+
`xychart-beta
|
|
366
|
+
x-axis [A, B, C]
|
|
367
|
+
y-axis 0 --> 100
|
|
368
|
+
bar [25, 50, 75]`,
|
|
369
|
+
true,
|
|
370
|
+
)
|
|
371
|
+
expect(result).toContain('+')
|
|
372
|
+
expect(result).not.toContain('┤')
|
|
373
|
+
expect(result).not.toContain('┬')
|
|
374
|
+
expect(result).not.toContain('┼')
|
|
375
|
+
})
|
|
376
|
+
})
|