@principal-ai/principal-view-react 0.7.39 → 0.7.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/EdgeInfoPanel.d.ts.map +1 -1
- package/dist/components/EdgeInfoPanel.js +18 -12
- package/dist/components/EdgeInfoPanel.js.map +1 -1
- package/dist/components/NodeInfoPanel.d.ts.map +1 -1
- package/dist/components/NodeInfoPanel.js +46 -30
- package/dist/components/NodeInfoPanel.js.map +1 -1
- package/dist/components/NodeTooltip.d.ts.map +1 -1
- package/dist/components/NodeTooltip.js +11 -6
- package/dist/components/NodeTooltip.js.map +1 -1
- package/dist/components/SelectionSidebar.d.ts.map +1 -1
- package/dist/components/SelectionSidebar.js +24 -13
- package/dist/components/SelectionSidebar.js.map +1 -1
- package/dist/edges/CustomEdge.d.ts.map +1 -1
- package/dist/edges/CustomEdge.js +8 -4
- package/dist/edges/CustomEdge.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/nodes/CustomNode.d.ts.map +1 -1
- package/dist/nodes/CustomNode.js +32 -19
- package/dist/nodes/CustomNode.js.map +1 -1
- package/dist/utils/orientationUtils.d.ts +19 -0
- package/dist/utils/orientationUtils.d.ts.map +1 -0
- package/dist/utils/orientationUtils.js +62 -0
- package/dist/utils/orientationUtils.js.map +1 -0
- package/package.json +1 -2
- package/src/components/EdgeInfoPanel.tsx +20 -14
- package/src/components/NodeInfoPanel.tsx +48 -32
- package/src/components/NodeTooltip.tsx +11 -6
- package/src/components/SelectionSidebar.tsx +24 -13
- package/src/edges/CustomEdge.tsx +8 -4
- package/src/index.ts +5 -0
- package/src/nodes/CustomNode.tsx +32 -19
- package/src/stories/GraphOrientation.stories.tsx +500 -0
- package/src/utils/orientationUtils.ts +73 -0
- package/dist/components/NarrativeRenderer.d.ts +0 -19
- package/dist/components/NarrativeRenderer.d.ts.map +0 -1
- package/dist/components/NarrativeRenderer.js +0 -103
- package/dist/components/NarrativeRenderer.js.map +0 -1
- package/dist/components/TestEventPanel.d.ts +0 -44
- package/dist/components/TestEventPanel.d.ts.map +0 -1
- package/dist/components/TestEventPanel.js +0 -277
- package/dist/components/TestEventPanel.js.map +0 -1
- package/dist/utils/narrative-converter.d.ts +0 -45
- package/dist/utils/narrative-converter.d.ts.map +0 -1
- package/dist/utils/narrative-converter.js +0 -121
- package/dist/utils/narrative-converter.js.map +0 -1
- package/dist/utils/narrative-loader.d.ts +0 -53
- package/dist/utils/narrative-loader.d.ts.map +0 -1
- package/dist/utils/narrative-loader.js +0 -163
- package/dist/utils/narrative-loader.js.map +0 -1
- package/src/stories/RealTestExecution.stories.tsx +0 -404
- package/src/stories/ValidatedExecution.stories.tsx +0 -158
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utilities for loading and managing narrative templates
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Load a narrative template from a URL or file path
|
|
6
|
-
*
|
|
7
|
-
* Note: In most cases, you should import templates directly:
|
|
8
|
-
* import template from './my-template.narrative.json';
|
|
9
|
-
*
|
|
10
|
-
* This function is useful when you need to dynamically load templates.
|
|
11
|
-
*
|
|
12
|
-
* @param path - Path to the narrative template JSON file
|
|
13
|
-
* @returns Promise resolving to the narrative template, or null if not found
|
|
14
|
-
*/
|
|
15
|
-
export async function loadNarrativeTemplate(path) {
|
|
16
|
-
try {
|
|
17
|
-
const response = await fetch(path);
|
|
18
|
-
if (!response.ok) {
|
|
19
|
-
console.warn(`Narrative template not found: ${path}`);
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
const template = await response.json();
|
|
23
|
-
return template;
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
console.warn(`Failed to load narrative template from ${path}:`, error);
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Auto-discover narrative template for a given canvas path
|
|
32
|
-
*
|
|
33
|
-
* Example:
|
|
34
|
-
* Canvas: "/path/to/graph-converter.otel.canvas"
|
|
35
|
-
* Narrative: "/path/to/graph-converter.narrative.json"
|
|
36
|
-
*
|
|
37
|
-
* @param canvasPath - Path to the .otel.canvas file
|
|
38
|
-
* @returns Promise resolving to the narrative template, or null if not found
|
|
39
|
-
*/
|
|
40
|
-
export async function discoverNarrativeTemplate(canvasPath) {
|
|
41
|
-
const narrativePath = canvasPath.replace('.otel.canvas', '.narrative.json');
|
|
42
|
-
return loadNarrativeTemplate(narrativePath);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Validate a narrative template has required fields
|
|
46
|
-
*
|
|
47
|
-
* @param template - Template to validate
|
|
48
|
-
* @returns True if valid, false otherwise
|
|
49
|
-
*/
|
|
50
|
-
export function validateNarrativeTemplate(template) {
|
|
51
|
-
if (!template || typeof template !== 'object') {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
const t = template;
|
|
55
|
-
// Check required fields
|
|
56
|
-
if (!t.version || !t.canvas || !t.name || !t.mode || !t.scenarioSelection || !t.scenarios) {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
// Check scenarios array
|
|
60
|
-
if (!Array.isArray(t.scenarios) || t.scenarios.length === 0) {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
// Check each scenario has required fields
|
|
64
|
-
for (const scenario of t.scenarios) {
|
|
65
|
-
if (!scenario.id || !scenario.priority || !scenario.condition || !scenario.template) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Get a user-friendly error message for invalid templates
|
|
73
|
-
*
|
|
74
|
-
* @param template - Template to check
|
|
75
|
-
* @returns Error message, or null if valid
|
|
76
|
-
*/
|
|
77
|
-
export function getNarrativeTemplateError(template) {
|
|
78
|
-
if (!template || typeof template !== 'object') {
|
|
79
|
-
return 'Template must be a valid JSON object';
|
|
80
|
-
}
|
|
81
|
-
const t = template;
|
|
82
|
-
if (!t.version)
|
|
83
|
-
return 'Missing required field: version';
|
|
84
|
-
if (!t.canvas)
|
|
85
|
-
return 'Missing required field: canvas';
|
|
86
|
-
if (!t.name)
|
|
87
|
-
return 'Missing required field: name';
|
|
88
|
-
if (!t.mode)
|
|
89
|
-
return 'Missing required field: mode';
|
|
90
|
-
if (!t.scenarioSelection)
|
|
91
|
-
return 'Missing required field: scenarioSelection';
|
|
92
|
-
if (!t.scenarios)
|
|
93
|
-
return 'Missing required field: scenarios';
|
|
94
|
-
if (!Array.isArray(t.scenarios)) {
|
|
95
|
-
return 'Field "scenarios" must be an array';
|
|
96
|
-
}
|
|
97
|
-
if (t.scenarios.length === 0) {
|
|
98
|
-
return 'Template must have at least one scenario';
|
|
99
|
-
}
|
|
100
|
-
for (let i = 0; i < t.scenarios.length; i++) {
|
|
101
|
-
const scenario = t.scenarios[i];
|
|
102
|
-
if (!scenario.id)
|
|
103
|
-
return `Scenario ${i}: missing required field "id"`;
|
|
104
|
-
if (scenario.priority === undefined)
|
|
105
|
-
return `Scenario ${i}: missing required field "priority"`;
|
|
106
|
-
if (!scenario.condition)
|
|
107
|
-
return `Scenario ${i}: missing required field "condition"`;
|
|
108
|
-
if (!scenario.template)
|
|
109
|
-
return `Scenario ${i}: missing required field "template"`;
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* React hook for loading narrative templates
|
|
115
|
-
*
|
|
116
|
-
* @param templatePath - Path to template, or null to skip loading
|
|
117
|
-
* @returns {template, loading, error}
|
|
118
|
-
*/
|
|
119
|
-
export function useNarrativeTemplate(templatePath) {
|
|
120
|
-
const [template, setTemplate] = React.useState(null);
|
|
121
|
-
const [loading, setLoading] = React.useState(false);
|
|
122
|
-
const [error, setError] = React.useState(null);
|
|
123
|
-
React.useEffect(() => {
|
|
124
|
-
if (!templatePath) {
|
|
125
|
-
setTemplate(null);
|
|
126
|
-
setLoading(false);
|
|
127
|
-
setError(null);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
setLoading(true);
|
|
131
|
-
setError(null);
|
|
132
|
-
loadNarrativeTemplate(templatePath)
|
|
133
|
-
.then((loadedTemplate) => {
|
|
134
|
-
if (loadedTemplate) {
|
|
135
|
-
const validationError = getNarrativeTemplateError(loadedTemplate);
|
|
136
|
-
if (validationError) {
|
|
137
|
-
setError(validationError);
|
|
138
|
-
setTemplate(null);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
setTemplate(loadedTemplate);
|
|
142
|
-
setError(null);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
setError('Template not found');
|
|
147
|
-
setTemplate(null);
|
|
148
|
-
}
|
|
149
|
-
})
|
|
150
|
-
.catch((err) => {
|
|
151
|
-
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
152
|
-
setTemplate(null);
|
|
153
|
-
})
|
|
154
|
-
.finally(() => {
|
|
155
|
-
setLoading(false);
|
|
156
|
-
});
|
|
157
|
-
}, [templatePath]);
|
|
158
|
-
return { template, loading, error };
|
|
159
|
-
}
|
|
160
|
-
// Note: React import is expected to be available in the React package
|
|
161
|
-
// If this causes issues, we can remove the hook and export it separately
|
|
162
|
-
import React from 'react';
|
|
163
|
-
//# sourceMappingURL=narrative-loader.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"narrative-loader.js","sourceRoot":"","sources":["../../src/utils/narrative-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
import { GraphRenderer } from '../components/GraphRenderer';
|
|
4
|
-
import { TestEventPanel } from '../components/TestEventPanel';
|
|
5
|
-
import type { ViewMode } from '../components/TestEventPanel';
|
|
6
|
-
import type { ExtendedCanvas, GraphEvent, JsonObject, NarrativeTemplate } from '@principal-ai/principal-view-core/browser';
|
|
7
|
-
import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
|
|
8
|
-
import testSpans from './data/graph-converter-test-execution.json';
|
|
9
|
-
import narrativeTemplate from './data/graph-converter-test.narrative.json';
|
|
10
|
-
|
|
11
|
-
const meta = {
|
|
12
|
-
title: 'Features/Real Test Execution',
|
|
13
|
-
component: GraphRenderer,
|
|
14
|
-
parameters: {
|
|
15
|
-
layout: 'fullscreen',
|
|
16
|
-
docs: {
|
|
17
|
-
description: {
|
|
18
|
-
component:
|
|
19
|
-
'Visualizes REAL test execution data from instrumented Bun tests using the "wide event" pattern. Shows actual spans with file/line information collected from running GraphConverter.test.ts. Hover over graph nodes to highlight related events in the panel.',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
tags: ['autodocs'],
|
|
24
|
-
decorators: [
|
|
25
|
-
(Story) => (
|
|
26
|
-
<ThemeProvider theme={defaultEditorTheme}>
|
|
27
|
-
<div style={{ width: '100vw', height: '100vh', background: '#0a0a0a' }}>
|
|
28
|
-
<Story />
|
|
29
|
-
</div>
|
|
30
|
-
</ThemeProvider>
|
|
31
|
-
),
|
|
32
|
-
],
|
|
33
|
-
} satisfies Meta<typeof GraphRenderer>;
|
|
34
|
-
|
|
35
|
-
export default meta;
|
|
36
|
-
type Story = StoryObj<typeof meta>;
|
|
37
|
-
|
|
38
|
-
// ============================================================================
|
|
39
|
-
// Test Execution Flow Canvas
|
|
40
|
-
// ============================================================================
|
|
41
|
-
|
|
42
|
-
const testExecutionCanvas: ExtendedCanvas = {
|
|
43
|
-
nodes: [
|
|
44
|
-
// Test Suite
|
|
45
|
-
{
|
|
46
|
-
id: 'test-suite',
|
|
47
|
-
type: 'text',
|
|
48
|
-
text: 'GraphConverter Test Suite',
|
|
49
|
-
x: -100,
|
|
50
|
-
y: -100,
|
|
51
|
-
width: 240,
|
|
52
|
-
height: 80,
|
|
53
|
-
pv: {
|
|
54
|
-
nodeType: 'test-suite',
|
|
55
|
-
name: 'Test Suite',
|
|
56
|
-
description: 'Collection of GraphConverter tests',
|
|
57
|
-
shape: 'rectangle',
|
|
58
|
-
fill: '#3b82f6',
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
// Test Phase Nodes
|
|
63
|
-
{
|
|
64
|
-
id: 'setup-phase',
|
|
65
|
-
type: 'text',
|
|
66
|
-
text: 'Setup',
|
|
67
|
-
x: -250,
|
|
68
|
-
y: 50,
|
|
69
|
-
width: 120,
|
|
70
|
-
height: 80,
|
|
71
|
-
pv: {
|
|
72
|
-
nodeType: 'test-phase',
|
|
73
|
-
name: 'Setup Phase',
|
|
74
|
-
description: 'Test data preparation',
|
|
75
|
-
shape: 'hexagon',
|
|
76
|
-
fill: '#10b981',
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
id: 'execution-phase',
|
|
81
|
-
type: 'text',
|
|
82
|
-
text: 'Execution',
|
|
83
|
-
x: -80,
|
|
84
|
-
y: 50,
|
|
85
|
-
width: 120,
|
|
86
|
-
height: 80,
|
|
87
|
-
pv: {
|
|
88
|
-
nodeType: 'test-phase',
|
|
89
|
-
name: 'Execution Phase',
|
|
90
|
-
description: 'Code under test runs',
|
|
91
|
-
shape: 'hexagon',
|
|
92
|
-
fill: '#f59e0b',
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
id: 'assertion-phase',
|
|
97
|
-
type: 'text',
|
|
98
|
-
text: 'Assertion',
|
|
99
|
-
x: 90,
|
|
100
|
-
y: 50,
|
|
101
|
-
width: 120,
|
|
102
|
-
height: 80,
|
|
103
|
-
pv: {
|
|
104
|
-
nodeType: 'test-phase',
|
|
105
|
-
name: 'Assertion Phase',
|
|
106
|
-
description: 'Verify results',
|
|
107
|
-
shape: 'hexagon',
|
|
108
|
-
fill: '#8b5cf6',
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
// Result Node
|
|
113
|
-
{
|
|
114
|
-
id: 'test-result',
|
|
115
|
-
type: 'text',
|
|
116
|
-
text: 'Test Result',
|
|
117
|
-
x: -100,
|
|
118
|
-
y: 200,
|
|
119
|
-
width: 240,
|
|
120
|
-
height: 80,
|
|
121
|
-
pv: {
|
|
122
|
-
nodeType: 'result',
|
|
123
|
-
name: 'Test Result',
|
|
124
|
-
description: 'Pass/Fail outcome',
|
|
125
|
-
shape: 'rectangle',
|
|
126
|
-
fill: '#10b981',
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
edges: [
|
|
131
|
-
{
|
|
132
|
-
id: 'suite-to-setup',
|
|
133
|
-
fromNode: 'test-suite',
|
|
134
|
-
toNode: 'setup-phase',
|
|
135
|
-
fromSide: 'bottom',
|
|
136
|
-
toSide: 'top',
|
|
137
|
-
label: 'start test',
|
|
138
|
-
pv: {
|
|
139
|
-
edgeType: 'flow',
|
|
140
|
-
style: 'solid',
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
id: 'setup-to-execution',
|
|
145
|
-
fromNode: 'setup-phase',
|
|
146
|
-
toNode: 'execution-phase',
|
|
147
|
-
fromSide: 'right',
|
|
148
|
-
toSide: 'left',
|
|
149
|
-
label: 'data ready',
|
|
150
|
-
pv: {
|
|
151
|
-
edgeType: 'flow',
|
|
152
|
-
style: 'solid',
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
id: 'execution-to-assertion',
|
|
157
|
-
fromNode: 'execution-phase',
|
|
158
|
-
toNode: 'assertion-phase',
|
|
159
|
-
fromSide: 'right',
|
|
160
|
-
toSide: 'left',
|
|
161
|
-
label: 'got result',
|
|
162
|
-
pv: {
|
|
163
|
-
edgeType: 'flow',
|
|
164
|
-
style: 'solid',
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
id: 'assertion-to-result',
|
|
169
|
-
fromNode: 'assertion-phase',
|
|
170
|
-
toNode: 'test-result',
|
|
171
|
-
fromSide: 'bottom',
|
|
172
|
-
toSide: 'top',
|
|
173
|
-
label: 'complete',
|
|
174
|
-
pv: {
|
|
175
|
-
edgeType: 'flow',
|
|
176
|
-
style: 'solid',
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
],
|
|
180
|
-
pv: {
|
|
181
|
-
version: '1.0.0',
|
|
182
|
-
name: 'Test Execution Flow',
|
|
183
|
-
description: 'Visualizes the flow of test execution through phases',
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// ============================================================================
|
|
188
|
-
// Interactive Story (No Animation)
|
|
189
|
-
// ============================================================================
|
|
190
|
-
|
|
191
|
-
const AnimatedTestExecution = () => {
|
|
192
|
-
const [events] = useState<GraphEvent[]>([]);
|
|
193
|
-
const [currentSpanIndex, setCurrentSpanIndex] = useState(0);
|
|
194
|
-
// Show all events by default - set to a large number
|
|
195
|
-
const [currentEventIndex] = useState(999);
|
|
196
|
-
const [highlightedPhase, setHighlightedPhase] = useState<string | undefined>();
|
|
197
|
-
|
|
198
|
-
// Extract spans and logs from test data
|
|
199
|
-
const testData = testSpans as JsonObject;
|
|
200
|
-
const spans = Array.isArray(testData) ? testData : testData.spans || testData;
|
|
201
|
-
const logs = testData.logs || [];
|
|
202
|
-
|
|
203
|
-
return (
|
|
204
|
-
<div style={{ display: 'flex', width: '100vw', height: '100vh' }}>
|
|
205
|
-
{/* Event Panel - Left Side */}
|
|
206
|
-
<div style={{ flex: '0 0 50%', height: '100%', borderRight: `1px solid #333`, overflow: 'hidden' }}>
|
|
207
|
-
<TestEventPanel
|
|
208
|
-
spans={spans}
|
|
209
|
-
logs={logs}
|
|
210
|
-
currentSpanIndex={currentSpanIndex}
|
|
211
|
-
currentEventIndex={currentEventIndex}
|
|
212
|
-
highlightedPhase={highlightedPhase}
|
|
213
|
-
onSpanIndexChange={setCurrentSpanIndex}
|
|
214
|
-
/>
|
|
215
|
-
</div>
|
|
216
|
-
|
|
217
|
-
{/* Graph Visualization - Right Side */}
|
|
218
|
-
<div
|
|
219
|
-
style={{ flex: '0 0 50%', height: '100%', position: 'relative' }}
|
|
220
|
-
onMouseLeave={() => setHighlightedPhase(undefined)}
|
|
221
|
-
>
|
|
222
|
-
<div
|
|
223
|
-
style={{ width: '100%', height: '100%' }}
|
|
224
|
-
onMouseOver={(e) => {
|
|
225
|
-
// Check if hovering over a phase node
|
|
226
|
-
const target = e.target as HTMLElement;
|
|
227
|
-
const textContent = target.textContent;
|
|
228
|
-
if (textContent === 'Setup') setHighlightedPhase('setup');
|
|
229
|
-
else if (textContent === 'Execution') setHighlightedPhase('execution');
|
|
230
|
-
else if (textContent === 'Assertion') setHighlightedPhase('assertion');
|
|
231
|
-
}}
|
|
232
|
-
>
|
|
233
|
-
<GraphRenderer
|
|
234
|
-
canvas={testExecutionCanvas}
|
|
235
|
-
showControls={true}
|
|
236
|
-
events={events}
|
|
237
|
-
/>
|
|
238
|
-
</div>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
);
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Interactive visualization of real test execution data using the "wide event" pattern.
|
|
246
|
-
*
|
|
247
|
-
* This demonstrates the key concept from loggingsucks.com:
|
|
248
|
-
* - ONE comprehensive span per test (not multiple child spans)
|
|
249
|
-
* - Events show the narrative of what happened during execution
|
|
250
|
-
* - Context accumulates through event attributes with file/line information
|
|
251
|
-
* - Easy to search by test.name to get full execution story
|
|
252
|
-
*
|
|
253
|
-
* **Interaction:**
|
|
254
|
-
* - Hover over graph nodes (Setup, Execution, Assertion) to highlight related events
|
|
255
|
-
* - Watch the code journey: blue = test file, green = code under test
|
|
256
|
-
* - All events are shown immediately for easy review
|
|
257
|
-
*/
|
|
258
|
-
export const Animated: Story = {
|
|
259
|
-
render: () => <AnimatedTestExecution />,
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Static view of the test execution flow showing phases.
|
|
264
|
-
*/
|
|
265
|
-
export const StaticView: Story = {
|
|
266
|
-
args: {
|
|
267
|
-
canvas: testExecutionCanvas,
|
|
268
|
-
showControls: true,
|
|
269
|
-
},
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Event panel component showing test execution narrative with file/line information.
|
|
274
|
-
*
|
|
275
|
-
* Shows how events and logs are interleaved in chronological order, with automatic
|
|
276
|
-
* file/line capture from stack traces and severity-based color coding for logs.
|
|
277
|
-
*/
|
|
278
|
-
export const EventPanelOnly: StoryObj = {
|
|
279
|
-
render: () => {
|
|
280
|
-
const testData = testSpans as JsonObject;
|
|
281
|
-
const spans = Array.isArray(testData) ? testData : testData.spans || testData;
|
|
282
|
-
const logs = testData.logs || [];
|
|
283
|
-
|
|
284
|
-
return (
|
|
285
|
-
<div style={{ width: '600px', height: '100vh' }}>
|
|
286
|
-
<TestEventPanel
|
|
287
|
-
spans={spans}
|
|
288
|
-
logs={logs}
|
|
289
|
-
currentSpanIndex={0}
|
|
290
|
-
currentEventIndex={999} // Show all events
|
|
291
|
-
highlightedPhase={undefined}
|
|
292
|
-
/>
|
|
293
|
-
</div>
|
|
294
|
-
);
|
|
295
|
-
},
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Event panel with narrative view toggle
|
|
300
|
-
*
|
|
301
|
-
* Demonstrates the new narrative view mode that transforms raw OTEL events into
|
|
302
|
-
* human-readable execution narratives. Toggle between "Raw Events" and "Narrative"
|
|
303
|
-
* to see both views.
|
|
304
|
-
*
|
|
305
|
-
* Features:
|
|
306
|
-
* - Human-readable narrative generated from template
|
|
307
|
-
* - Scenario-based rendering (test-passed vs test-failed)
|
|
308
|
-
* - Syntax highlighting for better readability
|
|
309
|
-
* - Optional metadata display
|
|
310
|
-
*/
|
|
311
|
-
export const WithNarrativeToggle: StoryObj = {
|
|
312
|
-
render: () => {
|
|
313
|
-
const [viewMode, setViewMode] = useState<ViewMode>('narrative');
|
|
314
|
-
const [currentSpanIndex, setCurrentSpanIndex] = useState(0);
|
|
315
|
-
const [showMetadata, setShowMetadata] = useState(false);
|
|
316
|
-
|
|
317
|
-
const testData = testSpans as JsonObject;
|
|
318
|
-
const spans = Array.isArray(testData) ? testData : testData.spans || testData;
|
|
319
|
-
const logs = testData.logs || [];
|
|
320
|
-
|
|
321
|
-
return (
|
|
322
|
-
<div style={{ width: '100vw', height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
323
|
-
{/* Controls */}
|
|
324
|
-
<div
|
|
325
|
-
style={{
|
|
326
|
-
padding: '12px 20px',
|
|
327
|
-
background: '#1a1a1a',
|
|
328
|
-
borderBottom: '1px solid #333',
|
|
329
|
-
display: 'flex',
|
|
330
|
-
gap: '12px',
|
|
331
|
-
alignItems: 'center',
|
|
332
|
-
}}
|
|
333
|
-
>
|
|
334
|
-
<label style={{ color: '#999', fontSize: '14px' }}>
|
|
335
|
-
<input
|
|
336
|
-
type="checkbox"
|
|
337
|
-
checked={showMetadata}
|
|
338
|
-
onChange={(e) => setShowMetadata(e.target.checked)}
|
|
339
|
-
style={{ marginRight: '6px' }}
|
|
340
|
-
/>
|
|
341
|
-
Show Metadata
|
|
342
|
-
</label>
|
|
343
|
-
</div>
|
|
344
|
-
|
|
345
|
-
{/* Event Panel */}
|
|
346
|
-
<div style={{ flex: 1, overflow: 'hidden' }}>
|
|
347
|
-
<TestEventPanel
|
|
348
|
-
spans={spans}
|
|
349
|
-
logs={logs}
|
|
350
|
-
currentSpanIndex={currentSpanIndex}
|
|
351
|
-
currentEventIndex={999}
|
|
352
|
-
onSpanIndexChange={setCurrentSpanIndex}
|
|
353
|
-
viewMode={viewMode}
|
|
354
|
-
narrativeTemplate={narrativeTemplate as NarrativeTemplate}
|
|
355
|
-
onViewModeChange={setViewMode}
|
|
356
|
-
showNarrativeMetadata={showMetadata}
|
|
357
|
-
/>
|
|
358
|
-
</div>
|
|
359
|
-
</div>
|
|
360
|
-
);
|
|
361
|
-
},
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Split view with narrative on left, graph on right
|
|
366
|
-
*
|
|
367
|
-
* Shows how the narrative view integrates with the graph visualization,
|
|
368
|
-
* providing a complete picture of test execution.
|
|
369
|
-
*/
|
|
370
|
-
export const NarrativeWithGraph: StoryObj = {
|
|
371
|
-
render: () => {
|
|
372
|
-
const [viewMode, setViewMode] = useState<ViewMode>('narrative');
|
|
373
|
-
const [currentSpanIndex, setCurrentSpanIndex] = useState(0);
|
|
374
|
-
const [events] = useState<GraphEvent[]>([]);
|
|
375
|
-
|
|
376
|
-
const testData = testSpans as JsonObject;
|
|
377
|
-
const spans = Array.isArray(testData) ? testData : testData.spans || testData;
|
|
378
|
-
const logs = testData.logs || [];
|
|
379
|
-
|
|
380
|
-
return (
|
|
381
|
-
<div style={{ display: 'flex', width: '100vw', height: '100vh' }}>
|
|
382
|
-
{/* Event Panel - Left Side */}
|
|
383
|
-
<div style={{ flex: '0 0 50%', height: '100%', borderRight: '1px solid #333', overflow: 'hidden' }}>
|
|
384
|
-
<TestEventPanel
|
|
385
|
-
spans={spans}
|
|
386
|
-
logs={logs}
|
|
387
|
-
currentSpanIndex={currentSpanIndex}
|
|
388
|
-
currentEventIndex={999}
|
|
389
|
-
onSpanIndexChange={setCurrentSpanIndex}
|
|
390
|
-
viewMode={viewMode}
|
|
391
|
-
narrativeTemplate={narrativeTemplate as NarrativeTemplate}
|
|
392
|
-
onViewModeChange={setViewMode}
|
|
393
|
-
showNarrativeMetadata={true}
|
|
394
|
-
/>
|
|
395
|
-
</div>
|
|
396
|
-
|
|
397
|
-
{/* Graph Visualization - Right Side */}
|
|
398
|
-
<div style={{ flex: '0 0 50%', height: '100%', position: 'relative' }}>
|
|
399
|
-
<GraphRenderer canvas={testExecutionCanvas} showControls={true} events={events} />
|
|
400
|
-
</div>
|
|
401
|
-
</div>
|
|
402
|
-
);
|
|
403
|
-
},
|
|
404
|
-
};
|