zombie-mermaid 4.2.2 → 4.2.3

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.
Files changed (92) hide show
  1. package/package.json +9 -7
  2. package/src/__tests__/architecture-integration.test.ts +0 -217
  3. package/src/__tests__/ascii-ansi.test.ts +0 -539
  4. package/src/__tests__/ascii-draw-bundles.test.ts +0 -849
  5. package/src/__tests__/ascii-edge-bundling-routing.test.ts +0 -214
  6. package/src/__tests__/ascii-edge-cross-style-overlap.test.ts +0 -216
  7. package/src/__tests__/ascii-grid-deep-chain.test.ts +0 -64
  8. package/src/__tests__/ascii-hyperlinks.test.ts +0 -364
  9. package/src/__tests__/ascii-nested-subgraph-sibling-order-444.test.ts +0 -108
  10. package/src/__tests__/ascii-nonmember-beside-frame-1283.test.ts +0 -113
  11. package/src/__tests__/ascii-package-boundary.test.ts +0 -291
  12. package/src/__tests__/ascii-parallel-edges-329.test.ts +0 -315
  13. package/src/__tests__/ascii-parallel-lane-border-1230.test.ts +0 -71
  14. package/src/__tests__/ascii-reverse-direction-edges-629.test.ts +0 -282
  15. package/src/__tests__/ascii-stacked-frame-geometry-1285.test.ts +0 -107
  16. package/src/__tests__/ascii-subgraph-bbox.test.ts +0 -139
  17. package/src/__tests__/ascii-subgraph-later-edgeless-301.test.ts +0 -277
  18. package/src/__tests__/browser.test.ts +0 -34
  19. package/src/__tests__/c4-integration.test.ts +0 -161
  20. package/src/__tests__/c4-orientation.test.ts +0 -162
  21. package/src/__tests__/c4-upstream-integration.test.ts +0 -211
  22. package/src/__tests__/class-click-interactions.test.ts +0 -232
  23. package/src/__tests__/class-integration.test.ts +0 -154
  24. package/src/__tests__/class-notes.test.ts +0 -317
  25. package/src/__tests__/class-parser-error-messages.test.ts +0 -162
  26. package/src/__tests__/class-parser.test.ts +0 -347
  27. package/src/__tests__/class-styling.test.ts +0 -485
  28. package/src/__tests__/cli-diagrams.test.ts +0 -106
  29. package/src/__tests__/cli-e2e.test.ts +0 -275
  30. package/src/__tests__/cli-entrypoint.test.ts +0 -146
  31. package/src/__tests__/cli-mcp-dispatch.test.ts +0 -68
  32. package/src/__tests__/cli-mcp.test.ts +0 -44
  33. package/src/__tests__/cli-png.test.ts +0 -35
  34. package/src/__tests__/cli-render.test.ts +0 -988
  35. package/src/__tests__/cli-test-helpers.ts +0 -54
  36. package/src/__tests__/cli-web-dispatch.test.ts +0 -62
  37. package/src/__tests__/cli-web.test.ts +0 -203
  38. package/src/__tests__/cli.test.ts +0 -1027
  39. package/src/__tests__/compound-flat-layout.test.ts +0 -901
  40. package/src/__tests__/direction-override.test.ts +0 -397
  41. package/src/__tests__/edge-approach-direction.test.ts +0 -295
  42. package/src/__tests__/edge-bundling-node-clearance.test.ts +0 -179
  43. package/src/__tests__/elk-layout-cache.test.ts +0 -223
  44. package/src/__tests__/er-click-unsupported.test.ts +0 -69
  45. package/src/__tests__/er-integration.test.ts +0 -531
  46. package/src/__tests__/er-parser-error-messages.test.ts +0 -82
  47. package/src/__tests__/er-parser.test.ts +0 -294
  48. package/src/__tests__/expanded-shapes.test.ts +0 -229
  49. package/src/__tests__/flowchart-back-edge-prereversal-1239.test.ts +0 -212
  50. package/src/__tests__/flowchart-syntax-gaps.test.ts +0 -371
  51. package/src/__tests__/guides-examples.test.ts +0 -181
  52. package/src/__tests__/integration.test.ts +0 -729
  53. package/src/__tests__/interactivity-config.test.ts +0 -723
  54. package/src/__tests__/interactivity-option.test.ts +0 -173
  55. package/src/__tests__/layout-disconnected.test.ts +0 -384
  56. package/src/__tests__/layout-engine.test.ts +0 -91
  57. package/src/__tests__/linkstyle.test.ts +0 -133
  58. package/src/__tests__/mcp-render-ascii-tool.test.ts +0 -95
  59. package/src/__tests__/mcp-render-svg-theme-guard.test.ts +0 -36
  60. package/src/__tests__/mcp-render-svg-tool.test.ts +0 -118
  61. package/src/__tests__/mermaid-entity-codes.test.ts +0 -114
  62. package/src/__tests__/monospace-metrics.test.ts +0 -74
  63. package/src/__tests__/multiline-labels.test.ts +0 -924
  64. package/src/__tests__/nested-subgraph-direction-crossing.test.ts +0 -322
  65. package/src/__tests__/nested-subgraph-edge-container-offset.test.ts +0 -151
  66. package/src/__tests__/package-info.test.ts +0 -51
  67. package/src/__tests__/parser.test.ts +0 -1544
  68. package/src/__tests__/render-options-overrides.test.ts +0 -257
  69. package/src/__tests__/render-options-per-type.test.ts +0 -178
  70. package/src/__tests__/renderer.test.ts +0 -743
  71. package/src/__tests__/resolve-colors.test.ts +0 -448
  72. package/src/__tests__/self-loop-edges.test.ts +0 -112
  73. package/src/__tests__/sequence-activation-check.test.ts +0 -219
  74. package/src/__tests__/sequence-integration.test.ts +0 -312
  75. package/src/__tests__/sequence-layout.test.ts +0 -1103
  76. package/src/__tests__/sequence-parser-syntax-errors.test.ts +0 -179
  77. package/src/__tests__/sequence-parser.test.ts +0 -821
  78. package/src/__tests__/start-marker-only-links-1386.test.ts +0 -104
  79. package/src/__tests__/state-composite-order-1287.test.ts +0 -132
  80. package/src/__tests__/state-flow-order-1240.test.ts +0 -113
  81. package/src/__tests__/state-styling.test.ts +0 -150
  82. package/src/__tests__/statements.test.ts +0 -264
  83. package/src/__tests__/styles.test.ts +0 -220
  84. package/src/__tests__/subgraph-title-edge-gap-1239.test.ts +0 -238
  85. package/src/__tests__/svg-accessible-name-conformance.test.ts +0 -276
  86. package/src/__tests__/svg-accessible-name.test.ts +0 -421
  87. package/src/__tests__/svg-csp-style-options.test.ts +0 -427
  88. package/src/__tests__/workspace-package-boundaries.test.ts +0 -336
  89. package/src/__tests__/xychart-integration.test.ts +0 -137
  90. package/src/__tests__/xychart-layout.test.ts +0 -205
  91. package/src/__tests__/xychart-parser-categories.test.ts +0 -78
  92. package/src/__tests__/xychart-parser-errors.test.ts +0 -95
