@transitrix/diagrams 1.8.6 → 1.8.7

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 (247) hide show
  1. package/package.json +7 -2
  2. package/dist/__tests__/edge-path.test.d.ts +0 -1
  3. package/dist/__tests__/edge-path.test.js +0 -55
  4. package/dist/__tests__/node-size-presets.test.d.ts +0 -1
  5. package/dist/__tests__/node-size-presets.test.js +0 -30
  6. package/dist/__tests__/scope.test.d.ts +0 -1
  7. package/dist/__tests__/scope.test.js +0 -23
  8. package/dist/__tests__/yaml-normalize.test.d.ts +0 -1
  9. package/dist/__tests__/yaml-normalize.test.js +0 -148
  10. package/dist/activities/__tests__/cpm.test.d.ts +0 -1
  11. package/dist/activities/__tests__/cpm.test.js +0 -146
  12. package/dist/activities/__tests__/gantt.test.d.ts +0 -1
  13. package/dist/activities/__tests__/gantt.test.js +0 -263
  14. package/dist/activities/__tests__/layout.test.d.ts +0 -1
  15. package/dist/activities/__tests__/layout.test.js +0 -98
  16. package/dist/activities/__tests__/validate.test.d.ts +0 -1
  17. package/dist/activities/__tests__/validate.test.js +0 -397
  18. package/dist/activity-card/__tests__/example.test.d.ts +0 -1
  19. package/dist/activity-card/__tests__/example.test.js +0 -73
  20. package/dist/activity-card/__tests__/layout.test.d.ts +0 -1
  21. package/dist/activity-card/__tests__/layout.test.js +0 -141
  22. package/dist/activity-card/__tests__/resolver.test.d.ts +0 -1
  23. package/dist/activity-card/__tests__/resolver.test.js +0 -325
  24. package/dist/activity-card/__tests__/validate.test.d.ts +0 -1
  25. package/dist/activity-card/__tests__/validate.test.js +0 -149
  26. package/dist/actor/__tests__/validate.test.d.ts +0 -1
  27. package/dist/actor/__tests__/validate.test.js +0 -95
  28. package/dist/applications/__tests__/validate.test.d.ts +0 -1
  29. package/dist/applications/__tests__/validate.test.js +0 -223
  30. package/dist/assertion/__tests__/example.test.d.ts +0 -1
  31. package/dist/assertion/__tests__/example.test.js +0 -32
  32. package/dist/assertion/__tests__/validate.test.d.ts +0 -1
  33. package/dist/assertion/__tests__/validate.test.js +0 -130
  34. package/dist/blocks/__tests__/layout.test.d.ts +0 -1
  35. package/dist/blocks/__tests__/layout.test.js +0 -153
  36. package/dist/blocks/__tests__/validate.test.d.ts +0 -1
  37. package/dist/blocks/__tests__/validate.test.js +0 -235
  38. package/dist/business-service/__tests__/validate.test.d.ts +0 -1
  39. package/dist/business-service/__tests__/validate.test.js +0 -135
  40. package/dist/capability-map/__tests__/CapabilityMapView.test.d.ts +0 -1
  41. package/dist/capability-map/__tests__/CapabilityMapView.test.js +0 -217
  42. package/dist/capability-map/__tests__/address.test.d.ts +0 -1
  43. package/dist/capability-map/__tests__/address.test.js +0 -49
  44. package/dist/capability-map/__tests__/dsm-layout.test.d.ts +0 -1
  45. package/dist/capability-map/__tests__/dsm-layout.test.js +0 -46
  46. package/dist/capability-map/__tests__/dsm-mutations.test.d.ts +0 -1
  47. package/dist/capability-map/__tests__/dsm-mutations.test.js +0 -125
  48. package/dist/capability-map/__tests__/dsm-validate.test.d.ts +0 -1
  49. package/dist/capability-map/__tests__/dsm-validate.test.js +0 -56
  50. package/dist/capability-map/__tests__/validate.test.d.ts +0 -1
  51. package/dist/capability-map/__tests__/validate.test.js +0 -212
  52. package/dist/change/__tests__/validate.test.d.ts +0 -1
  53. package/dist/change/__tests__/validate.test.js +0 -79
  54. package/dist/codex/__tests__/example.test.d.ts +0 -1
  55. package/dist/codex/__tests__/example.test.js +0 -33
  56. package/dist/codex/__tests__/validate.test.d.ts +0 -1
  57. package/dist/codex/__tests__/validate.test.js +0 -68
  58. package/dist/compliance/__tests__/canon-catalog.test.d.ts +0 -1
  59. package/dist/compliance/__tests__/canon-catalog.test.js +0 -60
  60. package/dist/compliance/__tests__/classify.test.d.ts +0 -1
  61. package/dist/compliance/__tests__/classify.test.js +0 -58
  62. package/dist/compliance/__tests__/confidence.test.d.ts +0 -1
  63. package/dist/compliance/__tests__/confidence.test.js +0 -78
  64. package/dist/compliance/__tests__/coverage-metric.test.d.ts +0 -1
  65. package/dist/compliance/__tests__/coverage-metric.test.js +0 -264
  66. package/dist/compliance/__tests__/demo-scenario.test.d.ts +0 -1
  67. package/dist/compliance/__tests__/demo-scenario.test.js +0 -226
  68. package/dist/compliance/__tests__/example.test.d.ts +0 -1
  69. package/dist/compliance/__tests__/example.test.js +0 -51
  70. package/dist/compliance/__tests__/gap-report.test.d.ts +0 -1
  71. package/dist/compliance/__tests__/gap-report.test.js +0 -113
  72. package/dist/compliance/__tests__/html.test.d.ts +0 -1
  73. package/dist/compliance/__tests__/html.test.js +0 -122
  74. package/dist/compliance/__tests__/impact.test.d.ts +0 -1
  75. package/dist/compliance/__tests__/impact.test.js +0 -913
  76. package/dist/compliance/__tests__/markdown.test.d.ts +0 -1
  77. package/dist/compliance/__tests__/markdown.test.js +0 -85
  78. package/dist/compliance/__tests__/trace.test.d.ts +0 -1
  79. package/dist/compliance/__tests__/trace.test.js +0 -144
  80. package/dist/compliance/__tests__/view-resolution.test.d.ts +0 -1
  81. package/dist/compliance/__tests__/view-resolution.test.js +0 -59
  82. package/dist/compliance/__tests__/views.test.d.ts +0 -1
  83. package/dist/compliance/__tests__/views.test.js +0 -59
  84. package/dist/compliance-matrix/__tests__/build.test.d.ts +0 -1
  85. package/dist/compliance-matrix/__tests__/build.test.js +0 -183
  86. package/dist/compliance-matrix/__tests__/example.test.d.ts +0 -1
  87. package/dist/compliance-matrix/__tests__/example.test.js +0 -49
  88. package/dist/confidence/__tests__/score.test.d.ts +0 -1
  89. package/dist/confidence/__tests__/score.test.js +0 -188
  90. package/dist/constraint/__tests__/example.test.d.ts +0 -1
  91. package/dist/constraint/__tests__/example.test.js +0 -14
  92. package/dist/constraint/__tests__/validate.test.d.ts +0 -1
  93. package/dist/constraint/__tests__/validate.test.js +0 -58
  94. package/dist/factor/__tests__/validate.test.d.ts +0 -1
  95. package/dist/factor/__tests__/validate.test.js +0 -112
  96. package/dist/fgca/__tests__/chain-table.test.d.ts +0 -1
  97. package/dist/fgca/__tests__/chain-table.test.js +0 -158
  98. package/dist/fgca/__tests__/layout.test.d.ts +0 -1
  99. package/dist/fgca/__tests__/layout.test.js +0 -140
  100. package/dist/fgca/__tests__/parse-canonical.test.d.ts +0 -1
  101. package/dist/fgca/__tests__/parse-canonical.test.js +0 -268
  102. package/dist/fgca/__tests__/preview-layout.test.d.ts +0 -1
  103. package/dist/fgca/__tests__/preview-layout.test.js +0 -106
  104. package/dist/fgca/__tests__/resolver.test.d.ts +0 -1
  105. package/dist/fgca/__tests__/resolver.test.js +0 -185
  106. package/dist/fgca/__tests__/validate.test.d.ts +0 -1
  107. package/dist/fgca/__tests__/validate.test.js +0 -125
  108. package/dist/goals/__tests__/GoalTreeView.test.d.ts +0 -1
  109. package/dist/goals/__tests__/GoalTreeView.test.js +0 -337
  110. package/dist/goals/__tests__/layout.test.d.ts +0 -1
  111. package/dist/goals/__tests__/layout.test.js +0 -153
  112. package/dist/goals/__tests__/mutations.test.d.ts +0 -1
  113. package/dist/goals/__tests__/mutations.test.js +0 -134
  114. package/dist/goals/__tests__/parse-canonical.test.d.ts +0 -1
  115. package/dist/goals/__tests__/parse-canonical.test.js +0 -131
  116. package/dist/goals/__tests__/validate.test.d.ts +0 -1
  117. package/dist/goals/__tests__/validate.test.js +0 -183
  118. package/dist/integration/__tests__/validate.test.d.ts +0 -1
  119. package/dist/integration/__tests__/validate.test.js +0 -169
  120. package/dist/location/__tests__/validate.test.d.ts +0 -1
  121. package/dist/location/__tests__/validate.test.js +0 -107
  122. package/dist/node/__tests__/validate.test.d.ts +0 -1
  123. package/dist/node/__tests__/validate.test.js +0 -100
  124. package/dist/process-blueprint/__tests__/compliance-lane.test.d.ts +0 -1
  125. package/dist/process-blueprint/__tests__/compliance-lane.test.js +0 -272
  126. package/dist/process-blueprint/__tests__/layout.test.d.ts +0 -1
  127. package/dist/process-blueprint/__tests__/layout.test.js +0 -196
  128. package/dist/process-blueprint/__tests__/validate.test.d.ts +0 -1
  129. package/dist/process-blueprint/__tests__/validate.test.js +0 -280
  130. package/dist/process-map/__tests__/validate.test.d.ts +0 -1
  131. package/dist/process-map/__tests__/validate.test.js +0 -199
  132. package/dist/products/__tests__/validate.test.d.ts +0 -1
  133. package/dist/products/__tests__/validate.test.js +0 -210
  134. package/dist/repo-validate/__tests__/validate-repo.test.d.ts +0 -1
  135. package/dist/repo-validate/__tests__/validate-repo.test.js +0 -576
  136. package/dist/requirement/__tests__/example.test.d.ts +0 -1
  137. package/dist/requirement/__tests__/example.test.js +0 -21
  138. package/dist/requirement/__tests__/validate.test.d.ts +0 -1
  139. package/dist/requirement/__tests__/validate.test.js +0 -80
  140. package/dist/scenarios/__tests__/validate.test.d.ts +0 -1
  141. package/dist/scenarios/__tests__/validate.test.js +0 -183
  142. package/dist/stakeholder/__tests__/validate.test.d.ts +0 -1
  143. package/dist/stakeholder/__tests__/validate.test.js +0 -88
  144. package/dist/target-state/__tests__/validate.test.d.ts +0 -1
  145. package/dist/target-state/__tests__/validate.test.js +0 -78
  146. package/dist/technology-service/__tests__/validate.test.d.ts +0 -1
  147. package/dist/technology-service/__tests__/validate.test.js +0 -121
  148. package/dist/theme/__tests__/tokens.test.d.ts +0 -1
  149. package/dist/theme/__tests__/tokens.test.js +0 -159
  150. package/dist/webview/__tests__/entity-text-layout.test.d.ts +0 -1
  151. package/dist/webview/__tests__/entity-text-layout.test.js +0 -155
  152. package/dist/webview/__tests__/entry-notations.test.d.ts +0 -1
  153. package/dist/webview/__tests__/entry-notations.test.js +0 -62
  154. package/dist/webview/__tests__/entry.test.d.ts +0 -1
  155. package/dist/webview/__tests__/entry.test.js +0 -56
  156. package/dist/webview/__tests__/render-activities.test.d.ts +0 -1
  157. package/dist/webview/__tests__/render-activities.test.js +0 -91
  158. package/dist/webview/__tests__/render-blocks.test.d.ts +0 -1
  159. package/dist/webview/__tests__/render-blocks.test.js +0 -175
  160. package/dist/webview/__tests__/render-goals.test.d.ts +0 -1
  161. package/dist/webview/__tests__/render-goals.test.js +0 -120
  162. package/dist/webview/__tests__/render-process.test.d.ts +0 -1
  163. package/dist/webview/__tests__/render-process.test.js +0 -463
  164. package/dist/webview/__tests__/render-util.test.d.ts +0 -1
  165. package/dist/webview/__tests__/render-util.test.js +0 -54
  166. package/src/__tests__/edge-path.test.ts +0 -67
  167. package/src/__tests__/node-size-presets.test.ts +0 -50
  168. package/src/__tests__/scope.test.ts +0 -27
  169. package/src/__tests__/yaml-normalize.test.ts +0 -158
  170. package/src/activities/__tests__/cpm.test.ts +0 -164
  171. package/src/activities/__tests__/gantt.test.ts +0 -290
  172. package/src/activities/__tests__/layout.test.ts +0 -112
  173. package/src/activities/__tests__/validate.test.ts +0 -446
  174. package/src/activity-card/__tests__/example.test.ts +0 -83
  175. package/src/activity-card/__tests__/layout.test.ts +0 -162
  176. package/src/activity-card/__tests__/resolver.test.ts +0 -357
  177. package/src/activity-card/__tests__/validate.test.ts +0 -167
  178. package/src/actor/__tests__/validate.test.ts +0 -114
  179. package/src/applications/__tests__/validate.test.ts +0 -256
  180. package/src/assertion/__tests__/example.test.ts +0 -37
  181. package/src/assertion/__tests__/validate.test.ts +0 -142
  182. package/src/blocks/__tests__/layout.test.ts +0 -169
  183. package/src/blocks/__tests__/validate.test.ts +0 -260
  184. package/src/business-service/__tests__/validate.test.ts +0 -160
  185. package/src/capability-map/__tests__/CapabilityMapView.test.tsx +0 -232
  186. package/src/capability-map/__tests__/address.test.ts +0 -59
  187. package/src/capability-map/__tests__/dsm-layout.test.ts +0 -52
  188. package/src/capability-map/__tests__/dsm-mutations.test.ts +0 -141
  189. package/src/capability-map/__tests__/dsm-validate.test.ts +0 -65
  190. package/src/capability-map/__tests__/validate.test.ts +0 -238
  191. package/src/change/__tests__/validate.test.ts +0 -97
  192. package/src/codex/__tests__/example.test.ts +0 -40
  193. package/src/codex/__tests__/validate.test.ts +0 -84
  194. package/src/compliance/__tests__/canon-catalog.test.ts +0 -68
  195. package/src/compliance/__tests__/classify.test.ts +0 -64
  196. package/src/compliance/__tests__/confidence.test.ts +0 -98
  197. package/src/compliance/__tests__/coverage-metric.test.ts +0 -300
  198. package/src/compliance/__tests__/demo-scenario.test.ts +0 -273
  199. package/src/compliance/__tests__/example.test.ts +0 -57
  200. package/src/compliance/__tests__/gap-report.test.ts +0 -129
  201. package/src/compliance/__tests__/html.test.ts +0 -142
  202. package/src/compliance/__tests__/impact.test.ts +0 -1060
  203. package/src/compliance/__tests__/markdown.test.ts +0 -95
  204. package/src/compliance/__tests__/trace.test.ts +0 -157
  205. package/src/compliance/__tests__/view-resolution.test.ts +0 -80
  206. package/src/compliance/__tests__/views.test.ts +0 -64
  207. package/src/compliance-matrix/__tests__/build.test.ts +0 -200
  208. package/src/compliance-matrix/__tests__/example.test.ts +0 -57
  209. package/src/confidence/__tests__/score.test.ts +0 -254
  210. package/src/constraint/__tests__/example.test.ts +0 -19
  211. package/src/constraint/__tests__/validate.test.ts +0 -68
  212. package/src/factor/__tests__/validate.test.ts +0 -133
  213. package/src/fgca/__tests__/chain-table.test.ts +0 -172
  214. package/src/fgca/__tests__/layout.test.ts +0 -179
  215. package/src/fgca/__tests__/parse-canonical.test.ts +0 -296
  216. package/src/fgca/__tests__/preview-layout.test.ts +0 -124
  217. package/src/fgca/__tests__/resolver.test.ts +0 -209
  218. package/src/fgca/__tests__/validate.test.ts +0 -142
  219. package/src/goals/__tests__/GoalTreeView.test.tsx +0 -361
  220. package/src/goals/__tests__/layout.test.ts +0 -175
  221. package/src/goals/__tests__/mutations.test.ts +0 -153
  222. package/src/goals/__tests__/parse-canonical.test.ts +0 -147
  223. package/src/goals/__tests__/validate.test.ts +0 -202
  224. package/src/integration/__tests__/validate.test.ts +0 -203
  225. package/src/location/__tests__/validate.test.ts +0 -127
  226. package/src/node/__tests__/validate.test.ts +0 -118
  227. package/src/process-blueprint/__tests__/compliance-lane.test.ts +0 -320
  228. package/src/process-blueprint/__tests__/layout.test.ts +0 -240
  229. package/src/process-blueprint/__tests__/validate.test.ts +0 -310
  230. package/src/process-map/__tests__/validate.test.ts +0 -223
  231. package/src/products/__tests__/validate.test.ts +0 -239
  232. package/src/repo-validate/__tests__/validate-repo.test.ts +0 -699
  233. package/src/requirement/__tests__/example.test.ts +0 -24
  234. package/src/requirement/__tests__/validate.test.ts +0 -96
  235. package/src/scenarios/__tests__/validate.test.ts +0 -209
  236. package/src/stakeholder/__tests__/validate.test.ts +0 -105
  237. package/src/target-state/__tests__/validate.test.ts +0 -95
  238. package/src/technology-service/__tests__/validate.test.ts +0 -143
  239. package/src/theme/__tests__/tokens.test.ts +0 -185
  240. package/src/webview/__tests__/entity-text-layout.test.ts +0 -182
  241. package/src/webview/__tests__/entry-notations.test.ts +0 -77
  242. package/src/webview/__tests__/entry.test.ts +0 -64
  243. package/src/webview/__tests__/render-activities.test.ts +0 -100
  244. package/src/webview/__tests__/render-blocks.test.ts +0 -206
  245. package/src/webview/__tests__/render-goals.test.ts +0 -128
  246. package/src/webview/__tests__/render-process.test.ts +0 -538
  247. package/src/webview/__tests__/render-util.test.ts +0 -66
