@aqa-pulse/cli 0.1.0

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 (37) hide show
  1. package/README.md +43 -0
  2. package/bin/aqa-pulse.js +49 -0
  3. package/dist/backend/generate-source-facts.d.ts +2 -0
  4. package/dist/backend/generate-source-facts.js +607 -0
  5. package/dist/backend/merge-reports.d.ts +19 -0
  6. package/dist/backend/merge-reports.js +314 -0
  7. package/dist/backend/upload-report-artifacts.d.ts +9 -0
  8. package/dist/backend/upload-report-artifacts.js +772 -0
  9. package/dist/backend/upload-report.d.ts +13 -0
  10. package/dist/backend/upload-report.js +338 -0
  11. package/dist/dashboard-utils.d.ts +437 -0
  12. package/dist/dashboard-utils.js +2627 -0
  13. package/dist/history-utils.d.ts +72 -0
  14. package/dist/history-utils.js +267 -0
  15. package/dist/shared/business-assumptions.d.ts +14 -0
  16. package/dist/shared/business-assumptions.js +61 -0
  17. package/dist/shared/dashboard-helpers.d.ts +63 -0
  18. package/dist/shared/dashboard-helpers.js +429 -0
  19. package/dist/shared/dashboard-metric-info.d.ts +61 -0
  20. package/dist/shared/dashboard-metric-info.js +15 -0
  21. package/dist/shared/error-utils.d.ts +1 -0
  22. package/dist/shared/error-utils.js +6 -0
  23. package/dist/shared/formatting.d.ts +3 -0
  24. package/dist/shared/formatting.js +42 -0
  25. package/dist/shared/i18n/ru.d.ts +558 -0
  26. package/dist/shared/i18n/ru.js +577 -0
  27. package/dist/shared/metric-info.d.ts +5 -0
  28. package/dist/shared/metric-info.js +210 -0
  29. package/dist/shared/navigation.d.ts +31 -0
  30. package/dist/shared/navigation.js +99 -0
  31. package/dist/shared/test-history-helpers.d.ts +51 -0
  32. package/dist/shared/test-history-helpers.js +294 -0
  33. package/dist/shared/test-history-metric-info.d.ts +17 -0
  34. package/dist/shared/test-history-metric-info.js +20 -0
  35. package/dist/shared/text-utils.d.ts +2 -0
  36. package/dist/shared/text-utils.js +15 -0
  37. package/package.json +37 -0
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveMetricIcon = resolveMetricIcon;
4
+ exports.renderMetricIconSvg = renderMetricIconSvg;
5
+ const ru_1 = require("./i18n/ru");
6
+ const SVG_ICON_BY_ID = {
7
+ 'check-circle': svgIcon('<circle cx="8" cy="8" r="5.25" /><path d="M5.7 8.1 7.25 9.65 10.45 6.45" />'),
8
+ 'x-circle': svgIcon('<circle cx="8" cy="8" r="5.25" /><path d="M6 6l4 4M10 6 6 10" />'),
9
+ spark: svgIcon('<path d="M8 2.5c.55 2.05 1.45 2.95 3.5 3.5-2.05.55-2.95 1.45-3.5 3.5-.55-2.05-1.45-2.95-3.5-3.5 2.05-.55 2.95-1.45 3.5-3.5Z" /><path d="M11.4 10.4c.28 1.05.75 1.52 1.8 1.8-1.05.28-1.52.75-1.8 1.8-.28-1.05-.75-1.52-1.8-1.8 1.05-.28 1.52-.75 1.8-1.8Z" />'),
10
+ orbit: svgIcon('<circle cx="8" cy="8" r="1.2" fill="currentColor" stroke="none" /><path d="M3.3 8c0-2.4 2.1-4.4 4.7-4.4 2.6 0 4.7 2 4.7 4.4s-2.1 4.4-4.7 4.4c-2.6 0-4.7-2-4.7-4.4Z" /><path d="M5 4.5c1.8.6 3.2 1.8 4.3 3.5.8 1.3 1.2 2.6 1.2 4" />'),
11
+ clock: svgIcon('<circle cx="8" cy="8" r="5.25" /><path d="M8 4.9v3.35l2.2 1.35" />'),
12
+ hourglass: svgIcon('<path d="M5 3.4h6M5 12.6h6M5.3 3.8c0 1.7.9 2.5 2 3.4 1.1.9 2.4 1.6 2.4 2.8M10.7 3.8c0 1.7-.9 2.5-2 3.4-1.1.9-2.4 1.6-2.4 2.8" />'),
13
+ cluster: svgIcon('<circle cx="5" cy="5.4" r="1.6" /><circle cx="11.1" cy="5" r="1.6" /><circle cx="8.2" cy="10.8" r="1.6" /><path d="M6.4 6.1 7.3 7.9M9.5 6 8.9 7.5" />'),
14
+ monitor: svgIcon('<rect x="2.6" y="3.2" width="10.8" height="7.2" rx="1.4" /><path d="M6.2 12.5h3.6M8 10.4v2.1" />'),
15
+ compare: svgIcon('<path d="M4 5.2h7.1M8.4 2.8l2.7 2.4-2.7 2.4M12 10.8H4.9M7.6 8.4 4.9 10.8l2.7 2.4" />'),
16
+ burst: svgIcon('<path d="m8 2.8 1.4 2.85 3.15.46-2.28 2.22.54 3.14L8 10.1l-2.81 1.47.54-3.14L3.45 6.1l3.15-.46L8 2.8Z" />'),
17
+ 'chart-up': svgIcon('<path d="M3.2 11.7h9.6" /><path d="M4.2 10 6.5 7.7l2 1.9 3.1-3.3" />'),
18
+ pie: svgIcon('<path d="M8 2.75a5.25 5.25 0 1 0 5.25 5.25H8Z" /><path d="M8.55 2.8a5.25 5.25 0 0 1 4.65 4.65H8.55Z" />'),
19
+ history: svgIcon('<path d="M3.4 7.8A4.9 4.9 0 1 1 5 11.5" /><path d="M3.6 4.6v3h3" /><path d="M8 5.4v2.8l1.9 1.2" />'),
20
+ note: svgIcon('<path d="M4.2 2.9h5.1l2.5 2.5v7.7H4.2Z" /><path d="M9.3 2.9v2.6h2.5M5.6 7h4.8M5.6 9.3h4.8" />'),
21
+ list: svgIcon('<path d="M5.5 4.8h6M5.5 8h6M5.5 11.2h6" /><circle cx="3.7" cy="4.8" r=".7" fill="currentColor" stroke="none" /><circle cx="3.7" cy="8" r=".7" fill="currentColor" stroke="none" /><circle cx="3.7" cy="11.2" r=".7" fill="currentColor" stroke="none" />'),
22
+ ruler: svgIcon('<rect x="3" y="4.2" width="10" height="7.6" rx="1.2" /><path d="M5.2 6v1.4M7 6v.8M8.8 6v1.4M10.6 6v.8" />'),
23
+ layers: svgIcon('<path d="m8 3.2 4.5 2.2L8 7.6 3.5 5.4 8 3.2ZM3.9 7.7 8 9.8l4.1-2.1M3.9 9.9 8 12l4.1-2.1" />'),
24
+ globe: svgIcon('<circle cx="8" cy="8" r="5.25" /><path d="M2.8 8h10.4M8 2.75c1.45 1.45 2.2 3.15 2.2 5.25S9.45 11.8 8 13.25M8 2.75C6.55 4.2 5.8 5.9 5.8 8s.75 3.8 2.2 5.25" />'),
25
+ compass: svgIcon('<circle cx="8" cy="8" r="5.25" /><path d="m9.9 6.1-1.2 3-2.8 1.2 1.2-3 2.8-1.2Z" />'),
26
+ 'chart-down': svgIcon('<path d="M3.2 11.7h9.6" /><path d="M4.2 6.1 6.5 8.4l2-1.9 3.1 3.3" />'),
27
+ alert: svgIcon('<path d="M8 3.1 12.5 11H3.5L8 3.1Z" /><path d="M8 6v2.3M8 9.9h.01" />'),
28
+ target: svgIcon('<circle cx="8" cy="8" r="4.9" /><circle cx="8" cy="8" r="2.2" /><circle cx="8" cy="8" r=".7" fill="currentColor" stroke="none" />'),
29
+ dice: svgIcon('<rect x="3.1" y="3.1" width="9.8" height="9.8" rx="1.8" /><circle cx="6" cy="6" r=".6" fill="currentColor" stroke="none" /><circle cx="10" cy="6" r=".6" fill="currentColor" stroke="none" /><circle cx="8" cy="8" r=".6" fill="currentColor" stroke="none" /><circle cx="6" cy="10" r=".6" fill="currentColor" stroke="none" /><circle cx="10" cy="10" r=".6" fill="currentColor" stroke="none" />'),
30
+ flag: svgIcon('<path d="M4.3 13V3.2" /><path d="M5 3.6h5l-1.1 1.8L10 7.2H5Z" />'),
31
+ bell: svgIcon('<path d="M5.2 10.6h5.6" /><path d="M6 10.6V7.4a2 2 0 1 1 4 0v3.2" /><path d="M7 11.2a1 1 0 0 0 2 0" />'),
32
+ wrench: svgIcon('<path d="M10.9 3.6a2.2 2.2 0 0 0-2.7 2.8L4.4 10.2a1.1 1.1 0 1 0 1.6 1.6l3.8-3.8a2.2 2.2 0 0 0 2.8-2.7l-1.7 1.1-1.3-1.3 1.3-1.5Z" />'),
33
+ coins: svgIcon('<ellipse cx="8" cy="4.5" rx="3.4" ry="1.6" /><path d="M4.6 4.5v4c0 .9 1.5 1.6 3.4 1.6s3.4-.7 3.4-1.6v-4" /><path d="M4.6 7c0 .9 1.5 1.6 3.4 1.6s3.4-.7 3.4-1.6" />'),
34
+ brain: svgIcon('<path d="M6.1 4.2a1.9 1.9 0 1 0-2.6 2.6 2 2 0 0 0 1.2 3.7h1.7V4.2ZM9.9 4.2a1.9 1.9 0 1 1 2.6 2.6 2 2 0 0 1-1.2 3.7H9.6V4.2Z" /><path d="M6.4 6.1h3.2M6.4 8.2h3.2" />'),
35
+ shield: svgIcon('<path d="M8 2.8 11.6 4v3.2c0 2.3-1.5 4.2-3.6 5-2.1-.8-3.6-2.7-3.6-5V4L8 2.8Z" /><path d="M6.2 8.1 7.5 9.3 9.9 6.9" />'),
36
+ calculator: svgIcon('<rect x="4.1" y="2.8" width="7.8" height="10.4" rx="1.3" /><path d="M5.6 5.1h4.8M5.8 7.8h1.1M8.1 7.8h1.1M10.4 7.8h.1M5.8 10.1h1.1M8.1 10.1h1.1M10.4 10.1h.1" />'),
37
+ sliders: svgIcon('<path d="M4.5 3.6v8.8M8 3.6v8.8M11.5 3.6v8.8" /><circle cx="4.5" cy="6" r="1.1" fill="currentColor" stroke="none" /><circle cx="8" cy="9" r="1.1" fill="currentColor" stroke="none" /><circle cx="11.5" cy="5.2" r="1.1" fill="currentColor" stroke="none" />'),
38
+ code: svgIcon('<path d="M6.2 5.3 4 8l2.2 2.7M9.8 5.3 12 8l-2.2 2.7M8.6 4.5 7.4 11.5" />'),
39
+ users: svgIcon('<circle cx="6.1" cy="5.7" r="1.7" /><circle cx="10.5" cy="6.3" r="1.4" /><path d="M3.9 11c.6-1.6 1.8-2.4 3.6-2.4S10.5 9.4 11 11M9.3 10.7c.4-1 1.2-1.6 2.4-1.6 1.1 0 1.8.4 2.3 1.4" />'),
40
+ bot: svgIcon('<rect x="4" y="4.7" width="8" height="6.1" rx="1.5" /><path d="M8 2.9v1.4M6.1 4.7l-.8-1M9.9 4.7l.8-1" /><circle cx="6.8" cy="7.7" r=".55" fill="currentColor" stroke="none" /><circle cx="9.2" cy="7.7" r=".55" fill="currentColor" stroke="none" /><path d="M6.9 9.2h2.2" />'),
41
+ calendar: svgIcon('<rect x="3.2" y="4" width="9.6" height="8.3" rx="1.2" /><path d="M5.4 2.8v2.1M10.6 2.8v2.1M3.2 6.2h9.6" />'),
42
+ hash: svgIcon('<path d="M6.1 3.2 5.2 12.8M10.8 3.2 9.9 12.8M3.5 6.2h8.8M3.1 9.8h8.8" />'),
43
+ timeline: svgIcon('<path d="M3.2 8h9.6" /><circle cx="4.3" cy="8" r="1" fill="currentColor" stroke="none" /><circle cx="8" cy="8" r="1" fill="currentColor" stroke="none" /><circle cx="11.7" cy="8" r="1" fill="currentColor" stroke="none" />'),
44
+ medical: svgIcon('<rect x="3.1" y="4.2" width="9.8" height="7.6" rx="1.8" /><path d="M8 5.8v4.4M5.8 8h4.4" />'),
45
+ bandage: svgIcon('<path d="m5.1 5.2 5.7 5.7M10.8 5.2l-5.7 5.7" /><rect x="3.2" y="6.2" width="9.6" height="3.6" rx="1.8" transform="rotate(-45 8 8)" /><path d="M7 7.1h.01M8 8h.01M9 8.9h.01" />'),
46
+ book: svgIcon('<path d="M4.2 3.6h5.1a1.8 1.8 0 0 1 1.8 1.8v6H6a1.8 1.8 0 0 0-1.8 1.8V5.4a1.8 1.8 0 0 1 1.8-1.8Z" /><path d="M6.2 5.9h3.3M6.2 8.1h3.3" />'),
47
+ link: svgIcon('<path d="M6.1 9.9 4.8 11.2a2 2 0 1 1-2.8-2.8l1.9-1.9a2 2 0 0 1 2.8 0M9.9 6.1l1.3-1.3a2 2 0 1 1 2.8 2.8L12 9.5a2 2 0 0 1-2.8 0M5.8 10.2l4.4-4.4" />'),
48
+ 'check-dot': svgIcon('<circle cx="8" cy="8" r="5.25" /><circle cx="8" cy="8" r="1.35" fill="currentColor" stroke="none" />'),
49
+ 'archive-box': svgIcon('<path d="M3.2 5.1h9.6v6.6H3.2z" /><path d="M2.6 3.4h10.8v1.7H2.6zM6.2 8h3.6" />'),
50
+ gauge: svgIcon('<path d="M3.6 10.9a4.8 4.8 0 1 1 8.8 0" /><path d="M8 8l2-2.1" /><circle cx="8" cy="8" r=".8" fill="currentColor" stroke="none" />'),
51
+ };
52
+ const METRIC_ICON_BY_KEY = {
53
+ passRate: 'check-circle',
54
+ failedTests: 'x-circle',
55
+ flakyTests: 'orbit',
56
+ runDuration: 'clock',
57
+ errorClusters: 'cluster',
58
+ environment: 'monitor',
59
+ latestVsPrevious: 'compare',
60
+ failures: 'burst',
61
+ flakyShort: 'orbit',
62
+ duration: 'clock',
63
+ passRateTrend: 'chart-up',
64
+ statusDistribution: 'pie',
65
+ recentRuns: 'history',
66
+ notes: 'note',
67
+ latestRuns: 'list',
68
+ durationTrend: 'chart-up',
69
+ p95Duration: 'ruler',
70
+ p99Duration: 'ruler',
71
+ topSlowestTests: 'hourglass',
72
+ topSlowestTestsP1: 'hourglass',
73
+ phaseBreakdown: 'layers',
74
+ suiteDuration: 'archive-box',
75
+ durationPerBrowser: 'globe',
76
+ leadingPhase: 'compass',
77
+ flakyTrend: 'chart-down',
78
+ clusterDistribution: 'cluster',
79
+ problematicTests: 'alert',
80
+ topFlakyTests: 'target',
81
+ flakyScore: 'orbit',
82
+ unstableRuns: 'dice',
83
+ lastStatus: 'flag',
84
+ timeToDetect: 'bell',
85
+ timeToFixFlaky: 'wrench',
86
+ ciWasteTime: 'hourglass',
87
+ costSection: 'coins',
88
+ costOfFlakiness: 'coins',
89
+ investigationCost: 'coins',
90
+ developerFriction: 'brain',
91
+ releaseConfidenceScore: 'shield',
92
+ releaseReadiness: 'shield',
93
+ qualityRisk: 'alert',
94
+ deliveryRisk: 'gauge',
95
+ managerSummary: 'shield',
96
+ automationRoi: 'chart-up',
97
+ costStructure: 'calculator',
98
+ configAssumptions: 'sliders',
99
+ codeQuality: 'code',
100
+ team: 'users',
101
+ ai: 'bot',
102
+ failureRate: 'chart-down',
103
+ mtbf: 'calendar',
104
+ archiveGaps: 'archive-box',
105
+ currentRunTests: 'list',
106
+ totalRuns: 'hash',
107
+ failedRuns: 'x-circle',
108
+ flakyRuns: 'orbit',
109
+ latestStatus: 'flag',
110
+ failRate: 'chart-down',
111
+ timeline: 'timeline',
112
+ latestError: 'burst',
113
+ latestFlakyEvent: 'orbit',
114
+ latestEvent: 'alert',
115
+ latestRecovery: 'bandage',
116
+ previousUnstableEvents: 'book',
117
+ currentStabilityStreak: 'check-dot',
118
+ unstableStreakBeforeRecovery: 'link',
119
+ };
120
+ const METRIC_ICON_BY_LABEL = createMetricIconLabelMap();
121
+ function resolveMetricIcon(metricKey, label) {
122
+ if (metricKey && METRIC_ICON_BY_KEY[metricKey]) {
123
+ return SVG_ICON_BY_ID[METRIC_ICON_BY_KEY[metricKey]];
124
+ }
125
+ return METRIC_ICON_BY_LABEL.get(normalizeMetricLabel(label)) ?? null;
126
+ }
127
+ function renderMetricIconSvg(icon) {
128
+ return icon.svg;
129
+ }
130
+ function createMetricIconLabelMap() {
131
+ const iconMap = new Map();
132
+ const register = (label, key) => {
133
+ iconMap.set(normalizeMetricLabel(label), SVG_ICON_BY_ID[METRIC_ICON_BY_KEY[key]]);
134
+ };
135
+ register(ru_1.ru.dashboard.metrics.passRate, 'passRate');
136
+ register(ru_1.ru.dashboard.metrics.failedTests, 'failedTests');
137
+ register(ru_1.ru.dashboard.metrics.flakyTests, 'flakyTests');
138
+ register(ru_1.ru.dashboard.metrics.runDuration, 'runDuration');
139
+ register(ru_1.ru.dashboard.metrics.errorClusters, 'errorClusters');
140
+ register(ru_1.ru.dashboard.metrics.environment, 'environment');
141
+ register(ru_1.ru.dashboard.metrics.latestVsPrevious, 'latestVsPrevious');
142
+ register(ru_1.ru.dashboard.metrics.failures, 'failures');
143
+ register(ru_1.ru.dashboard.metrics.flakyShort, 'flakyShort');
144
+ register(ru_1.ru.dashboard.metrics.duration, 'duration');
145
+ register(ru_1.ru.dashboard.metrics.passRateTrend, 'passRateTrend');
146
+ register(ru_1.ru.dashboard.metrics.statusDistribution, 'statusDistribution');
147
+ register(ru_1.ru.dashboard.metrics.recentRuns, 'recentRuns');
148
+ register(ru_1.ru.dashboard.metrics.notes, 'notes');
149
+ register(ru_1.ru.dashboard.metrics.latestRuns, 'latestRuns');
150
+ register(ru_1.ru.dashboard.metrics.durationTrend, 'durationTrend');
151
+ register(ru_1.ru.dashboard.metrics.p95Duration, 'p95Duration');
152
+ register(ru_1.ru.dashboard.metrics.p99Duration, 'p99Duration');
153
+ register(ru_1.ru.dashboard.metrics.topSlowestTests, 'topSlowestTests');
154
+ register(ru_1.ru.dashboard.metrics.topSlowestTestsP1, 'topSlowestTestsP1');
155
+ register(ru_1.ru.dashboard.metrics.phaseBreakdown, 'phaseBreakdown');
156
+ register(ru_1.ru.dashboard.metrics.suiteDuration, 'suiteDuration');
157
+ register(ru_1.ru.dashboard.metrics.durationPerBrowser, 'durationPerBrowser');
158
+ register(ru_1.ru.dashboard.metrics.leadingPhase, 'leadingPhase');
159
+ register(ru_1.ru.dashboard.metrics.flakyTrend, 'flakyTrend');
160
+ register(ru_1.ru.dashboard.metrics.clusterDistribution, 'clusterDistribution');
161
+ register(ru_1.ru.dashboard.metrics.problematicTests, 'problematicTests');
162
+ register(ru_1.ru.dashboard.metrics.topFlakyTests, 'topFlakyTests');
163
+ register(ru_1.ru.dashboard.metrics.flakyScore, 'flakyScore');
164
+ register(ru_1.ru.dashboard.metrics.unstableRuns, 'unstableRuns');
165
+ register(ru_1.ru.dashboard.metrics.lastStatus, 'lastStatus');
166
+ register(ru_1.ru.dashboard.metrics.timeToDetect, 'timeToDetect');
167
+ register(ru_1.ru.dashboard.metrics.timeToFixFlaky, 'timeToFixFlaky');
168
+ register(ru_1.ru.dashboard.metrics.ciWasteTime, 'ciWasteTime');
169
+ register(ru_1.ru.dashboard.metrics.costSection, 'costSection');
170
+ register(ru_1.ru.dashboard.metrics.costOfFlakiness, 'costOfFlakiness');
171
+ register(ru_1.ru.dashboard.metrics.investigationCost, 'investigationCost');
172
+ register(ru_1.ru.dashboard.metrics.developerFriction, 'developerFriction');
173
+ register(ru_1.ru.dashboard.metrics.releaseConfidenceScore, 'releaseConfidenceScore');
174
+ register(ru_1.ru.dashboard.metrics.automationRoi, 'automationRoi');
175
+ register(ru_1.ru.dashboard.metrics.costStructure, 'costStructure');
176
+ register(ru_1.ru.dashboard.metrics.configAssumptions, 'configAssumptions');
177
+ register(ru_1.ru.dashboard.metrics.codeQuality, 'codeQuality');
178
+ register(ru_1.ru.dashboard.metrics.team, 'team');
179
+ register(ru_1.ru.dashboard.metrics.ai, 'ai');
180
+ register(ru_1.ru.dashboard.manager.releaseReadiness, 'releaseReadiness');
181
+ register(ru_1.ru.dashboard.manager.qualityRisk, 'qualityRisk');
182
+ register(ru_1.ru.dashboard.manager.deliveryRisk, 'deliveryRisk');
183
+ register(ru_1.ru.dashboard.testsBrowser.title, 'currentRunTests');
184
+ register(ru_1.ru.testHistory.metrics.archiveGaps, 'archiveGaps');
185
+ register(ru_1.ru.testHistory.metrics.totalRuns, 'totalRuns');
186
+ register(ru_1.ru.testHistory.metrics.failedRuns, 'failedRuns');
187
+ register(ru_1.ru.testHistory.metrics.flakyRuns, 'flakyRuns');
188
+ register(ru_1.ru.testHistory.metrics.latestStatus, 'latestStatus');
189
+ register(ru_1.ru.testHistory.metrics.passRate, 'passRate');
190
+ register(ru_1.ru.testHistory.metrics.failRate, 'failRate');
191
+ register(ru_1.ru.testHistory.metrics.flakyScore, 'flakyScore');
192
+ register(ru_1.ru.testHistory.metrics.latestError, 'latestError');
193
+ register(ru_1.ru.testHistory.metrics.latestFlakyEvent, 'latestFlakyEvent');
194
+ register(ru_1.ru.testHistory.metrics.previousUnstableEvents, 'previousUnstableEvents');
195
+ register(ru_1.ru.testHistory.metrics.latestStableRecovery, 'latestRecovery');
196
+ register(ru_1.ru.testHistory.metrics.currentStabilityStreak, 'currentStabilityStreak');
197
+ register(ru_1.ru.testHistory.metrics.unstableStreakBeforeRecovery, 'unstableStreakBeforeRecovery');
198
+ register(ru_1.ru.testHistory.metrics.timeline, 'timeline');
199
+ register('Доля падений', 'failureRate');
200
+ register('MTBF', 'mtbf');
201
+ return iconMap;
202
+ }
203
+ function normalizeMetricLabel(label) {
204
+ return label.trim().toLowerCase();
205
+ }
206
+ function svgIcon(body) {
207
+ return {
208
+ svg: `<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">${body}</svg>`,
209
+ };
210
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Назначение: единая сборка href и query/filter routing для React runtime, server shell routes и static mode.
3
+ */
4
+ export interface QueryFilters {
5
+ branch?: string | null;
6
+ project?: string | null;
7
+ file?: string | null;
8
+ }
9
+ /**
10
+ * Собирает dashboard href так, чтобы одна и та же фильтрация одинаково работала в standalone, workspace и embedded сценариях.
11
+ */
12
+ export declare function buildWorkspaceDashboardHref(workspaceSlug: string | null, filters: QueryFilters): string;
13
+ export declare function buildSummaryApiUrl(workspaceSlug: string | null, filters: QueryFilters): string;
14
+ export declare function buildWorkspaceTestHistoryHref(workspaceSlug: string | null, testName: string, filters: QueryFilters): string;
15
+ export declare function buildTestHistoryApiUrl(workspaceSlug: string | null, testName: string, filters: QueryFilters): string;
16
+ export declare function buildArtifactBaseUrl(workspaceSlug: string | null): string;
17
+ export declare function buildDashboardHrefFromBasePath(filters: QueryFilters, basePath: string): string;
18
+ export declare function buildTestHistoryHrefFromTestDetailsBasePath(testName: string, filters: QueryFilters, testDetailsBasePath: string): string;
19
+ export declare function buildTestHistoryHrefFromDashboardBasePath(testName: string, filters: QueryFilters, dashboardBasePath: string): string;
20
+ export declare function buildApiTestHistoryHrefFromBasePath(testName: string, filters: QueryFilters, apiBasePath: string): string;
21
+ export declare function buildDashboardTabHref(dashboardActionPath: string, filters: QueryFilters, tabId: string, sectionId?: string): string;
22
+ export declare function buildWorkspaceLoginHref(workspaceSlug: string): string;
23
+ /**
24
+ * Нормализует фильтры из URLSearchParams в общий QueryFilters shape, чтобы runtime и shell route-слои не расходились по трактовке пустых значений.
25
+ */
26
+ export declare function readFiltersFromSearchParams(searchParams: URLSearchParams): QueryFilters;
27
+ /**
28
+ * Централизует сериализацию query filters, чтобы изменение набора фильтров происходило в одном месте и не расходилось между HTML и React слоями.
29
+ */
30
+ export declare function buildQueryString(filters: QueryFilters): string;
31
+ export declare function normalizeOptionalFilter(value: string | null | undefined): string | null;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildWorkspaceDashboardHref = buildWorkspaceDashboardHref;
4
+ exports.buildSummaryApiUrl = buildSummaryApiUrl;
5
+ exports.buildWorkspaceTestHistoryHref = buildWorkspaceTestHistoryHref;
6
+ exports.buildTestHistoryApiUrl = buildTestHistoryApiUrl;
7
+ exports.buildArtifactBaseUrl = buildArtifactBaseUrl;
8
+ exports.buildDashboardHrefFromBasePath = buildDashboardHrefFromBasePath;
9
+ exports.buildTestHistoryHrefFromTestDetailsBasePath = buildTestHistoryHrefFromTestDetailsBasePath;
10
+ exports.buildTestHistoryHrefFromDashboardBasePath = buildTestHistoryHrefFromDashboardBasePath;
11
+ exports.buildApiTestHistoryHrefFromBasePath = buildApiTestHistoryHrefFromBasePath;
12
+ exports.buildDashboardTabHref = buildDashboardTabHref;
13
+ exports.buildWorkspaceLoginHref = buildWorkspaceLoginHref;
14
+ exports.readFiltersFromSearchParams = readFiltersFromSearchParams;
15
+ exports.buildQueryString = buildQueryString;
16
+ exports.normalizeOptionalFilter = normalizeOptionalFilter;
17
+ /**
18
+ * Собирает dashboard href так, чтобы одна и та же фильтрация одинаково работала в standalone, workspace и embedded сценариях.
19
+ */
20
+ function buildWorkspaceDashboardHref(workspaceSlug, filters) {
21
+ const pathname = workspaceSlug ? `/w/${encodeURIComponent(workspaceSlug)}` : '/';
22
+ return appendQueryString(pathname, filters);
23
+ }
24
+ function buildSummaryApiUrl(workspaceSlug, filters) {
25
+ const pathname = workspaceSlug ? `/api/workspaces/${encodeURIComponent(workspaceSlug)}/summary` : '/api/summary';
26
+ return appendQueryString(pathname, filters);
27
+ }
28
+ function buildWorkspaceTestHistoryHref(workspaceSlug, testName, filters) {
29
+ const pathname = workspaceSlug
30
+ ? `/w/${encodeURIComponent(workspaceSlug)}/test/${encodeURIComponent(testName)}`
31
+ : `/test/${encodeURIComponent(testName)}`;
32
+ return appendQueryString(pathname, filters);
33
+ }
34
+ function buildTestHistoryApiUrl(workspaceSlug, testName, filters) {
35
+ const pathname = workspaceSlug
36
+ ? `/api/workspaces/${encodeURIComponent(workspaceSlug)}/test/${encodeURIComponent(testName)}`
37
+ : `/api/test/${encodeURIComponent(testName)}`;
38
+ return appendQueryString(pathname, filters);
39
+ }
40
+ function buildArtifactBaseUrl(workspaceSlug) {
41
+ return workspaceSlug
42
+ ? `/api/workspaces/${encodeURIComponent(workspaceSlug)}/artifacts`
43
+ : '/api/artifacts';
44
+ }
45
+ function buildDashboardHrefFromBasePath(filters, basePath) {
46
+ return appendQueryString(basePath || '/', filters);
47
+ }
48
+ function buildTestHistoryHrefFromTestDetailsBasePath(testName, filters, testDetailsBasePath) {
49
+ return appendQueryString(`${testDetailsBasePath}/${encodeURIComponent(testName)}`, filters);
50
+ }
51
+ function buildTestHistoryHrefFromDashboardBasePath(testName, filters, dashboardBasePath) {
52
+ const testHistoryBasePath = dashboardBasePath ? `${dashboardBasePath}/test` : '/test';
53
+ return appendQueryString(`${testHistoryBasePath}/${encodeURIComponent(testName)}`, filters);
54
+ }
55
+ function buildApiTestHistoryHrefFromBasePath(testName, filters, apiBasePath) {
56
+ const testHistoryBasePath = apiBasePath || '/api/test';
57
+ return appendQueryString(`${testHistoryBasePath}/${encodeURIComponent(testName)}`, filters);
58
+ }
59
+ function buildDashboardTabHref(dashboardActionPath, filters, tabId, sectionId) {
60
+ const query = buildQueryString(filters);
61
+ const hash = sectionId ? `#${tabId}:${sectionId}` : `#${tabId}`;
62
+ return query ? `${dashboardActionPath}?${query}${hash}` : `${dashboardActionPath}${hash}`;
63
+ }
64
+ function buildWorkspaceLoginHref(workspaceSlug) {
65
+ return `/w/${encodeURIComponent(workspaceSlug)}/login`;
66
+ }
67
+ /**
68
+ * Нормализует фильтры из URLSearchParams в общий QueryFilters shape, чтобы runtime и shell route-слои не расходились по трактовке пустых значений.
69
+ */
70
+ function readFiltersFromSearchParams(searchParams) {
71
+ return {
72
+ branch: normalizeOptionalFilter(searchParams.get('branch')),
73
+ project: normalizeOptionalFilter(searchParams.get('project')),
74
+ file: normalizeOptionalFilter(searchParams.get('file')),
75
+ };
76
+ }
77
+ /**
78
+ * Централизует сериализацию query filters, чтобы изменение набора фильтров происходило в одном месте и не расходилось между HTML и React слоями.
79
+ */
80
+ function buildQueryString(filters) {
81
+ const searchParams = new URLSearchParams();
82
+ if (filters.branch) {
83
+ searchParams.set('branch', filters.branch);
84
+ }
85
+ if (filters.project) {
86
+ searchParams.set('project', filters.project);
87
+ }
88
+ if (filters.file) {
89
+ searchParams.set('file', filters.file);
90
+ }
91
+ return searchParams.toString();
92
+ }
93
+ function normalizeOptionalFilter(value) {
94
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
95
+ }
96
+ function appendQueryString(pathname, filters) {
97
+ const query = buildQueryString(filters);
98
+ return query ? `${pathname}?${query}` : pathname;
99
+ }
@@ -0,0 +1,51 @@
1
+ import type { TestHistoryResponse } from '../api-store';
2
+ type TestHistoryItem = TestHistoryResponse['history'][number];
3
+ type TestHistoryAttachment = TestHistoryItem['attemptDetails'][number]['attachments'][number];
4
+ type TestHistoryAttemptStep = TestHistoryItem['attemptDetails'][number]['steps'][number];
5
+ export interface DiagnosticStepTreeNode {
6
+ step: TestHistoryAttemptStep;
7
+ stepIndex: number;
8
+ children: DiagnosticStepTreeNode[];
9
+ }
10
+ export declare function buildHistoryRowAnchor(runId: string): string;
11
+ export declare function formatRunsLabel(count: number): string;
12
+ export declare function getRunWord(count: number): string;
13
+ export declare function formatTemplate(template: string, values: Record<string, string>): string;
14
+ export declare function isStableHistoryItem(item: TestHistoryResponse['history'][number]): boolean;
15
+ export declare function isUnstableHistoryItem(item: TestHistoryResponse['history'][number]): boolean;
16
+ export declare function getUnstableHistoryItems(history: TestHistoryResponse['history']): TestHistoryResponse['history'];
17
+ export declare function getUnstableEventLabel(item: TestHistoryResponse['history'][number]): string;
18
+ export declare function findLatestStableRecovery(history: TestHistoryResponse['history']): {
19
+ recovery: TestHistoryResponse['history'][number];
20
+ previousUnstable: TestHistoryResponse['history'][number];
21
+ } | null;
22
+ export declare function findCurrentStabilityStreak(history: TestHistoryResponse['history']): {
23
+ count: number;
24
+ latestStable: TestHistoryResponse['history'][number] | null;
25
+ oldestStable: TestHistoryResponse['history'][number] | null;
26
+ previousUnstable: TestHistoryResponse['history'][number] | null;
27
+ };
28
+ export declare function findUnstableStreakBeforeRecovery(history: TestHistoryResponse['history']): {
29
+ count: number;
30
+ recovery: TestHistoryResponse['history'][number];
31
+ latestUnstable: TestHistoryResponse['history'][number];
32
+ oldestUnstable: TestHistoryResponse['history'][number];
33
+ } | null;
34
+ export declare function formatCurrentStabilityDescription(streak: ReturnType<typeof findCurrentStabilityStreak>): string;
35
+ export declare function normalizeAnchorLookupValue(value: string | null | undefined): string | null;
36
+ export declare function buildStepAnchor(runId: string, attemptNumber: number, stepIndex: number): string;
37
+ export declare function buildDiagnosticStepTree(steps: TestHistoryResponse['history'][number]['attemptDetails'][number]['steps']): DiagnosticStepTreeNode[];
38
+ export declare function findIncidentStepAnchor(history: TestHistoryResponse['history'], input: {
39
+ failureStepTitle: string | null;
40
+ failureStepCategory?: string | null;
41
+ failureStepErrorMessage?: string | null;
42
+ failureStepRunId?: string | null;
43
+ failureStepAttempt?: number | null;
44
+ failureStepOffsetMs?: number | null;
45
+ }): string | null;
46
+ export declare function getAttachmentReference(attachment: TestHistoryAttachment): string;
47
+ export declare function buildAttachmentHref(runId: string, attachment: TestHistoryAttachment, artifactBasePath: string): string | null;
48
+ export declare function isImageAttachment(attachment: TestHistoryAttachment): boolean;
49
+ export declare function isMarkdownAttachment(attachment: TestHistoryAttachment): boolean;
50
+ export declare function canInlineMarkdownPreview(href: string): boolean;
51
+ export {};