@@ -1,264 +0,0 @@
1
- /**
2
- * Tests for issue #181 — semicolon-separated statements.
3
- *
4
- * `detectDiagramType` isolated the header by splitting on `[\n;]`, so
5
- * `sequenceDiagram;A->>B: Hi` routed to the sequence pipeline — but every
6
- * parser then split the body on newlines only, discarding everything after
7
- * the header and rendering an empty diagram. The same gap existed on the
8
- * flowchart path, where `flowchart TD;A-->B` instead threw
9
- * "Invalid mermaid header".
10
- *
11
- * `splitStatements` is now the single definition of "what is one statement",
12
- * shared by the detector and all five parser entry points.
13
- */
14
- import { describe, it, expect } from 'vitest'
15
- import { splitStatements, detectDiagramType } from '@zombie-mermaid/core'
16
- import { renderMermaidASCII, renderMermaidSVG } from '../index.ts'
17
-
18
- /** Project a `splitStatements` result down to just the text, for assertions
19
- * that don't care about position (see the dedicated `line` tests below for
20
- * position coverage). */
21
- const texts = (source: string) => splitStatements(source).map((s) => s.text)
22
-
23
- describe('splitStatements (issue #181)', () => {
24
- it('splits on newlines', () => {
25
- expect(texts('graph TD\nA-->B')).toEqual(['graph TD', 'A-->B'])
26
- })
27
-
28
- it('splits on semicolons', () => {
29
- expect(texts('graph TD;A-->B;B-->C')).toEqual([
30
- 'graph TD',
31
- 'A-->B',
32
- 'B-->C',
33
- ])
34
- })
35
-
36
- it('drops empty statements from trailing or repeated separators', () => {
37
- expect(texts('graph TD;A-->B;')).toEqual(['graph TD', 'A-->B'])
38
- expect(texts('graph TD;;;A-->B')).toEqual(['graph TD', 'A-->B'])
39
- expect(texts('graph TD\n\n\nA-->B')).toEqual(['graph TD', 'A-->B'])
40
- })
41
-
42
- it('trims whitespace around each statement', () => {
43
- expect(texts(' graph TD ; A-->B ')).toEqual(['graph TD', 'A-->B'])
44
- })
45
-
46
- describe('does not split a semicolon that is not a separator', () => {
47
- it('inside a double-quoted label', () => {
48
- expect(texts('graph TD\nA["a; b"]-->B')).toEqual([
49
- 'graph TD',
50
- 'A["a; b"]-->B',
51
- ])
52
- })
53
-
54
- it('inside a single-quoted label', () => {
55
- expect(texts("graph TD\nA['x; y']-->B")).toEqual([
56
- 'graph TD',
57
- "A['x; y']-->B",
58
- ])
59
- })
60
-
61
- it('terminating a named character reference', () => {
62
- expect(texts('graph TD\nA[&]-->B')).toEqual([
63
- 'graph TD',
64
- 'A[&]-->B',
65
- ])
66
- })
67
-
68
- it('terminating a numeric character reference', () => {
69
- expect(texts('graph TD\nA[#]-->B')).toEqual([
70
- 'graph TD',
71
- 'A[#]-->B',
72
- ])
73
- })
74
-
75
- it('terminating a hex character reference', () => {
76
- expect(texts('graph TD\nA[😀]-->B')).toEqual([
77
- 'graph TD',
78
- 'A[😀]-->B',
79
- ])
80
- })
81
-
82
- it('but still splits a real separator on the same line as an entity', () => {
83
- expect(texts('graph TD;A[&]-->B;B-->C')).toEqual([
84
- 'graph TD',
85
- 'A[&]-->B',
86
- 'B-->C',
87
- ])
88
- })
89
-
90
- it('and treats a lone ampersand as an ordinary character', () => {
91
- // `A & B` is flowchart's parallel-link syntax, not an entity.
92
- expect(texts('graph TD;A-->B & C')).toEqual(['graph TD', 'A-->B & C'])
93
- })
94
- })
95
-
96
- describe('comments', () => {
97
- it('drops whole-line comments without splitting their semicolons', () => {
98
- expect(texts('graph TD\n%% note; not code\nA-->B')).toEqual([
99
- 'graph TD',
100
- 'A-->B',
101
- ])
102
- })
103
-
104
- it('drops a comment that only begins after a semicolon', () => {
105
- expect(texts('graph TD\nA-->B; %% note')).toEqual(['graph TD', 'A-->B'])
106
- })
107
-
108
- it('lets the comment swallow the rest of the line', () => {
109
- /*
110
- * A Mermaid comment runs to end of line, so a `;` inside it must not
111
- * resurrect what follows. Splitting first and then discarding fragments
112
- * that merely begin with `%%` left `C-->D` parsing as code.
113
- */
114
- expect(texts('graph TD\nA-->B; %% note; C-->D')).toEqual([
115
- 'graph TD',
116
- 'A-->B',
117
- ])
118
- })
119
-
120
- it('treats %% inside a quoted label as text, not a comment', () => {
121
- expect(texts('graph TD\nA["100%% done"]-->B')).toEqual([
122
- 'graph TD',
123
- 'A["100%% done"]-->B',
124
- ])
125
- })
126
- })
127
-
128
- describe("Mermaid's own #-prefixed character references", () => {
129
- /*
130
- * Mermaid spells character references with `#` where HTML uses `&` —
131
- * `#59;` is the documented way to put a literal semicolon in a label.
132
- * Treating that `;` as a separator both corrupts the label and invents a
133
- * statement from the remainder.
134
- */
135
- it.each([
136
- ['A[semi #59; here]-->B', 'a numeric reference'],
137
- ['A[heart #9829; x]-->B', 'a symbol reference'],
138
- ['A[say #quot;hi#quot;]-->B', 'a named reference'],
139
- ])('keeps %s intact (%s)', (statement) => {
140
- expect(texts(`graph TD\n ${statement}`)).toEqual(['graph TD', statement])
141
- })
142
-
143
- it('still splits a real separator on the same line as one', () => {
144
- expect(texts('graph TD;A[#59;]-->B;B-->C')).toEqual([
145
- 'graph TD',
146
- 'A[#59;]-->B',
147
- 'B-->C',
148
- ])
149
- })
150
-
151
- it('leaves a bare # that is not a reference alone', () => {
152
- expect(texts('graph TD;A[C# lang]-->B')).toEqual([
153
- 'graph TD',
154
- 'A[C# lang]-->B',
155
- ])
156
- })
157
- })
158
- })
159
-
160
- describe('splitStatements — line numbers (issue #760)', () => {
161
- it('reports the 1-based physical source line for each newline-separated statement', () => {
162
- const stmts = splitStatements('graph TD\nA-->B\nB-->C')
163
- expect(stmts.map((s) => s.line)).toEqual([1, 2, 3])
164
- })
165
-
166
- it('carries forward the original line number across dropped blank lines', () => {
167
- const stmts = splitStatements('graph TD\n\n\nA-->B')
168
- expect(stmts.map((s) => ({ text: s.text, line: s.line }))).toEqual([
169
- { text: 'graph TD', line: 1 },
170
- { text: 'A-->B', line: 4 },
171
- ])
172
- })
173
-
174
- it('carries forward the original line number across a dropped comment line', () => {
175
- const stmts = splitStatements('graph TD\n%% a comment\nA-->B')
176
- expect(stmts.map((s) => ({ text: s.text, line: s.line }))).toEqual([
177
- { text: 'graph TD', line: 1 },
178
- { text: 'A-->B', line: 3 },
179
- ])
180
- })
181
-
182
- it('gives every semicolon-separated statement on one physical line the same line number', () => {
183
- const stmts = splitStatements('graph TD\nA-->B;B-->C;C-->D')
184
- expect(stmts.map((s) => s.line)).toEqual([1, 2, 2, 2])
185
- })
186
- })
187
-
188
- describe('semicolon-separated diagrams render their bodies (issue #181)', () => {
189
- const cases = [
190
- {
191
- type: 'flowchart' as const,
192
- // Previously threw: the header regex saw the whole line.
193
- source: 'flowchart TD;A-->B;B-->C',
194
- expected: ['A', 'B', 'C'],
195
- },
196
- {
197
- type: 'sequence' as const,
198
- source: 'sequenceDiagram;A->>B: Hi',
199
- expected: ['A', 'B', 'Hi'],
200
- },
201
- {
202
- type: 'class' as const,
203
- source: 'classDiagram;class Animal;class Dog',
204
- expected: ['Animal', 'Dog'],
205
- },
206
- {
207
- type: 'er' as const,
208
- source: 'erDiagram;CUSTOMER ||--o{ ORDER : places',
209
- expected: ['CUSTOMER', 'ORDER', 'places'],
210
- },
211
- {
212
- type: 'xychart' as const,
213
- source: 'xychart-beta;title "Sales";bar [1, 2, 3]',
214
- expected: ['Sales'],
215
- },
216
- ]
217
-
218
- for (const { type, source, expected } of cases) {
219
- describe(type, () => {
220
- it('routes to the expected pipeline', () => {
221
- expect(detectDiagramType(source)).toBe(type)
222
- })
223
-
224
- it('renders the body to ASCII instead of an empty diagram', () => {
225
- const ascii = renderMermaidASCII(source, { colorMode: 'none' })
226
- expect(ascii.trim()).not.toBe('')
227
- for (const token of expected) expect(ascii).toContain(token)
228
- })
229
-
230
- it('renders the body to SVG instead of an empty diagram', () => {
231
- const svg = renderMermaidSVG(source)
232
- for (const token of expected) expect(svg).toContain(token)
233
- })
234
- })
235
- }
236
-
237
- it('accepts semicolons after a newline-separated header', () => {
238
- const ascii = renderMermaidASCII('sequenceDiagram\nA->>B: Hi;B->>C: Yo', {
239
- colorMode: 'none',
240
- })
241
- expect(ascii).toContain('Hi')
242
- expect(ascii).toContain('Yo')
243
- expect(ascii).toContain('C')
244
- // The old behavior swallowed the second message into the first label.
245
- expect(ascii).not.toContain('Hi;B')
246
- })
247
-
248
- it('leaves newline-separated sources rendering identically', () => {
249
- const withNewlines = renderMermaidASCII('flowchart TD\nA-->B\nB-->C', {
250
- colorMode: 'none',
251
- })
252
- const withSemicolons = renderMermaidASCII('flowchart TD;A-->B;B-->C', {
253
- colorMode: 'none',
254
- })
255
- expect(withSemicolons).toBe(withNewlines)
256
- })
257
-
258
- it('preserves a semicolon that is part of a label', () => {
259
- const ascii = renderMermaidASCII('flowchart TD\nA["a; b"]-->B', {
260
- colorMode: 'none',
261
- })
262
- expect(ascii).toContain('a; b')
263
- })
264
- })
@@ -1,220 +0,0 @@
1
- /**
2
- * Tests for styles module — text measurement and constants.
3
- * Theme resolution tests are in theme.test.ts (CSS custom property system).
4
- */
5
- import { describe, it, expect } from 'vitest'
6
- import {
7
- estimateTextWidth,
8
- FONT_SIZES,
9
- FONT_WEIGHTS,
10
- NODE_PADDING,
11
- STROKE_WIDTHS,
12
- ARROW_HEAD,
13
- buildSvgStyleBlock,
14
- } from '@zombie-mermaid/svg-renderer'
15
- import {
16
- THEMES,
17
- DEFAULTS,
18
- fromShikiTheme,
19
- buildStyleBlock,
20
- svgOpenTag,
21
- } from '@zombie-mermaid/core'
22
- import type { DiagramColors } from '@zombie-mermaid/core'
23
-
24
- // ============================================================================
25
- // Theme system (CSS custom properties)
26
- // ============================================================================
27
-
28
- describe('THEMES', () => {
29
- it('contains well-known theme palettes', () => {
30
- expect(THEMES['zinc-light']).toBeDefined()
31
- expect(THEMES['zinc-dark']).toBeDefined()
32
- expect(THEMES['tokyo-night']).toBeDefined()
33
- expect(THEMES['catppuccin-mocha']).toBeDefined()
34
- expect(THEMES['nord']).toBeDefined()
35
- })
36
-
37
- it('each theme has valid bg and fg colors', () => {
38
- for (const colors of Object.values(THEMES)) {
39
- expect(colors.bg).toMatch(/^#[0-9a-fA-F]{6}$/)
40
- expect(colors.fg).toMatch(/^#[0-9a-fA-F]{6}$/)
41
- }
42
- })
43
- })
44
-
45
- describe('DEFAULTS', () => {
46
- it('provides zinc-light bg/fg', () => {
47
- expect(DEFAULTS.bg).toBe('#FFFFFF')
48
- expect(DEFAULTS.fg).toBe('#27272A')
49
- })
50
- })
51
-
52
- describe('svgOpenTag', () => {
53
- it('sets --bg and --fg CSS variables in inline style', () => {
54
- const tag = svgOpenTag(400, 300, { bg: '#1a1b26', fg: '#a9b1d6' })
55
- expect(tag).toContain('--bg:#1a1b26')
56
- expect(tag).toContain('--fg:#a9b1d6')
57
- expect(tag).toContain('background:var(--bg)')
58
- })
59
-
60
- it('includes optional enrichment variables when provided', () => {
61
- const colors: DiagramColors = {
62
- bg: '#1a1b26',
63
- fg: '#a9b1d6',
64
- line: '#3d59a1',
65
- accent: '#7aa2f7',
66
- }
67
- const tag = svgOpenTag(400, 300, colors)
68
- expect(tag).toContain('--line:#3d59a1')
69
- expect(tag).toContain('--accent:#7aa2f7')
70
- })
71
-
72
- it('omits unset enrichment variables', () => {
73
- const tag = svgOpenTag(400, 300, { bg: '#fff', fg: '#000' })
74
- expect(tag).not.toContain('--line')
75
- expect(tag).not.toContain('--accent')
76
- expect(tag).not.toContain('--muted')
77
- })
78
- })
79
-
80
- describe('buildStyleBlock', () => {
81
- it('includes derived CSS variable declarations', () => {
82
- const style = buildStyleBlock('Inter', false)
83
- expect(style).toContain('--_text')
84
- expect(style).toContain('--_line')
85
- expect(style).toContain('--_arrow')
86
- expect(style).toContain('--_node-fill')
87
- expect(style).toContain('--_node-stroke')
88
- })
89
-
90
- it('includes mono font class when requested', () => {
91
- const withMono = buildSvgStyleBlock('Inter', true)
92
- expect(withMono).toContain('.mono')
93
- expect(withMono).toContain('JetBrains Mono')
94
-
95
- const withoutMono = buildStyleBlock('Inter', false)
96
- expect(withoutMono).not.toContain('.mono')
97
- })
98
-
99
- it('buildSvgStyleBlock omits the mono rule and font face when hasMonoFont is false', () => {
100
- const block = buildSvgStyleBlock('Inter', false)
101
- expect(block).not.toContain('.mono')
102
- expect(block).not.toContain('@font-face')
103
- expect(block).toContain('--_text')
104
- })
105
-
106
- it('embeds the self-hosted mono subset as a base64 woff2 data URI (#1061)', () => {
107
- const block = buildSvgStyleBlock('Inter', true)
108
- expect(block).toContain('@font-face')
109
- expect(block).toContain('data:font/woff2;base64,')
110
- expect(block).toContain("font-family: 'JetBrains Mono NL';")
111
- expect(block).toContain(
112
- " .mono { font-family: 'JetBrains Mono NL', 'SF Mono', 'Fira Code', ui-monospace, monospace; }",
113
- )
114
- expect(block).not.toContain('fonts.googleapis.com/css2?family=JetBrains')
115
- })
116
- })
117
-
118
- describe('fromShikiTheme', () => {
119
- it('extracts bg/fg from editor colors', () => {
120
- const colors = fromShikiTheme({
121
- type: 'dark',
122
- colors: {
123
- 'editor.background': '#1a1b26',
124
- 'editor.foreground': '#a9b1d6',
125
- },
126
- })
127
- expect(colors.bg).toBe('#1a1b26')
128
- expect(colors.fg).toBe('#a9b1d6')
129
- })
130
-
131
- it('falls back for missing editor colors', () => {
132
- const dark = fromShikiTheme({ type: 'dark' })
133
- expect(dark.bg).toBe('#1e1e1e')
134
- expect(dark.fg).toBe('#d4d4d4')
135
-
136
- const light = fromShikiTheme({ type: 'light' })
137
- expect(light.bg).toBe('#ffffff')
138
- expect(light.fg).toBe('#333333')
139
- })
140
- })
141
-
142
- // ============================================================================
143
- // Text width estimation
144
- // ============================================================================
145
-
146
- describe('estimateTextWidth', () => {
147
- it('returns a positive number for non-empty text', () => {
148
- const width = estimateTextWidth('Hello', 13, 500)
149
- expect(width).toBeGreaterThan(0)
150
- })
151
-
152
- it('returns minimum padding for empty text', () => {
153
- // Empty text still returns minimum padding (fontSize * 0.15) for layout safety
154
- expect(estimateTextWidth('', 13, 500)).toBeCloseTo(1.95, 1)
155
- })
156
-
157
- it('scales with text length', () => {
158
- const short = estimateTextWidth('Hi', 13, 500)
159
- const long = estimateTextWidth('Hello World', 13, 500)
160
- expect(long).toBeGreaterThan(short)
161
- })
162
-
163
- it('scales with font size', () => {
164
- const small = estimateTextWidth('Text', 11, 500)
165
- const large = estimateTextWidth('Text', 16, 500)
166
- expect(large).toBeGreaterThan(small)
167
- })
168
-
169
- it('heavier weights produce wider estimates', () => {
170
- const regular = estimateTextWidth('Text', 13, 400)
171
- const bold = estimateTextWidth('Text', 13, 600)
172
- expect(bold).toBeGreaterThan(regular)
173
- })
174
-
175
- it('produces reasonable widths for typical node labels', () => {
176
- // A 5-character label at 13px/500w should be roughly 35px (5 * 13 * 0.55)
177
- const width = estimateTextWidth(
178
- 'Hello',
179
- FONT_SIZES.nodeLabel,
180
- FONT_WEIGHTS.nodeLabel,
181
- )
182
- expect(width).toBeGreaterThan(25)
183
- expect(width).toBeLessThan(60)
184
- })
185
- })
186
-
187
- // ============================================================================
188
- // Exported constants
189
- // ============================================================================
190
-
191
- describe('constants', () => {
192
- it('FONT_SIZES has expected values', () => {
193
- expect(FONT_SIZES.nodeLabel).toBe(13)
194
- expect(FONT_SIZES.edgeLabel).toBe(11)
195
- expect(FONT_SIZES.groupHeader).toBe(12)
196
- })
197
-
198
- it('FONT_WEIGHTS has expected values', () => {
199
- expect(FONT_WEIGHTS.nodeLabel).toBe(500)
200
- expect(FONT_WEIGHTS.edgeLabel).toBe(400)
201
- expect(FONT_WEIGHTS.groupHeader).toBe(600)
202
- })
203
-
204
- it('NODE_PADDING has expected values', () => {
205
- expect(NODE_PADDING.horizontal).toBe(20)
206
- expect(NODE_PADDING.vertical).toBe(10)
207
- expect(NODE_PADDING.diamondExtra).toBe(24)
208
- })
209
-
210
- it('STROKE_WIDTHS has expected values', () => {
211
- expect(STROKE_WIDTHS.outerBox).toBe(1)
212
- expect(STROKE_WIDTHS.innerBox).toBe(0.75)
213
- expect(STROKE_WIDTHS.connector).toBe(1)
214
- })
215
-
216
- it('ARROW_HEAD has expected values', () => {
217
- expect(ARROW_HEAD.width).toBe(8)
218
- expect(ARROW_HEAD.height).toBe(5)
219
- })
220
- })