@@ -1,185 +0,0 @@
1
- /**
2
- * Brand default-theme regression tests (hub issue #548).
3
- *
4
- * Locks in the three-role brand mapping from `brand/transitrix_brand.md`:
5
- * - Petrol = structure (node/edge stroke) and hierarchy depth (fill tint).
6
- * - A depth ramp never switches hue — only lightness/saturation move.
7
- * - Text stays WCAG-AA readable against every fill it can be drawn on.
8
- * `hc` (VS Code high-contrast) is intentionally excluded — it's an
9
- * accessibility override, not one of the three brand-contract themes.
10
- */
11
- import { describe, expect, it } from 'vitest';
12
- import {
13
- BRAND,
14
- BRAND_EMPHASIS,
15
- LAYER_COLORS,
16
- LEVEL_COLORS,
17
- MATURITY_COLORS,
18
- TREE_MATURITY_COLORS,
19
- STRUCTURAL,
20
- TREE_LEVEL_COLORS,
21
- } from '../tokens.js';
22
-
23
- function hexToRgb(hex: string): [number, number, number] {
24
- const n = hex.replace('#', '');
25
- return [0, 2, 4].map((i) => parseInt(n.slice(i, i + 2), 16)) as [number, number, number];
26
- }
27
-
28
- function relativeLuminance([r, g, b]: [number, number, number]): number {
29
- const f = (v: number): number => {
30
- const c = v / 255;
31
- return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
32
- };
33
- const [R, G, B] = [f(r), f(g), f(b)];
34
- return 0.2126 * R + 0.7152 * G + 0.0722 * B;
35
- }
36
-
37
- /** WCAG 2.x contrast ratio between two hex colors (1..21). */
38
- function contrast(a: string, b: string): number {
39
- const la = relativeLuminance(hexToRgb(a));
40
- const lb = relativeLuminance(hexToRgb(b));
41
- const [hi, lo] = la > lb ? [la, lb] : [lb, la];
42
- return (hi + 0.05) / (lo + 0.05);
43
- }
44
-
45
- /** Hue in degrees (0-360) from a hex color; undefined (grey) hues return -1. */
46
- function hue(hex: string): number {
47
- const [r, g, b] = hexToRgb(hex).map((v) => v / 255);
48
- const max = Math.max(r, g, b), min = Math.min(r, g, b);
49
- const delta = max - min;
50
- if (delta < 0.001) return -1; // achromatic
51
- let h: number;
52
- if (max === r) h = ((g - b) / delta) % 6;
53
- else if (max === g) h = (b - r) / delta + 2;
54
- else h = (r - g) / delta + 4;
55
- h *= 60;
56
- return h < 0 ? h + 360 : h;
57
- }
58
-
59
- const PETROL_HUE = hue(BRAND.petrol);
60
- const HUE_TOLERANCE = 6; // degrees — rounding slop from hand-tuned hex values
61
-
62
- function expectSameHueFamily(hexes: string[], label: string): void {
63
- for (const hex of hexes) {
64
- const h = hue(hex);
65
- const diff = Math.min(Math.abs(h - PETROL_HUE), 360 - Math.abs(h - PETROL_HUE));
66
- expect(diff, `${label} ${hex} should stay in the petrol hue family (got hue ${h.toFixed(1)}, petrol is ${PETROL_HUE.toFixed(1)})`).toBeLessThanOrEqual(HUE_TOLERANCE);
67
- }
68
- }
69
-
70
- // Minimum AA text-contrast ratios this design targets. Primary/secondary text
71
- // is small (10-12px) so we hold the full 4.5:1 normal-text bar for the name
72
- // label, and the calibrated ~4.0 bar the pre-brand palette already shipped
73
- // for the muted secondary/id label (see brand-ramp calibration in PR #548).
74
- const MIN_PRIMARY_CONTRAST = 4.5;
75
- const MIN_SECONDARY_CONTRAST = 4.0;
76
-
77
- describe('brand default theme (#548) — structure is petrol', () => {
78
- it('nodeStroke and edgeStroke are brand petrol in the light theme', () => {
79
- expect(STRUCTURAL.nodeStroke.light).toBe(BRAND.petrol);
80
- expect(STRUCTURAL.edgeStroke.light).toBe(BRAND.petrol);
81
- });
82
-
83
- it('nodeStroke/edgeStroke stay in the petrol hue family in the dark theme', () => {
84
- expectSameHueFamily([STRUCTURAL.nodeStroke.dark, STRUCTURAL.edgeStroke.dark], 'dark stroke');
85
- });
86
- });
87
-
88
- describe('brand default theme (#548) — tint depth never switches hue', () => {
89
- it('LEVEL_COLORS light ramp stays in the petrol hue family', () => {
90
- expectSameHueFamily([...LEVEL_COLORS.light], 'LEVEL light');
91
- });
92
- it('LEVEL_COLORS dark ramp stays in the petrol hue family', () => {
93
- expectSameHueFamily([...LEVEL_COLORS.dark], 'LEVEL dark');
94
- });
95
- it('LAYER_COLORS (DGCA/DGA columns) stay in the petrol hue family, both themes', () => {
96
- const keys = ['driver', 'factor', 'goal', 'change', 'activity'] as const;
97
- expectSameHueFamily(keys.map((k) => LAYER_COLORS[k].light), 'LAYER light');
98
- expectSameHueFamily(keys.map((k) => LAYER_COLORS[k].dark), 'LAYER dark');
99
- });
100
- it('TREE_LEVEL_COLORS (capability map bands) stay in the petrol hue family, both themes', () => {
101
- const keys = ['band0', 'band1', 'band2'] as const;
102
- expectSameHueFamily(keys.map((k) => TREE_LEVEL_COLORS[k].light), 'BAND light');
103
- expectSameHueFamily(keys.map((k) => TREE_LEVEL_COLORS[k].dark), 'BAND dark');
104
- });
105
- });
106
-
107
- describe('brand default theme (#548) — WCAG-AA text-on-fill contrast', () => {
108
- function checkFills(label: string, fills: string[], textPrimary: string, textSecondary: string): void {
109
- for (const fill of fills) {
110
- expect(contrast(fill, textPrimary), `${label} ${fill} vs text-primary`).toBeGreaterThanOrEqual(MIN_PRIMARY_CONTRAST);
111
- expect(contrast(fill, textSecondary), `${label} ${fill} vs text-secondary`).toBeGreaterThanOrEqual(MIN_SECONDARY_CONTRAST);
112
- }
113
- }
114
-
115
- it('LEVEL_COLORS — every slot, both themes', () => {
116
- checkFills('LEVEL light', [...LEVEL_COLORS.light], STRUCTURAL.textPrimary.light, STRUCTURAL.textSecondary.light);
117
- checkFills('LEVEL dark', [...LEVEL_COLORS.dark], STRUCTURAL.textPrimary.dark, STRUCTURAL.textSecondary.dark);
118
- });
119
-
120
- it('LAYER_COLORS — every column, both themes', () => {
121
- const keys = ['driver', 'factor', 'goal', 'change', 'activity'] as const;
122
- checkFills('LAYER light', keys.map((k) => LAYER_COLORS[k].light), STRUCTURAL.textPrimary.light, STRUCTURAL.textSecondary.light);
123
- checkFills('LAYER dark', keys.map((k) => LAYER_COLORS[k].dark), STRUCTURAL.textPrimary.dark, STRUCTURAL.textSecondary.dark);
124
- });
125
-
126
- it('TREE_LEVEL_COLORS — every band, both themes', () => {
127
- const keys = ['band0', 'band1', 'band2'] as const;
128
- checkFills('BAND light', keys.map((k) => TREE_LEVEL_COLORS[k].light), STRUCTURAL.textPrimary.light, STRUCTURAL.textSecondary.light);
129
- checkFills('BAND dark', keys.map((k) => TREE_LEVEL_COLORS[k].dark), STRUCTURAL.textPrimary.dark, STRUCTURAL.textSecondary.dark);
130
- });
131
-
132
- it('textPrimary/textSecondary stay readable against the shell background', () => {
133
- expect(contrast(STRUCTURAL.textPrimary.light, '#ffffff')).toBeGreaterThanOrEqual(MIN_PRIMARY_CONTRAST);
134
- expect(contrast(STRUCTURAL.textPrimary.dark, '#0a1628')).toBeGreaterThanOrEqual(MIN_PRIMARY_CONTRAST);
135
- });
136
- });
137
-
138
- describe('brand default theme (#548) — amber/orange emphasis stays distinct from functional colors', () => {
139
- it('does not reuse brand amber/orange for functional status', () => {
140
- // FUNCTIONAL warning/error must remain visually distinct from the brand
141
- // accents so a red error is never confused with brand orange, per
142
- // brand/transitrix_brand.md "Don't reuse brand orange as semantic error".
143
- expect(BRAND_EMPHASIS.orange.light).toBe(BRAND.orange);
144
- expect(BRAND_EMPHASIS.amber.light).toBe(BRAND.amber);
145
- });
146
-
147
- it('critical-path emphasis tint pairs with the orange stroke, both themes', () => {
148
- expect(contrast(BRAND_EMPHASIS.orange.light, BRAND_EMPHASIS.orangeTint.light)).toBeGreaterThan(1.5);
149
- expect(contrast(BRAND_EMPHASIS.orange.dark, BRAND_EMPHASIS.orangeTint.dark)).toBeGreaterThan(1.5);
150
- });
151
- });
152
-
153
- describe('maturity Likert ramp — one harmonious scale, white-label contrast', () => {
154
- // .maturity-pill / tree-maturity badges hardcode white label text
155
- // (capability-map-preview.ts, render-capability-tree.ts) regardless of
156
- // theme, so every step needs AA (light/dark) or AAA (hc, since VS Code
157
- // high-contrast mode exists specifically to demand stronger contrast)
158
- // against white — not against the page's own text-primary/secondary.
159
- const MIN_WHITE_CONTRAST_AA = 4.5;
160
- const MIN_WHITE_CONTRAST_AAA = 7;
161
-
162
- it('MATURITY_COLORS — every step reads white text at AA, light + dark', () => {
163
- for (const hex of MATURITY_COLORS.light) {
164
- expect(contrast(hex, '#ffffff'), `MATURITY light ${hex} vs white`).toBeGreaterThanOrEqual(MIN_WHITE_CONTRAST_AA);
165
- }
166
- for (const hex of MATURITY_COLORS.dark) {
167
- expect(contrast(hex, '#ffffff'), `MATURITY dark ${hex} vs white`).toBeGreaterThanOrEqual(MIN_WHITE_CONTRAST_AA);
168
- }
169
- });
170
-
171
- it('MATURITY_COLORS hc — every step reads white text at AAA', () => {
172
- for (const hex of MATURITY_COLORS.hc) {
173
- expect(contrast(hex, '#ffffff'), `MATURITY hc ${hex} vs white`).toBeGreaterThanOrEqual(MIN_WHITE_CONTRAST_AAA);
174
- }
175
- });
176
-
177
- it('TREE_MATURITY_COLORS is the same harmonious ramp as MATURITY_COLORS', () => {
178
- // Previously a divergent DSM-matching pink/yellow/blue set (plus a grey
179
- // L1, an odd "no signal" hue for the worst rating) — "maturity" now
180
- // means the same colors everywhere it's shown.
181
- expect(TREE_MATURITY_COLORS.light).toEqual(MATURITY_COLORS.light);
182
- expect(TREE_MATURITY_COLORS.dark).toEqual(MATURITY_COLORS.dark);
183
- expect(TREE_MATURITY_COLORS.hc).toEqual(MATURITY_COLORS.hc);
184
- });
185
- });
@@ -1,182 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import {
3
- layoutCenteredEntityText,
4
- layoutLeafBlockText,
5
- layoutHeaderBlockText,
6
- LINE_H_PRIMARY,
7
- LINE_H_SECONDARY,
8
- TEXT_MARGIN_Y,
9
- maxCharsForInnerWidth,
10
- ROW_GROUP_GAP,
11
- wrapWords,
12
- wrapTextLines,
13
- } from '../entity-text-layout.js';
14
- import { ENTITY_NODE_SIZE } from '../../node-size-presets.js';
15
-
16
- describe('entity-text-layout', () => {
17
- it('wrapWords respects max lines and truncates with ellipsis', () => {
18
- const long = 'one two three four five six seven eight nine ten eleven twelve';
19
- const lines = wrapWords(long, 12, 2);
20
- expect(lines.length).toBeLessThanOrEqual(2);
21
- expect(lines[lines.length - 1]).toMatch(/…$/);
22
- });
23
-
24
- it('maxCharsForInnerWidth scales with inner width', () => {
25
- expect(maxCharsForInnerWidth(160, 7)).toBeGreaterThan(maxCharsForInnerWidth(120, 7));
26
- });
27
-
28
- it('layoutCenteredEntityText keeps name and type labels separated', () => {
29
- const specs = layoutCenteredEntityText({
30
- boxX: 0,
31
- boxY: 0,
32
- boxWidth: 250,
33
- boxHeight: 80,
34
- name: 'Grow enterprise value',
35
- type: 'Strategic',
36
- id: 'GOAL-VALUE-1',
37
- nameMaxLines: 2,
38
- idMaxLines: 1,
39
- });
40
- const nameY = specs.find((l) => l.cls === 'text-primary')!.y;
41
- const typeY = specs.find((l) => l.cls === 'text-secondary')!.y;
42
- const minGap = typeY - nameY - LINE_H_PRIMARY / 2 - LINE_H_SECONDARY / 2;
43
- expect(minGap).toBeGreaterThanOrEqual(ROW_GROUP_GAP - 1);
44
- });
45
-
46
- it('layoutCenteredEntityText emits more name capacity on wide boxes', () => {
47
- const narrow = layoutCenteredEntityText({
48
- boxX: 0,
49
- boxY: 0,
50
- boxWidth: 200,
51
- boxHeight: 80,
52
- name: 'Improve customer onboarding experience across regions',
53
- id: 'GOAL-001',
54
- nameMaxLines: 2,
55
- idMaxLines: 1,
56
- });
57
- const wide = layoutCenteredEntityText({
58
- boxX: 0,
59
- boxY: 0,
60
- boxWidth: 320,
61
- boxHeight: 80,
62
- name: 'Improve customer onboarding experience across regions',
63
- id: 'GOAL-001',
64
- nameMaxLines: 2,
65
- idMaxLines: 1,
66
- });
67
- const narrowPrimary = narrow.filter((l) => l.cls === 'text-primary').map((l) => l.text).join(' ');
68
- const widePrimary = wide.filter((l) => l.cls === 'text-primary').map((l) => l.text).join(' ');
69
- expect(widePrimary.replace(/…/g, '').length).toBeGreaterThanOrEqual(narrowPrimary.replace(/…/g, '').length);
70
- });
71
-
72
- it('wrapTextLines hard-breaks oversized tokens (blueprint cells)', () => {
73
- const lines = wrapTextLines('supercalifragilisticexpialidocious word', 10, 3);
74
- expect(lines.length).toBeGreaterThan(1);
75
- expect(lines.every((l) => l.length <= 10)).toBe(true);
76
- });
77
- });
78
-
79
- // Approximate half-extent (px) of each text class's glyphs around its centre
80
- // y — font-size / 2, the same heuristic the render-blocks overlap regression
81
- // test (#419) uses. Used below to assert text never visually crosses the box
82
- // border, not just that the *nominal* line-height budget fits.
83
- const HALF_EXTENT: Record<string, number> = {
84
- 'text-primary': 6, // 12px font
85
- 'text-secondary': 6, // 11px font, rounded up
86
- 'text-id': 5, // 10px font
87
- };
88
-
89
- describe('entity text layout — never overflows the box, always keeps padding', () => {
90
- const PRESETS = Object.entries(ENTITY_NODE_SIZE); // compact/normal/wide
91
- const NAMES = [
92
- 'Short',
93
- 'Achieve full GDPR & NIS2 compliance',
94
- 'A name so long it would need three or more lines to render in full without any height limit at all',
95
- ];
96
-
97
- function assertNoOverflowAndPadded(
98
- specs: { cls: string; y: number }[],
99
- boxTop: number,
100
- boxBottom: number,
101
- ): void {
102
- for (const s of specs) {
103
- const half = HALF_EXTENT[s.cls] ?? 6;
104
- expect(s.y - half).toBeGreaterThanOrEqual(boxTop);
105
- expect(s.y + half).toBeLessThanOrEqual(boxBottom);
106
- }
107
- }
108
-
109
- for (const [presetName, size] of PRESETS) {
110
- for (const withType of [false, true]) {
111
- for (const name of NAMES) {
112
- it(`layoutCenteredEntityText: ${presetName} preset, type=${withType}, name="${name.slice(0, 20)}…" stays inside the box`, () => {
113
- const boxY = 0;
114
- const specs = layoutCenteredEntityText({
115
- boxX: 0,
116
- boxY,
117
- boxWidth: size.width,
118
- boxHeight: size.height,
119
- name,
120
- type: withType ? 'Strategic Goal' : undefined,
121
- id: 'GOAL-EU-COMPLIANCE-1',
122
- nameMaxLines: 2,
123
- idMaxLines: 1,
124
- });
125
- assertNoOverflowAndPadded(specs, boxY, boxY + size.height);
126
- });
127
- }
128
- }
129
- }
130
-
131
- it('reproduces the reported case (wide preset, 2-line name + type + long id) with real padding on all sides', () => {
132
- const size = ENTITY_NODE_SIZE.normal;
133
- const boxY = 100;
134
- const specs = layoutCenteredEntityText({
135
- boxX: 0,
136
- boxY,
137
- boxWidth: size.width,
138
- boxHeight: size.height,
139
- name: 'Achieve full GDPR & NIS2 compliance',
140
- type: 'Project Goal',
141
- id: 'GOAL-EU-COMPLIANCE-1',
142
- nameMaxLines: 2,
143
- idMaxLines: 1,
144
- });
145
- const nameLines = specs.filter((s) => s.cls === 'text-primary');
146
- expect(nameLines.length).toBe(2); // normal preset is tall enough to keep both lines
147
- assertNoOverflowAndPadded(specs, boxY, boxY + size.height);
148
- // Explicit padding, not just "doesn't cross the border".
149
- const firstTop = Math.min(...specs.map((s) => s.y - (HALF_EXTENT[s.cls] ?? 6)));
150
- const lastBottom = Math.max(...specs.map((s) => s.y + (HALF_EXTENT[s.cls] ?? 6)));
151
- expect(firstTop - boxY).toBeGreaterThanOrEqual(TEXT_MARGIN_Y);
152
- expect(boxY + size.height - lastBottom).toBeGreaterThanOrEqual(TEXT_MARGIN_Y);
153
- });
154
-
155
- it('layoutLeafBlockText stays inside the box for compact preset with a long name and id', () => {
156
- const size = ENTITY_NODE_SIZE.compact;
157
- const boxY = 0;
158
- const specs = layoutLeafBlockText({
159
- boxX: 0,
160
- boxY,
161
- boxWidth: size.width,
162
- boxHeight: size.height,
163
- name: 'Personal data records that linger after consent withdrawal across many systems and archives',
164
- id: 'VERY_LONG_BLOCK_IDENTIFIER_WITH_MANY_SEGMENTS_AND_MORE',
165
- });
166
- assertNoOverflowAndPadded(specs, boxY, boxY + size.height);
167
- });
168
-
169
- it('layoutHeaderBlockText stays inside a short header strip', () => {
170
- const boxY = 0;
171
- const headerHeight = 28;
172
- const specs = layoutHeaderBlockText({
173
- boxX: 0,
174
- boxY,
175
- boxWidth: 250,
176
- headerHeight,
177
- name: 'A fairly long container name here',
178
- id: 'VERY_LONG_CONTAINER_IDENTIFIER',
179
- });
180
- assertNoOverflowAndPadded(specs, boxY, boxY + headerHeight);
181
- });
182
- });
@@ -1,77 +0,0 @@
1
- /**
2
- * Step 4 parity check: every supported notation must render through the
3
- * webview bundle's `render(kind, source)` API from the same example fixtures
4
- * the VS Code previews consume — a valid document yields `status: 'ok'` with
5
- * non-empty markup, and a malformed document degrades to the error panel
6
- * (structured errors, no thrown exception reaching the JVM host).
7
- */
8
- import { readFileSync } from 'node:fs';
9
- import path from 'node:path';
10
- import { fileURLToPath } from 'node:url';
11
-
12
- import { describe, expect, it } from 'vitest';
13
-
14
- import { api, render, type NotationKind } from '../entry.js';
15
-
16
- const EXAMPLES = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../../tests/fixtures/notation-corpus');
17
-
18
- interface Fixture {
19
- kind: NotationKind;
20
- file: string;
21
- /** The markup wrapper the renderer emits: an <svg> diagram or an HTML <section>. */
22
- markup: 'svg' | 'section';
23
- }
24
-
25
- // Mirrors the fixtures used by the VS Code previews. One representative
26
- // example per notation is enough to prove the dispatch + renderer wiring.
27
- const FIXTURES: Fixture[] = [
28
- { kind: 'goals', file: 'goals/strategy-2026.goals.transitrix.yaml', markup: 'svg' },
29
- { kind: 'dgca', file: 'dgca/strategy-2026.dgca.transitrix.yaml', markup: 'svg' },
30
- { kind: 'dga', file: 'dga/strategy-2026.dga.transitrix.yaml', markup: 'svg' },
31
- { kind: 'action', file: 'action/platform-launch.action.transitrix.yaml', markup: 'svg' },
32
- { kind: 'action-card', file: 'action-card/eu-programme.action-card.transitrix.yaml', markup: 'svg' },
33
- { kind: 'process-blueprint', file: 'process-blueprint/order-fulfilment.process-blueprint.transitrix.yaml', markup: 'svg' },
34
- { kind: 'blocks', file: 'blocks/architecture.blocks.transitrix.yaml', markup: 'svg' },
35
- { kind: 'applications', file: 'applications/portfolio-2026.applications.transitrix.yaml', markup: 'section' },
36
- { kind: 'products', file: 'products/portfolio-2026.products.transitrix.yaml', markup: 'section' },
37
- { kind: 'process-map', file: 'process-map/enterprise.process-map.transitrix.yaml', markup: 'section' },
38
- { kind: 'scenarios', file: 'scenarios/omnichannel-2028.scenarios.transitrix.yaml', markup: 'section' },
39
- { kind: 'capability-map', file: 'capability-map/business.capability-map.transitrix.yaml', markup: 'section' },
40
- ];
41
-
42
- describe('webview/entry — Step 4 notation coverage', () => {
43
- it('wires every supported kind (no kind left on NOTATION-NOT-WIRED)', () => {
44
- // The fixture table must cover the full supported-kinds surface, so a new
45
- // kind added to the bundle without a renderer fails this test loudly.
46
- const covered = new Set(FIXTURES.map((f) => f.kind));
47
- for (const kind of api.supportedKinds) {
48
- expect(covered.has(kind)).toBe(true);
49
- }
50
- });
51
-
52
- for (const fx of FIXTURES) {
53
- it(`renders ${fx.kind} from its example fixture`, () => {
54
- const source = readFileSync(path.join(EXAMPLES, fx.file), 'utf8');
55
- const r = render(fx.kind, source);
56
- expect(r.notation).toBe(fx.kind);
57
- expect(r.errors).toEqual([]);
58
- expect(r.status).toBe('ok');
59
- expect(r.svg.length).toBeGreaterThan(0);
60
- expect(r.svg).toContain(fx.markup === 'svg' ? '<svg ' : '<section');
61
- });
62
- }
63
-
64
- it('degrades malformed input to the error panel for a diagram notation', () => {
65
- const r = render('process-blueprint', 'process_blueprint:\n stages: "not a list"\n');
66
- expect(r.status).toBe('error');
67
- expect(r.errors.length).toBeGreaterThan(0);
68
- expect(r.svg).toBe('');
69
- });
70
-
71
- it('degrades malformed input to the error panel for a catalogue notation', () => {
72
- const r = render('applications', 'applications_catalogue:\n applications: "not a list"\n');
73
- expect(r.status).toBe('error');
74
- expect(r.errors.length).toBeGreaterThan(0);
75
- expect(r.svg).toBe('');
76
- });
77
- });
@@ -1,64 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { api, render } from '../entry.js';
4
-
5
- const VALID_GOALS_DOC = `
6
- notation: goals
7
- id: GOALS-WEBVIEW-1
8
- name: "Webview entry smoke test"
9
- goal_types:
10
- - { name: "Strategy", level: 0 }
11
- goals:
12
- - id: GOAL-MOON-1
13
- name: "Reach the moon"
14
- type: "Strategy"
15
- level: 0
16
- `;
17
-
18
- describe('webview/entry — Step 2 host API', () => {
19
- it('exposes a version + supportedKinds + render() trio', () => {
20
- expect(api.version).toMatch(/^\d+\.\d+\.\d+/);
21
- expect(api.supportedKinds).toContain('goals');
22
- expect(typeof api.render).toBe('function');
23
- });
24
-
25
- it('parses + validates + renders SVG for a well-formed goals document', () => {
26
- const r = render('goals', VALID_GOALS_DOC);
27
- expect(r.notation).toBe('goals');
28
- expect(r.status).toBe('ok');
29
- expect(r.errors).toEqual([]);
30
- // Step 3 wires the goals renderer — SVG must now be non-empty and contain
31
- // the goal name from the YAML.
32
- expect(r.svg).toContain('<svg ');
33
- expect(r.svg).toContain('Reach the moon');
34
- });
35
-
36
- it('returns YAML-PARSE on malformed input — no exception escapes to the host', () => {
37
- const r = render('goals', 'goals:\n - id: g1\n name: [unterminated');
38
- expect(r.status).toBe('error');
39
- expect(r.errors[0].code).toBe('YAML-PARSE');
40
- });
41
-
42
- it('returns KIND-UNKNOWN for a non-Transitrix notation kind', () => {
43
- const r = render('plantuml', VALID_GOALS_DOC);
44
- expect(r.status).toBe('error');
45
- expect(r.errors[0].code).toBe('KIND-UNKNOWN');
46
- });
47
-
48
- it('routes non-goals kinds to their validator after Step 4 (no NOTATION-NOT-WIRED)', () => {
49
- // Every supported kind is now wired to its validator + renderer. A
50
- // malformed dgca document must surface a structured validation error from
51
- // the dgca validator — never the Step-2/3 "not wired" placeholder.
52
- const r = render('dgca', 'changes: []\n');
53
- expect(r.status).toBe('error');
54
- expect(r.errors.length).toBeGreaterThan(0);
55
- expect(r.errors.every((e) => e.code !== 'NOTATION-NOT-WIRED')).toBe(true);
56
- });
57
-
58
- it('surfaces goals validation errors structurally (e.g. missing notation header)', () => {
59
- const r = render('goals', 'goals: []\n');
60
- expect(r.status).toBe('error');
61
- // parseCanonicalGoals emits GOALS-* codes for canonical-shape violations.
62
- expect(r.errors.some((e) => e.code.startsWith('GOALS-'))).toBe(true);
63
- });
64
- });
@@ -1,100 +0,0 @@
1
- /**
2
- * Unit tests for renderActivitiesSvg — project-node suppression (#421).
3
- *
4
- * Convention: activities with activity_type === 'project' are omitted from
5
- * the Network/PSND layout by default (suppressProjectNodes: true). Text/Tree
6
- * views keep them via the caller; data is never mutated.
7
- */
8
- import { describe, it, expect } from 'vitest';
9
- import { renderActivitiesSvg } from '../render-activities.js';
10
- import type { ActivityDoc } from '../../activities/types.js';
11
-
12
- const BASE_DOC: ActivityDoc = {
13
- notation: 'action',
14
- activities: [
15
- {
16
- id: 'PROJ-1',
17
- name: 'Platform Launch',
18
- activity_type: 'project',
19
- },
20
- {
21
- id: 'TASK-1',
22
- name: 'Design',
23
- activity_type: 'task',
24
- parent: 'PROJ-1',
25
- },
26
- {
27
- id: 'TASK-2',
28
- name: 'Implement',
29
- activity_type: 'task',
30
- parent: 'PROJ-1',
31
- predecessors: ['TASK-1'],
32
- },
33
- ],
34
- };
35
-
36
- describe('renderActivitiesSvg — project-node suppression (#421)', () => {
37
- it('suppresses project-type nodes in the network SVG by default', () => {
38
- const svg = renderActivitiesSvg(BASE_DOC);
39
- // TASK-1 and TASK-2 should appear as rendered nodes.
40
- expect(svg).toContain('TASK-1');
41
- expect(svg).toContain('TASK-2');
42
- // The project container should not appear as a rendered node.
43
- expect(svg).not.toContain('PROJ-1');
44
- });
45
-
46
- it('suppresses project nodes when suppressProjectNodes is explicitly true', () => {
47
- const svg = renderActivitiesSvg(BASE_DOC, { suppressProjectNodes: true });
48
- expect(svg).not.toContain('PROJ-1');
49
- expect(svg).toContain('TASK-1');
50
- });
51
-
52
- it('renders project nodes when suppressProjectNodes is false', () => {
53
- const svg = renderActivitiesSvg(BASE_DOC, { suppressProjectNodes: false });
54
- expect(svg).toContain('PROJ-1');
55
- expect(svg).toContain('TASK-1');
56
- expect(svg).toContain('TASK-2');
57
- });
58
-
59
- it('does not mutate the original doc', () => {
60
- const original = BASE_DOC.activities.length;
61
- renderActivitiesSvg(BASE_DOC);
62
- expect(BASE_DOC.activities).toHaveLength(original);
63
- });
64
-
65
- it('suppresses project_type matching case-insensitively', () => {
66
- const doc: ActivityDoc = {
67
- notation: 'action',
68
- activities: [
69
- { id: 'P1', name: 'Container', activity_type: 'Project' },
70
- { id: 'T1', name: 'Leaf', activity_type: 'task' },
71
- ],
72
- };
73
- const svg = renderActivitiesSvg(doc);
74
- expect(svg).not.toContain('P1');
75
- expect(svg).toContain('T1');
76
- });
77
-
78
- it('renders a valid SVG when all activities are project-type (graceful degrade)', () => {
79
- const doc: ActivityDoc = {
80
- notation: 'action',
81
- activities: [{ id: 'P1', name: 'Only project', activity_type: 'project' }],
82
- };
83
- const svg = renderActivitiesSvg(doc);
84
- // When all activities are filtered out, the renderer returns an empty SVG.
85
- expect(svg).toContain('<svg');
86
- });
87
-
88
- it('renders activities without activity_type unchanged', () => {
89
- const doc: ActivityDoc = {
90
- notation: 'action',
91
- activities: [
92
- { id: 'A1', name: 'Analysis', duration: 5 },
93
- { id: 'A2', name: 'Design', duration: 3, predecessors: ['A1'] },
94
- ],
95
- };
96
- const svg = renderActivitiesSvg(doc);
97
- expect(svg).toContain('A1');
98
- expect(svg).toContain('A2');
99
- });
100
- });