@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,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive tests for class diagram arrow directions.
|
|
3
|
+
*
|
|
4
|
+
* Ensures all relationship types have correctly oriented arrows:
|
|
5
|
+
* - Inheritance/Realization: hollow triangles point toward parent/interface
|
|
6
|
+
* - Association/Dependency: filled arrows point from source to target
|
|
7
|
+
* - Composition/Aggregation: diamonds are omnidirectional
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, test, expect } from 'vitest'
|
|
11
|
+
import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer'
|
|
12
|
+
|
|
13
|
+
describe('Class Diagram Arrow Directions', () => {
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// INHERITANCE (<|--)
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
describe('Inheritance (<|--)', () => {
|
|
19
|
+
test('parent above child - triangle points UP toward parent', () => {
|
|
20
|
+
const diagram = `classDiagram
|
|
21
|
+
Animal <|-- Dog`
|
|
22
|
+
const result = renderMermaidASCII(diagram)
|
|
23
|
+
|
|
24
|
+
// Should contain upward triangle
|
|
25
|
+
expect(result).toContain('△')
|
|
26
|
+
expect(result).not.toContain('▽')
|
|
27
|
+
|
|
28
|
+
// Parent should be above child
|
|
29
|
+
const lines = result.split('\n')
|
|
30
|
+
const animalLine = lines.findIndex((l) => l.includes('Animal'))
|
|
31
|
+
const dogLine = lines.findIndex((l) => l.includes('Dog'))
|
|
32
|
+
expect(animalLine).toBeLessThan(dogLine)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('multiple inheritance creates separate arrows', () => {
|
|
36
|
+
const diagram = `classDiagram
|
|
37
|
+
Animal <|-- Dog
|
|
38
|
+
Animal <|-- Cat
|
|
39
|
+
Dog <|-- Puppy`
|
|
40
|
+
const result = renderMermaidASCII(diagram)
|
|
41
|
+
|
|
42
|
+
// Animal should be at top, then Dog/Cat, then Puppy
|
|
43
|
+
const lines = result.split('\n')
|
|
44
|
+
const animalLine = lines.findIndex((l) => l.includes('Animal'))
|
|
45
|
+
const dogLine = lines.findIndex((l) => l.includes('Dog'))
|
|
46
|
+
const catLine = lines.findIndex((l) => l.includes('Cat'))
|
|
47
|
+
const puppyLine = lines.findIndex((l) => l.includes('Puppy'))
|
|
48
|
+
|
|
49
|
+
expect(animalLine).toBeLessThan(dogLine)
|
|
50
|
+
expect(animalLine).toBeLessThan(catLine)
|
|
51
|
+
expect(dogLine).toBeLessThan(puppyLine)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('multi-level inheritance - all triangles point UP', () => {
|
|
55
|
+
const diagram = `classDiagram
|
|
56
|
+
Animal <|-- Mammal
|
|
57
|
+
Mammal <|-- Dog`
|
|
58
|
+
const result = renderMermaidASCII(diagram)
|
|
59
|
+
|
|
60
|
+
// Verify ordering: Animal > Mammal > Dog (top to bottom)
|
|
61
|
+
const lines = result.split('\n')
|
|
62
|
+
const animalLine = lines.findIndex((l) => l.includes('Animal'))
|
|
63
|
+
const mammalLine = lines.findIndex((l) => l.includes('Mammal'))
|
|
64
|
+
const dogLine = lines.findIndex((l) => l.includes('Dog'))
|
|
65
|
+
|
|
66
|
+
expect(animalLine).toBeLessThan(mammalLine)
|
|
67
|
+
expect(mammalLine).toBeLessThan(dogLine)
|
|
68
|
+
|
|
69
|
+
// All triangles should point up
|
|
70
|
+
expect(result.match(/△/g)?.length).toBe(2)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('multiple inheritance from same parent', () => {
|
|
74
|
+
const diagram = `classDiagram
|
|
75
|
+
Animal <|-- Dog
|
|
76
|
+
Animal <|-- Cat`
|
|
77
|
+
const result = renderMermaidASCII(diagram)
|
|
78
|
+
|
|
79
|
+
// Animal should be above both children
|
|
80
|
+
const lines = result.split('\n')
|
|
81
|
+
const animalLine = lines.findIndex((l) => l.includes('Animal'))
|
|
82
|
+
const dogLine = lines.findIndex((l) => l.includes('Dog'))
|
|
83
|
+
const catLine = lines.findIndex((l) => l.includes('Cat'))
|
|
84
|
+
|
|
85
|
+
expect(animalLine).toBeLessThan(dogLine)
|
|
86
|
+
expect(animalLine).toBeLessThan(catLine)
|
|
87
|
+
|
|
88
|
+
// Should have at least one triangle pointing up (may merge visually)
|
|
89
|
+
expect(result).toContain('△')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('ASCII mode uses ^ for upward triangle', () => {
|
|
93
|
+
const diagram = `classDiagram
|
|
94
|
+
Animal <|-- Dog`
|
|
95
|
+
const result = renderMermaidASCII(diagram, { useAscii: true })
|
|
96
|
+
|
|
97
|
+
expect(result).toContain('^')
|
|
98
|
+
expect(result).not.toContain('v')
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// ASSOCIATION (-->)
|
|
104
|
+
// ============================================================================
|
|
105
|
+
|
|
106
|
+
describe('Association (-->)', () => {
|
|
107
|
+
test('source above target - arrow points DOWN', () => {
|
|
108
|
+
const diagram = `classDiagram
|
|
109
|
+
Person --> Address`
|
|
110
|
+
const result = renderMermaidASCII(diagram)
|
|
111
|
+
|
|
112
|
+
// Should contain downward arrow
|
|
113
|
+
expect(result).toContain('▼')
|
|
114
|
+
expect(result).not.toContain('▲')
|
|
115
|
+
|
|
116
|
+
// Person should be above Address
|
|
117
|
+
const lines = result.split('\n')
|
|
118
|
+
const personLine = lines.findIndex((l) => l.includes('Person'))
|
|
119
|
+
const addressLine = lines.findIndex((l) => l.includes('Address'))
|
|
120
|
+
expect(personLine).toBeLessThan(addressLine)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('multiple associations from same source', () => {
|
|
124
|
+
const diagram = `classDiagram
|
|
125
|
+
Person --> Address
|
|
126
|
+
Person --> Phone`
|
|
127
|
+
const result = renderMermaidASCII(diagram)
|
|
128
|
+
|
|
129
|
+
// Person should be above both targets
|
|
130
|
+
const lines = result.split('\n')
|
|
131
|
+
const personLine = lines.findIndex((l) => l.includes('Person'))
|
|
132
|
+
const addressLine = lines.findIndex((l) => l.includes('Address'))
|
|
133
|
+
const phoneLine = lines.findIndex((l) => l.includes('Phone'))
|
|
134
|
+
|
|
135
|
+
expect(personLine).toBeLessThan(addressLine)
|
|
136
|
+
expect(personLine).toBeLessThan(phoneLine)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('chain of associations', () => {
|
|
140
|
+
const diagram = `classDiagram
|
|
141
|
+
A --> B
|
|
142
|
+
B --> C`
|
|
143
|
+
const result = renderMermaidASCII(diagram)
|
|
144
|
+
|
|
145
|
+
// A > B > C ordering
|
|
146
|
+
const lines = result.split('\n')
|
|
147
|
+
const aLine = lines.findIndex((l) => l.includes('│ A │'))
|
|
148
|
+
const bLine = lines.findIndex((l) => l.includes('│ B │'))
|
|
149
|
+
const cLine = lines.findIndex((l) => l.includes('│ C │'))
|
|
150
|
+
|
|
151
|
+
expect(aLine).toBeLessThan(bLine)
|
|
152
|
+
expect(bLine).toBeLessThan(cLine)
|
|
153
|
+
|
|
154
|
+
// Both arrows point down
|
|
155
|
+
expect(result.match(/▼/g)?.length).toBe(2)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('ASCII mode uses v for downward arrow', () => {
|
|
159
|
+
const diagram = `classDiagram
|
|
160
|
+
Person --> Address`
|
|
161
|
+
const result = renderMermaidASCII(diagram, { useAscii: true })
|
|
162
|
+
|
|
163
|
+
expect(result).toContain('v')
|
|
164
|
+
expect(result).not.toContain('^')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('association routed around an obstruction still points down (non-hierarchical detour case)', () => {
|
|
168
|
+
// Same detour/collision-avoidance routing as the realization test
|
|
169
|
+
// below, but with a plain association so the target-below-source
|
|
170
|
+
// detour branch's `isHierarchical` ternary is exercised on its
|
|
171
|
+
// non-hierarchical ('down') side too, not just the hierarchical
|
|
172
|
+
// ('up') side.
|
|
173
|
+
const diagram = `classDiagram
|
|
174
|
+
class Flyable {
|
|
175
|
+
<<interface>>
|
|
176
|
+
+fly() void
|
|
177
|
+
}
|
|
178
|
+
class Helper {
|
|
179
|
+
+assist() void
|
|
180
|
+
}
|
|
181
|
+
class Bird {
|
|
182
|
+
+fly() void
|
|
183
|
+
}
|
|
184
|
+
Bird --> Helper
|
|
185
|
+
Helper --> Flyable
|
|
186
|
+
Bird --> Flyable`
|
|
187
|
+
const result = renderMermaidASCII(diagram)
|
|
188
|
+
|
|
189
|
+
expect(result).toContain('▼')
|
|
190
|
+
expect(result).not.toContain('▲')
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// DEPENDENCY (..>)
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
describe('Dependency (..>)', () => {
|
|
199
|
+
test('source above target - arrow points DOWN', () => {
|
|
200
|
+
const diagram = `classDiagram
|
|
201
|
+
Client ..> Server`
|
|
202
|
+
const result = renderMermaidASCII(diagram)
|
|
203
|
+
|
|
204
|
+
expect(result).toContain('▼')
|
|
205
|
+
expect(result).not.toContain('▲')
|
|
206
|
+
|
|
207
|
+
const lines = result.split('\n')
|
|
208
|
+
const clientLine = lines.findIndex((l) => l.includes('Client'))
|
|
209
|
+
const serverLine = lines.findIndex((l) => l.includes('Server'))
|
|
210
|
+
expect(clientLine).toBeLessThan(serverLine)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('multiple dependencies', () => {
|
|
214
|
+
const diagram = `classDiagram
|
|
215
|
+
Client ..> Server
|
|
216
|
+
Client ..> Database`
|
|
217
|
+
const result = renderMermaidASCII(diagram)
|
|
218
|
+
|
|
219
|
+
const lines = result.split('\n')
|
|
220
|
+
const clientLine = lines.findIndex((l) => l.includes('Client'))
|
|
221
|
+
const serverLine = lines.findIndex((l) => l.includes('Server'))
|
|
222
|
+
const dbLine = lines.findIndex((l) => l.includes('Database'))
|
|
223
|
+
|
|
224
|
+
expect(clientLine).toBeLessThan(serverLine)
|
|
225
|
+
expect(clientLine).toBeLessThan(dbLine)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test('ASCII mode uses v for downward arrow', () => {
|
|
229
|
+
const diagram = `classDiagram
|
|
230
|
+
Client ..> Server`
|
|
231
|
+
const result = renderMermaidASCII(diagram, { useAscii: true })
|
|
232
|
+
|
|
233
|
+
expect(result).toContain('v')
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
// ============================================================================
|
|
238
|
+
// REALIZATION (..|>)
|
|
239
|
+
// ============================================================================
|
|
240
|
+
|
|
241
|
+
describe('Realization (..|>)', () => {
|
|
242
|
+
test('implementation above interface - triangle points down into interface', () => {
|
|
243
|
+
// Circle ..|> Shape means "Circle implements Shape". Layout places
|
|
244
|
+
// "from" above "to" for every relationship type (matching real
|
|
245
|
+
// mermaid.js — see issue #446), so Circle (implementation/from) is
|
|
246
|
+
// placed ABOVE Shape (interface/to), with the hollow triangle sitting
|
|
247
|
+
// just above Shape, its TIP touching (pointing down into) the
|
|
248
|
+
// interface box — a hollow triangle points toward whichever box it's
|
|
249
|
+
// adjacent to, not away from it.
|
|
250
|
+
const diagram = `classDiagram
|
|
251
|
+
Circle ..|> Shape`
|
|
252
|
+
const result = renderMermaidASCII(diagram)
|
|
253
|
+
|
|
254
|
+
// Circle (implementation) should be above Shape (interface)
|
|
255
|
+
const lines = result.split('\n')
|
|
256
|
+
const shapeLine = lines.findIndex((l) => l.includes('Shape'))
|
|
257
|
+
const circleLine = lines.findIndex((l) => l.includes('Circle'))
|
|
258
|
+
expect(circleLine).toBeLessThan(shapeLine)
|
|
259
|
+
expect(result).toContain('▽')
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
test('realization with <|.. syntax (marker at from end)', () => {
|
|
263
|
+
// Shape <|.. Circle means "Circle implements Shape" (same as Circle ..|> Shape)
|
|
264
|
+
const diagram = `classDiagram
|
|
265
|
+
Shape <|.. Circle`
|
|
266
|
+
const result = renderMermaidASCII(diagram)
|
|
267
|
+
|
|
268
|
+
// Shape (interface) should be above Circle (implementation)
|
|
269
|
+
const lines = result.split('\n')
|
|
270
|
+
const shapeLine = lines.findIndex((l) => l.includes('Shape'))
|
|
271
|
+
const circleLine = lines.findIndex((l) => l.includes('Circle'))
|
|
272
|
+
expect(shapeLine).toBeLessThan(circleLine)
|
|
273
|
+
expect(result).toContain('△')
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
test('multiple implementations', () => {
|
|
277
|
+
// Circle and Square both implement Shape
|
|
278
|
+
const diagram = `classDiagram
|
|
279
|
+
Circle ..|> Shape
|
|
280
|
+
Square ..|> Shape`
|
|
281
|
+
const result = renderMermaidASCII(diagram)
|
|
282
|
+
|
|
283
|
+
// Both implementations above the shared interface
|
|
284
|
+
const lines = result.split('\n')
|
|
285
|
+
const shapeLine = lines.findIndex((l) => l.includes('Shape'))
|
|
286
|
+
const circleLine = lines.findIndex((l) => l.includes('Circle'))
|
|
287
|
+
const squareLine = lines.findIndex((l) => l.includes('Square'))
|
|
288
|
+
|
|
289
|
+
expect(circleLine).toBeLessThan(shapeLine)
|
|
290
|
+
expect(squareLine).toBeLessThan(shapeLine)
|
|
291
|
+
// At least one triangle (may merge visually if same connection point).
|
|
292
|
+
// markerAt='to' here (Shape), so the triangle points down into it: ▽.
|
|
293
|
+
expect(result).toContain('▽')
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
test('realization edge routed around an obstruction still points down into the interface', () => {
|
|
297
|
+
// Bird realizes Flyable two levels down (Helper sits in between,
|
|
298
|
+
// forced there by Bird-->Helper and Helper-->Flyable), so Bird's
|
|
299
|
+
// realization edge can't run straight down — it must detour around
|
|
300
|
+
// Helper's box via the collision-avoidance routing path. This is a
|
|
301
|
+
// different code path from the direct/no-collision case covered by
|
|
302
|
+
// the tests above, and had its own separate rotation bug that was
|
|
303
|
+
// missed in the original fix (only caught in review): the detour
|
|
304
|
+
// branch's marker-drawing code needed the same `isHierarchical`
|
|
305
|
+
// rotation compensation as the direct-routing branches.
|
|
306
|
+
const diagram = `classDiagram
|
|
307
|
+
class Flyable {
|
|
308
|
+
<<interface>>
|
|
309
|
+
+fly() void
|
|
310
|
+
}
|
|
311
|
+
class Helper {
|
|
312
|
+
+assist() void
|
|
313
|
+
}
|
|
314
|
+
class Bird {
|
|
315
|
+
+fly() void
|
|
316
|
+
}
|
|
317
|
+
Bird --> Helper
|
|
318
|
+
Helper --> Flyable
|
|
319
|
+
Bird ..|> Flyable`
|
|
320
|
+
const result = renderMermaidASCII(diagram)
|
|
321
|
+
|
|
322
|
+
// The realization edge is declared last, so it draws last and its
|
|
323
|
+
// marker wins where it lands on the same cell as Helper-->Flyable's
|
|
324
|
+
// own (unrelated, association-style) arrowhead — a separate,
|
|
325
|
+
// pre-existing limitation of this renderer's last-write-wins cell
|
|
326
|
+
// model, not something this test is about.
|
|
327
|
+
expect(result).toContain('▽')
|
|
328
|
+
})
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
// ============================================================================
|
|
332
|
+
// COMPOSITION & AGGREGATION (omnidirectional diamonds)
|
|
333
|
+
// ============================================================================
|
|
334
|
+
|
|
335
|
+
describe('Composition (*--) and Aggregation (o--)', () => {
|
|
336
|
+
test('composition - diamond is omnidirectional', () => {
|
|
337
|
+
const diagram = `classDiagram
|
|
338
|
+
Car *-- Engine`
|
|
339
|
+
const result = renderMermaidASCII(diagram)
|
|
340
|
+
|
|
341
|
+
// Should contain filled diamond
|
|
342
|
+
expect(result).toContain('◆')
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
test('aggregation - hollow diamond is omnidirectional', () => {
|
|
346
|
+
const diagram = `classDiagram
|
|
347
|
+
Team o-- Player`
|
|
348
|
+
const result = renderMermaidASCII(diagram)
|
|
349
|
+
|
|
350
|
+
// Should contain hollow diamond
|
|
351
|
+
expect(result).toContain('◇')
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
// ============================================================================
|
|
356
|
+
// MIXED SCENARIOS
|
|
357
|
+
// ============================================================================
|
|
358
|
+
|
|
359
|
+
describe('Mixed Relationship Scenarios', () => {
|
|
360
|
+
test('all 6 relationship types together', () => {
|
|
361
|
+
const diagram = `classDiagram
|
|
362
|
+
A <|-- B : inheritance
|
|
363
|
+
C *-- D : composition
|
|
364
|
+
E o-- F : aggregation
|
|
365
|
+
G --> H : association
|
|
366
|
+
I ..> J : dependency
|
|
367
|
+
K ..|> L : realization`
|
|
368
|
+
const result = renderMermaidASCII(diagram)
|
|
369
|
+
|
|
370
|
+
// A above B (inheritance, marker at 'from'/A) — marker sits just below
|
|
371
|
+
// A pointing up into it: △. K above L (realization, marker at
|
|
372
|
+
// 'to'/L) — marker sits just above L pointing down into it: ▽. Both
|
|
373
|
+
// point toward their parent/interface; the glyph differs because the
|
|
374
|
+
// marker's position relative to its target differs.
|
|
375
|
+
expect(result.match(/△/g)?.length).toBe(1)
|
|
376
|
+
expect(result.match(/▽/g)?.length).toBe(1)
|
|
377
|
+
|
|
378
|
+
// Downward arrows for association and dependency
|
|
379
|
+
expect(result.match(/▼/g)?.length).toBe(2)
|
|
380
|
+
|
|
381
|
+
// Diamonds for composition and aggregation
|
|
382
|
+
expect(result).toContain('◆')
|
|
383
|
+
expect(result).toContain('◇')
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
test('inheritance with association - different arrow directions', () => {
|
|
387
|
+
const diagram = `classDiagram
|
|
388
|
+
Animal <|-- Dog
|
|
389
|
+
Dog --> Food`
|
|
390
|
+
const result = renderMermaidASCII(diagram)
|
|
391
|
+
|
|
392
|
+
// Should have both up triangle (inheritance) and down arrow (association)
|
|
393
|
+
expect(result).toContain('△')
|
|
394
|
+
expect(result).toContain('▼')
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
test('circular reference creates valid layout', () => {
|
|
398
|
+
const diagram = `classDiagram
|
|
399
|
+
A --> B
|
|
400
|
+
B --> C
|
|
401
|
+
C ..> A`
|
|
402
|
+
const result = renderMermaidASCII(diagram)
|
|
403
|
+
|
|
404
|
+
// Cycles may create mixed arrow directions (up and down) to avoid overlaps
|
|
405
|
+
// Just verify arrows are present and classes are rendered
|
|
406
|
+
const hasUpArrow = result.includes('▲')
|
|
407
|
+
const hasDownArrow = result.includes('▼')
|
|
408
|
+
expect(hasUpArrow || hasDownArrow).toBe(true)
|
|
409
|
+
expect(result).toContain('│ A │')
|
|
410
|
+
expect(result).toContain('│ B │')
|
|
411
|
+
expect(result).toContain('│ C │')
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
test('circular reference with a realization edge points the hollow triangle correctly (same-level routing)', () => {
|
|
415
|
+
// Same cyclic structure as the test above (which forces A and C onto
|
|
416
|
+
// the same level, routing their connecting edge below both boxes and
|
|
417
|
+
// back up — a third, separate marker-drawing branch from the two
|
|
418
|
+
// "target below/above source" cases covered elsewhere in this file),
|
|
419
|
+
// but with the cycle-closing edge as a realization instead of a
|
|
420
|
+
// dependency, so this same-level branch's `isHierarchical` rotation
|
|
421
|
+
// compensation is exercised on its hierarchical side too.
|
|
422
|
+
const diagram = `classDiagram
|
|
423
|
+
A --> B
|
|
424
|
+
B --> C
|
|
425
|
+
C ..|> A`
|
|
426
|
+
const result = renderMermaidASCII(diagram)
|
|
427
|
+
|
|
428
|
+
// The hollow triangle must point up, into A (the realized interface,
|
|
429
|
+
// approached from below after routing under B) — not down, away from
|
|
430
|
+
// it.
|
|
431
|
+
expect(result).toContain('△')
|
|
432
|
+
expect(result).not.toContain('▽')
|
|
433
|
+
})
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
// ============================================================================
|
|
437
|
+
// ASCII vs UNICODE CONSISTENCY
|
|
438
|
+
// ============================================================================
|
|
439
|
+
|
|
440
|
+
describe('ASCII and Unicode Mode Consistency', () => {
|
|
441
|
+
test('same diagram produces consistent layouts in both modes', () => {
|
|
442
|
+
const diagram = `classDiagram
|
|
443
|
+
Animal <|-- Dog
|
|
444
|
+
Person --> Address`
|
|
445
|
+
|
|
446
|
+
const unicode = renderMermaidASCII(diagram)
|
|
447
|
+
const ascii = renderMermaidASCII(diagram, { useAscii: true })
|
|
448
|
+
|
|
449
|
+
// Both should have same node ordering
|
|
450
|
+
const unicodeLines = unicode.split('\n')
|
|
451
|
+
const asciiLines = ascii.split('\n')
|
|
452
|
+
|
|
453
|
+
const uAnimal = unicodeLines.findIndex((l) => l.includes('Animal'))
|
|
454
|
+
const uDog = unicodeLines.findIndex((l) => l.includes('Dog'))
|
|
455
|
+
const aPerson = asciiLines.findIndex((l) => l.includes('Person'))
|
|
456
|
+
const aAddress = asciiLines.findIndex((l) => l.includes('Address'))
|
|
457
|
+
|
|
458
|
+
expect(uAnimal).toBeLessThan(uDog)
|
|
459
|
+
expect(aPerson).toBeLessThan(aAddress)
|
|
460
|
+
|
|
461
|
+
// Unicode has △ and ▼, ASCII has ^ and v
|
|
462
|
+
expect(unicode).toContain('△')
|
|
463
|
+
expect(unicode).toContain('▼')
|
|
464
|
+
expect(ascii).toContain('^')
|
|
465
|
+
expect(ascii).toContain('v')
|
|
466
|
+
})
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
// ============================================================================
|
|
470
|
+
// EDGE CASES
|
|
471
|
+
// ============================================================================
|
|
472
|
+
|
|
473
|
+
describe('Edge Cases', () => {
|
|
474
|
+
test('single inheritance relationship', () => {
|
|
475
|
+
const diagram = `classDiagram
|
|
476
|
+
A <|-- B`
|
|
477
|
+
const result = renderMermaidASCII(diagram)
|
|
478
|
+
|
|
479
|
+
expect(result).toContain('△')
|
|
480
|
+
const lines = result.split('\n')
|
|
481
|
+
const aLine = lines.findIndex((l) => l.includes('│ A │'))
|
|
482
|
+
const bLine = lines.findIndex((l) => l.includes('│ B │'))
|
|
483
|
+
expect(aLine).toBeLessThan(bLine)
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
test('classes with members maintain arrow directions', () => {
|
|
487
|
+
const diagram = `classDiagram
|
|
488
|
+
class Animal {
|
|
489
|
+
+String name
|
|
490
|
+
+eat() void
|
|
491
|
+
}
|
|
492
|
+
class Dog {
|
|
493
|
+
+bark() void
|
|
494
|
+
}
|
|
495
|
+
Animal <|-- Dog`
|
|
496
|
+
const result = renderMermaidASCII(diagram)
|
|
497
|
+
|
|
498
|
+
expect(result).toContain('△')
|
|
499
|
+
const lines = result.split('\n')
|
|
500
|
+
const animalLine = lines.findIndex((l) => l.includes('Animal'))
|
|
501
|
+
const dogLine = lines.findIndex((l) => l.includes('Dog'))
|
|
502
|
+
expect(animalLine).toBeLessThan(dogLine)
|
|
503
|
+
})
|
|
504
|
+
})
|
|
505
|
+
})
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct unit tests for drawLine's diagonal routing branches
|
|
3
|
+
* (UpperLeft/UpperRight/LowerLeft/LowerRight in src/ascii/draw-lines.ts).
|
|
4
|
+
*
|
|
5
|
+
* These branches only fire when `from` and `to` differ on both axes.
|
|
6
|
+
* Every real call site (draw-arrows.ts's drawPath, draw-bundles.ts) feeds
|
|
7
|
+
* drawLine coordinates derived from an A* grid path — and the pathfinder
|
|
8
|
+
* only ever moves 4-directionally (see pathfinder.ts's MOVE_DIRS) — so no
|
|
9
|
+
* Mermaid diagram through the renderer can actually produce a diagonal
|
|
10
|
+
* `from`/`to` pair here; the pure-vertical/horizontal branches are already
|
|
11
|
+
* covered that way. Testing this diagonal routing therefore has to call
|
|
12
|
+
* drawLine directly, the same way pathfinder.test.ts unit-tests getPath
|
|
13
|
+
* directly with hand-built inputs rather than through a full render.
|
|
14
|
+
*/
|
|
15
|
+
import { describe, it, expect } from 'vitest'
|
|
16
|
+
import { drawLine } from '../draw-lines.ts'
|
|
17
|
+
import { mkCanvas } from '../canvas.ts'
|
|
18
|
+
|
|
19
|
+
describe('drawLine diagonal routing', () => {
|
|
20
|
+
it('routes UpperLeft as a horizontal-then-vertical bend', () => {
|
|
21
|
+
const canvas = mkCanvas(10, 10)
|
|
22
|
+
const drawn = drawLine(canvas, { x: 8, y: 8 }, { x: 2, y: 2 }, 1, -1, false)
|
|
23
|
+
expect(canvas[7]![8]).toBe('─')
|
|
24
|
+
expect(canvas[2]![8]).toBe('─')
|
|
25
|
+
expect(canvas[2]![7]).toBe('│')
|
|
26
|
+
expect(canvas[2]![3]).toBe('│')
|
|
27
|
+
expect(drawn.length).toBe(6 + 5)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('routes UpperRight as a horizontal-then-vertical bend', () => {
|
|
31
|
+
const canvas = mkCanvas(10, 10)
|
|
32
|
+
const drawn = drawLine(canvas, { x: 2, y: 8 }, { x: 8, y: 2 }, 1, -1, false)
|
|
33
|
+
expect(canvas[3]![8]).toBe('─')
|
|
34
|
+
expect(canvas[8]![8]).toBe('─')
|
|
35
|
+
expect(canvas[8]![7]).toBe('│')
|
|
36
|
+
expect(canvas[8]![3]).toBe('│')
|
|
37
|
+
expect(drawn.length).toBe(6 + 5)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('routes LowerLeft as a horizontal-then-vertical bend', () => {
|
|
41
|
+
const canvas = mkCanvas(10, 10)
|
|
42
|
+
const drawn = drawLine(canvas, { x: 8, y: 2 }, { x: 2, y: 8 }, 1, -1, false)
|
|
43
|
+
expect(canvas[7]![2]).toBe('─')
|
|
44
|
+
expect(canvas[2]![2]).toBe('─')
|
|
45
|
+
expect(canvas[2]![3]).toBe('│')
|
|
46
|
+
expect(canvas[2]![7]).toBe('│')
|
|
47
|
+
expect(drawn.length).toBe(6 + 5)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('LowerRight', () => {
|
|
51
|
+
it('draws a straight vertical line when x delta is 1 (source-aligned)', () => {
|
|
52
|
+
const canvas = mkCanvas(10, 10)
|
|
53
|
+
const drawn = drawLine(
|
|
54
|
+
canvas,
|
|
55
|
+
{ x: 5, y: 2 },
|
|
56
|
+
{ x: 6, y: 8 },
|
|
57
|
+
1,
|
|
58
|
+
-1,
|
|
59
|
+
false,
|
|
60
|
+
)
|
|
61
|
+
for (let y = 3; y <= 7; y++) {
|
|
62
|
+
expect(canvas[5]![y]).toBe('│')
|
|
63
|
+
}
|
|
64
|
+
expect(canvas[6]![8]).toBe(' ')
|
|
65
|
+
expect(drawn.every((c) => c.x === 5)).toBe(true)
|
|
66
|
+
expect(drawn.length).toBe(5)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('routes as a horizontal-then-vertical bend when x delta is greater than 1', () => {
|
|
70
|
+
const canvas = mkCanvas(10, 10)
|
|
71
|
+
const drawn = drawLine(
|
|
72
|
+
canvas,
|
|
73
|
+
{ x: 2, y: 2 },
|
|
74
|
+
{ x: 8, y: 8 },
|
|
75
|
+
1,
|
|
76
|
+
-1,
|
|
77
|
+
false,
|
|
78
|
+
)
|
|
79
|
+
expect(canvas[3]![2]).toBe('─')
|
|
80
|
+
expect(canvas[8]![2]).toBe('─')
|
|
81
|
+
expect(canvas[8]![3]).toBe('│')
|
|
82
|
+
expect(canvas[8]![7]).toBe('│')
|
|
83
|
+
expect(drawn.length).toBe(6 + 5)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('uses ASCII characters in ASCII mode', () => {
|
|
87
|
+
const canvas = mkCanvas(10, 10)
|
|
88
|
+
drawLine(canvas, { x: 2, y: 2 }, { x: 8, y: 8 }, 1, -1, true)
|
|
89
|
+
expect(canvas[3]![2]).toBe('-')
|
|
90
|
+
expect(canvas[8]![3]).toBe('|')
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
})
|