testeranto 0.233.3 → 0.233.4
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/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "testeranto",
|
|
3
3
|
"displayName": "Testeranto",
|
|
4
4
|
"description": "the Dockerized, AI powered, BDD test framework for polyglot projects",
|
|
5
|
-
"version": "0.233.
|
|
5
|
+
"version": "0.233.4",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.19.0",
|
|
8
8
|
"vscode": "^1.60.0",
|
|
@@ -345,7 +345,6 @@
|
|
|
345
345
|
"url": "^0.11.4",
|
|
346
346
|
"uuid": "^10.0.0",
|
|
347
347
|
"validate-with-xmllint": "^1.2.1",
|
|
348
|
-
"viz": "^0.1.0",
|
|
349
348
|
"ws": "^8.18.3",
|
|
350
349
|
"xmldom": "^0.6.0",
|
|
351
350
|
"xterm": "^5.3.0",
|
|
@@ -2,14 +2,14 @@ this file documents how test results can be visualized.
|
|
|
2
2
|
|
|
3
3
|
testeranto includes user-defined visualizations, in the form of react components passed via configuration.
|
|
4
4
|
|
|
5
|
-
The
|
|
5
|
+
The grafeovidajo library is a set of react components to assist that. At it's core, it takes graph data, and projects it onto a 2d space, similar to d3. The goal is to provide the right hooks to allow the creation of multiple popular charts.
|
|
6
6
|
|
|
7
7
|
- eishhower matrix
|
|
8
8
|
- gantt chart
|
|
9
9
|
- kanan board
|
|
10
10
|
- trees and graphs
|
|
11
11
|
|
|
12
|
-
given 2 attributes on the nodes and some customizations, the
|
|
12
|
+
given 2 attributes on the nodes and some customizations, the grafeovidajo library project your graph data onto the 2d screen.
|
|
13
13
|
|
|
14
14
|
First, an x and y attribute are chosen
|
|
15
15
|
Second, if those attributes are a set or continuous
|
|
@@ -46,18 +46,18 @@ interface Edge {
|
|
|
46
46
|
interface ProjectionConfig {
|
|
47
47
|
xAttribute: string;
|
|
48
48
|
yAttribute: string;
|
|
49
|
-
xType:
|
|
50
|
-
yType:
|
|
51
|
-
|
|
52
|
-
layout?:
|
|
49
|
+
xType: "categorical" | "continuous" | "ordinal" | "temporal";
|
|
50
|
+
yType: "categorical" | "continuous" | "ordinal" | "temporal";
|
|
51
|
+
|
|
52
|
+
layout?: "grid" | "force" | "tree" | "timeline";
|
|
53
53
|
spacing?: {
|
|
54
54
|
x: number;
|
|
55
55
|
y: number;
|
|
56
56
|
};
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
xDomain?: [min: number, max: number] | string[];
|
|
59
59
|
yDomain?: [min: number, max: number] | string[];
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
xTransform?: (value: any) => number;
|
|
62
62
|
yTransform?: (value: any) => number;
|
|
63
63
|
}
|
|
@@ -66,7 +66,7 @@ interface ProjectionConfig {
|
|
|
66
66
|
interface StyleConfig {
|
|
67
67
|
nodeSize: number | ((node: Node) => number);
|
|
68
68
|
nodeColor: string | ((node: Node) => string);
|
|
69
|
-
nodeShape:
|
|
69
|
+
nodeShape: "circle" | "square" | "diamond" | ((node: Node) => string);
|
|
70
70
|
edgeColor?: string;
|
|
71
71
|
edgeWidth?: number;
|
|
72
72
|
labels?: {
|
|
@@ -94,8 +94,8 @@ interface VizConfig {
|
|
|
94
94
|
```typescript
|
|
95
95
|
// Project graph data to screen coordinates
|
|
96
96
|
function projectGraph(
|
|
97
|
-
graph: GraphData,
|
|
98
|
-
config: ProjectionConfig
|
|
97
|
+
graph: GraphData,
|
|
98
|
+
config: ProjectionConfig,
|
|
99
99
|
): ProjectedGraph;
|
|
100
100
|
|
|
101
101
|
interface ProjectedGraph {
|
|
@@ -115,15 +115,25 @@ interface ProjectedNode extends Node {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// Layout algorithms
|
|
118
|
-
function layoutGrid(
|
|
118
|
+
function layoutGrid(
|
|
119
|
+
nodes: ProjectedNode[],
|
|
120
|
+
spacing: { x: number; y: number },
|
|
121
|
+
): ProjectedNode[];
|
|
119
122
|
function layoutForce(nodes: ProjectedNode[], edges?: Edge[]): ProjectedNode[];
|
|
120
|
-
function layoutTree(
|
|
121
|
-
|
|
123
|
+
function layoutTree(
|
|
124
|
+
nodes: ProjectedNode[],
|
|
125
|
+
edges: Edge[],
|
|
126
|
+
rootId?: string,
|
|
127
|
+
): ProjectedNode[];
|
|
128
|
+
function layoutTimeline(
|
|
129
|
+
nodes: ProjectedNode[],
|
|
130
|
+
timeAttribute: string,
|
|
131
|
+
): ProjectedNode[];
|
|
122
132
|
|
|
123
133
|
// Apply styles to projected graph
|
|
124
134
|
function applyStyles(
|
|
125
135
|
projectedGraph: ProjectedGraph,
|
|
126
|
-
styleConfig: StyleConfig
|
|
136
|
+
styleConfig: StyleConfig,
|
|
127
137
|
): StyledGraph;
|
|
128
138
|
|
|
129
139
|
interface StyledGraph extends ProjectedGraph {
|
|
@@ -154,10 +164,10 @@ interface VizComponentProps {
|
|
|
154
164
|
// Eisenhower Matrix
|
|
155
165
|
interface EisenhowerConfig extends VizConfig {
|
|
156
166
|
quadrants: {
|
|
157
|
-
urgentImportant: { x: [0, 0.5]
|
|
158
|
-
notUrgentImportant: { x: [0.5, 1]
|
|
159
|
-
urgentNotImportant: { x: [0, 0.5]
|
|
160
|
-
notUrgentNotImportant: { x: [0.5, 1]
|
|
167
|
+
urgentImportant: { x: [0, 0.5]; y: [0, 0.5] };
|
|
168
|
+
notUrgentImportant: { x: [0.5, 1]; y: [0, 0.5] };
|
|
169
|
+
urgentNotImportant: { x: [0, 0.5]; y: [0.5, 1] };
|
|
170
|
+
notUrgentNotImportant: { x: [0.5, 1]; y: [0.5, 1] };
|
|
161
171
|
};
|
|
162
172
|
}
|
|
163
173
|
|
|
@@ -181,7 +191,7 @@ interface KanbanConfig extends VizConfig {
|
|
|
181
191
|
// Tree/Graph
|
|
182
192
|
interface TreeConfig extends VizConfig {
|
|
183
193
|
rootId?: string;
|
|
184
|
-
orientation:
|
|
194
|
+
orientation: "horizontal" | "vertical";
|
|
185
195
|
nodeSeparation: number;
|
|
186
196
|
levelSeparation: number;
|
|
187
197
|
}
|
|
@@ -221,7 +231,7 @@ const eisenhowerConfig: EisenhowerConfig = {
|
|
|
221
231
|
};
|
|
222
232
|
|
|
223
233
|
// Usage in React component
|
|
224
|
-
<EisenhowerMatrix
|
|
234
|
+
<EisenhowerMatrix
|
|
225
235
|
data={testResultsGraph}
|
|
226
236
|
config={eisenhowerConfig}
|
|
227
237
|
width={800}
|
|
@@ -237,4 +247,7 @@ const eisenhowerConfig: EisenhowerConfig = {
|
|
|
237
247
|
3. **Extensible Layouts**: Easy to add new layout algorithms
|
|
238
248
|
4. **Responsive Design**: Works with any screen size
|
|
239
249
|
5. **Interactive**: Built-in support for hover, click, drag interactions
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
|
|
240
253
|
```
|
|
@@ -318,7 +318,7 @@ export class Server_HTTP_utils {
|
|
|
318
318
|
fileContents: await this.extractFileContentsFromTree(collatedFilesTree),
|
|
319
319
|
// Add feature graph for visualization
|
|
320
320
|
featureGraph: featureGraph,
|
|
321
|
-
// Add
|
|
321
|
+
// Add grafeovidajo configuration
|
|
322
322
|
vizConfig: {
|
|
323
323
|
projection: {
|
|
324
324
|
xAttribute: 'status',
|
|
@@ -353,7 +353,7 @@ export class Server_HTTP_utils {
|
|
|
353
353
|
const jsonString = JSON.stringify(embeddedData);
|
|
354
354
|
// Encode to base64 to avoid any HTML/JS escaping issues
|
|
355
355
|
const base64String = Buffer.from(jsonString, 'utf8').toString('base64');
|
|
356
|
-
|
|
356
|
+
|
|
357
357
|
// Create a script tag that decodes the base64 string
|
|
358
358
|
const scriptTag = `<script>
|
|
359
359
|
window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
@@ -466,20 +466,20 @@ window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
|
466
466
|
// Try to read and parse markdown file
|
|
467
467
|
let frontmatter: Record<string, any> = {};
|
|
468
468
|
let content: string | undefined;
|
|
469
|
-
|
|
469
|
+
|
|
470
470
|
try {
|
|
471
471
|
const fullPath = path.join(process.cwd(), normalizedPath);
|
|
472
472
|
if (fs.existsSync(fullPath)) {
|
|
473
473
|
const fileContent = await fs.promises.readFile(fullPath, 'utf-8');
|
|
474
474
|
content = fileContent;
|
|
475
|
-
|
|
475
|
+
|
|
476
476
|
// Parse markdown frontmatter
|
|
477
477
|
const match = fileContent.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
478
478
|
if (match) {
|
|
479
479
|
const [, frontmatterStr] = match;
|
|
480
480
|
frontmatter = this.parseYamlFrontmatter(frontmatterStr);
|
|
481
481
|
}
|
|
482
|
-
|
|
482
|
+
|
|
483
483
|
// Create feature node for graph
|
|
484
484
|
const nodeId = `feature:${normalizedPath}`;
|
|
485
485
|
featureNodes.push({
|
|
@@ -492,13 +492,13 @@ window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
|
492
492
|
_feature: feature
|
|
493
493
|
}
|
|
494
494
|
});
|
|
495
|
-
|
|
495
|
+
|
|
496
496
|
// Add edges for dependencies
|
|
497
497
|
if (frontmatter.dependsUpon) {
|
|
498
|
-
const dependencies = Array.isArray(frontmatter.dependsUpon)
|
|
499
|
-
? frontmatter.dependsUpon
|
|
498
|
+
const dependencies = Array.isArray(frontmatter.dependsUpon)
|
|
499
|
+
? frontmatter.dependsUpon
|
|
500
500
|
: [frontmatter.dependsUpon];
|
|
501
|
-
|
|
501
|
+
|
|
502
502
|
for (const dep of dependencies) {
|
|
503
503
|
let depPath = dep;
|
|
504
504
|
if (depPath.startsWith('./')) {
|
|
@@ -508,7 +508,7 @@ window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
|
508
508
|
depPath = depPath.substring(1);
|
|
509
509
|
}
|
|
510
510
|
depPath = depPath.replace(/\\/g, '/');
|
|
511
|
-
|
|
511
|
+
|
|
512
512
|
const depNodeId = `feature:${depPath}`;
|
|
513
513
|
featureEdges.push({
|
|
514
514
|
source: depNodeId,
|
|
@@ -544,7 +544,7 @@ window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
|
544
544
|
feature,
|
|
545
545
|
isDocumentation: false,
|
|
546
546
|
});
|
|
547
|
-
|
|
547
|
+
|
|
548
548
|
// Create node for plain feature
|
|
549
549
|
const nodeId = `feature:plain:${feature.replace(/[^a-zA-Z0-9]/g, '_')}`;
|
|
550
550
|
featureNodes.push({
|
|
@@ -583,16 +583,16 @@ window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
|
583
583
|
try {
|
|
584
584
|
const result: Record<string, any> = {};
|
|
585
585
|
const lines = yamlStr.split('\n');
|
|
586
|
-
|
|
586
|
+
|
|
587
587
|
for (const line of lines) {
|
|
588
588
|
const trimmed = line.trim();
|
|
589
589
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
590
|
-
|
|
590
|
+
|
|
591
591
|
const colonIndex = trimmed.indexOf(':');
|
|
592
592
|
if (colonIndex > 0) {
|
|
593
593
|
const key = trimmed.substring(0, colonIndex).trim();
|
|
594
594
|
let value = trimmed.substring(colonIndex + 1).trim();
|
|
595
|
-
|
|
595
|
+
|
|
596
596
|
// Try to parse values
|
|
597
597
|
if (value === 'true') value = true;
|
|
598
598
|
else if (value === 'false') value = false;
|
|
@@ -610,11 +610,11 @@ window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
|
610
610
|
// Keep as string
|
|
611
611
|
}
|
|
612
612
|
}
|
|
613
|
-
|
|
613
|
+
|
|
614
614
|
result[key] = value;
|
|
615
615
|
}
|
|
616
616
|
}
|
|
617
|
-
|
|
617
|
+
|
|
618
618
|
return result;
|
|
619
619
|
} catch (error) {
|
|
620
620
|
console.warn('Failed to parse YAML frontmatter:', error);
|
|
@@ -854,7 +854,7 @@ window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
|
854
854
|
|
|
855
855
|
private static async readDocumentationFiles(files: string[]): Promise<Record<string, string>> {
|
|
856
856
|
const contents: Record<string, string> = {};
|
|
857
|
-
|
|
857
|
+
|
|
858
858
|
for (const file of files) {
|
|
859
859
|
try {
|
|
860
860
|
const fullPath = path.join(process.cwd(), file);
|
|
@@ -866,16 +866,16 @@ window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
|
866
866
|
console.warn(`Could not read documentation file ${file}:`, error);
|
|
867
867
|
}
|
|
868
868
|
}
|
|
869
|
-
|
|
869
|
+
|
|
870
870
|
return contents;
|
|
871
871
|
}
|
|
872
872
|
|
|
873
873
|
private static async extractFileContentsFromTree(tree: any): Promise<Record<string, string>> {
|
|
874
874
|
const contents: Record<string, string> = {};
|
|
875
|
-
|
|
875
|
+
|
|
876
876
|
const traverse = async (node: any, currentPath: string = '') => {
|
|
877
877
|
if (!node) return;
|
|
878
|
-
|
|
878
|
+
|
|
879
879
|
if (node.type === 'file' && node.path) {
|
|
880
880
|
try {
|
|
881
881
|
const fullPath = path.join(process.cwd(), node.path);
|
|
@@ -887,14 +887,14 @@ window.TESTERANTO_EMBEDDED_DATA = JSON.parse(atob('${base64String}'));
|
|
|
887
887
|
console.warn(`Could not read file ${node.path}:`, error);
|
|
888
888
|
}
|
|
889
889
|
}
|
|
890
|
-
|
|
890
|
+
|
|
891
891
|
if (node.children) {
|
|
892
892
|
for (const [key, child] of Object.entries(node.children)) {
|
|
893
893
|
await traverse(child, currentPath ? `${currentPath}/${key}` : key);
|
|
894
894
|
}
|
|
895
895
|
}
|
|
896
896
|
};
|
|
897
|
-
|
|
897
|
+
|
|
898
898
|
await traverse(tree);
|
|
899
899
|
return contents;
|
|
900
900
|
}
|
package/src/viz.md
CHANGED
|
@@ -22,9 +22,9 @@ dependsUpon: ./someOtherFeature.md
|
|
|
22
22
|
|
|
23
23
|
This graph is precomputed and embedded in the html. The front end presents it. WE can still use fetch command, so we do not necessarily need to embed the file contents int the graph, only theirilfenames.
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## grafeovidajo
|
|
26
26
|
|
|
27
|
-
the
|
|
27
|
+
the grafeovidajo library works by mapping graph data to 2d plain. We need to achieve the falling charts
|
|
28
28
|
|
|
29
29
|
- esenhow matric
|
|
30
30
|
- gantt
|