testeranto 0.158.1 → 0.160.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.
- package/dist/common/src/Init.js +3 -3
- package/dist/common/src/utils/api.js +33 -192
- package/dist/common/tsconfig.common.tsbuildinfo +1 -1
- package/dist/module/src/Init.js +3 -3
- package/dist/module/src/ProjectPage.js +128 -21
- package/dist/module/src/ProjectsPage.js +15 -9
- package/dist/module/src/TestPage.js +65 -18
- package/dist/module/src/utils/api.js +33 -192
- package/dist/module/tsconfig.module.tsbuildinfo +1 -1
- package/dist/prebuild/App.js +901 -581
- package/dist/prebuild/init-docs.mjs +3 -3
- package/dist/types/src/utils/api.d.ts +2 -20
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/notify.sh +47 -0
- package/package.json +2 -2
- package/src/Init.ts +3 -3
- package/src/ProjectPage.tsx +204 -38
- package/src/ProjectsPage.tsx +23 -19
- package/src/TestPage.tsx +79 -18
- package/src/utils/api.ts +40 -193
- package/testeranto/App.js +901 -581
- package/testeranto/reports/allTests/src/lib/baseBuilder.test/baseBuilder.test.web/web/logs.txt +4 -4
- package/testeranto/reports/allTests/src/lib/classBuilder.test/classBuilder.test/node/logs.txt +1 -1
- package/dist/module/src/BuildLogsPage.js +0 -99
- package/dist/module/src/Project.js +0 -332
- package/src/BuildLogsPage.tsx +0 -108
- package/src/Project.tsx +0 -375
package/notify.sh
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/zsh
|
|
2
|
+
|
|
3
|
+
# NOTIFICATION SYSTEM DOCUMENTATION
|
|
4
|
+
# ================================
|
|
5
|
+
# This script plays system sounds for CI/CD notifications. The AI assistant uses it to:
|
|
6
|
+
# 1. Alert when human input is needed
|
|
7
|
+
# 2. Signal completion of tasks
|
|
8
|
+
# 3. Indicate errors/warnings
|
|
9
|
+
|
|
10
|
+
# USAGE:
|
|
11
|
+
# ./notify.sh [sound] [volume]
|
|
12
|
+
# sound: Any from the list below (default: Ping)
|
|
13
|
+
# volume: 0-1 (default: 1)
|
|
14
|
+
|
|
15
|
+
# AVAILABLE SOUNDS:
|
|
16
|
+
# Basso - Low pitch error sound (use for critical failures)
|
|
17
|
+
# Blow - Quick negative sound
|
|
18
|
+
# Bottle - Light glass ping
|
|
19
|
+
# Frog - Unique attention-grabber
|
|
20
|
+
# Funk - Positive completion sound
|
|
21
|
+
# Glass - Clean success notification
|
|
22
|
+
# Hero - Triumphant completion
|
|
23
|
+
# Morse - For process-related events
|
|
24
|
+
# Ping - Default neutral notification
|
|
25
|
+
# Pop - Light positive sound
|
|
26
|
+
# Purr - Subtle background notification
|
|
27
|
+
# Sosumi - Urgent alert sound
|
|
28
|
+
# Submarine- Deep warning sound
|
|
29
|
+
# Tink - High-pitched attention sound
|
|
30
|
+
|
|
31
|
+
# STANDARD USAGE PATTERNS:
|
|
32
|
+
# [ACTION NEEDED] -> ./notify.sh Sosumi 0.8
|
|
33
|
+
# [COMPLETED] -> ./notify.sh Glass 0.5
|
|
34
|
+
# [ERROR] -> ./notify.sh Basso 1
|
|
35
|
+
# [WARNING] -> ./notify.sh Tink 0.6
|
|
36
|
+
# [WORKING] -> ./notify.sh Morse 0.3
|
|
37
|
+
|
|
38
|
+
SOUND=${1:-Ping}
|
|
39
|
+
VOLUME=${2:-1} # Range: 0 (silent) to 1 (full volume)
|
|
40
|
+
|
|
41
|
+
# Play the specified system sound
|
|
42
|
+
afplay -v $VOLUME /System/Library/Sounds/$SOUND.aiff
|
|
43
|
+
|
|
44
|
+
# Exit codes:
|
|
45
|
+
# 0 - Success
|
|
46
|
+
# 1 - Invalid sound specified
|
|
47
|
+
# 2 - Volume out of range
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testeranto",
|
|
3
3
|
"description": "the AI powered BDD test framework for typescript projects",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.160.0",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "18.18.0"
|
|
7
7
|
},
|
|
@@ -212,4 +212,4 @@
|
|
|
212
212
|
"url": "^0.11.4",
|
|
213
213
|
"uuid": "^10.0.0"
|
|
214
214
|
}
|
|
215
|
-
}
|
|
215
|
+
}
|
package/src/Init.ts
CHANGED
|
@@ -6,11 +6,11 @@ export default async () => {
|
|
|
6
6
|
`testeranto/`,
|
|
7
7
|
`testeranto/bundles/`,
|
|
8
8
|
`testeranto/bundles/node`,
|
|
9
|
-
`testeranto/bundles/pure`,
|
|
10
9
|
`testeranto/bundles/web`,
|
|
11
|
-
`testeranto/
|
|
12
|
-
`testeranto/features/`,
|
|
10
|
+
`testeranto/bundles/pure`,
|
|
13
11
|
`testeranto/reports/`,
|
|
12
|
+
`testeranto/features/`,
|
|
13
|
+
`testeranto/externalTests/`,
|
|
14
14
|
].forEach((f) => {
|
|
15
15
|
try {
|
|
16
16
|
fs.mkdirSync(`${process.cwd()}/${f}`);
|
package/src/ProjectPage.tsx
CHANGED
|
@@ -1,5 +1,185 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Tab } from 'react-bootstrap';
|
|
3
|
+
import { Card, ListGroup, Badge } from 'react-bootstrap';
|
|
4
|
+
|
|
5
|
+
const BuildLogViewer = ({ logs, runtime }: { logs: any, runtime: string }) => {
|
|
6
|
+
if (!logs) return <Alert variant="info">Loading {runtime.toLowerCase()} build logs...</Alert>;
|
|
7
|
+
|
|
8
|
+
const hasErrors = logs.errors?.length > 0;
|
|
9
|
+
const hasWarnings = logs.warnings?.length > 0;
|
|
10
|
+
const [activeTab, setActiveTab] = useState('summary');
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div>
|
|
14
|
+
<Tab.Container activeKey={activeTab} onSelect={(k) => setActiveTab(k || 'summary')}>
|
|
15
|
+
<Nav variant="tabs" className="mb-3">
|
|
16
|
+
<Nav.Item>
|
|
17
|
+
<Nav.Link eventKey="summary">
|
|
18
|
+
Build Summary
|
|
19
|
+
</Nav.Link>
|
|
20
|
+
</Nav.Item>
|
|
21
|
+
<Nav.Item>
|
|
22
|
+
<Nav.Link eventKey="warnings">
|
|
23
|
+
{hasWarnings ? `⚠️ Warnings (${logs.warnings.length})` : 'Warnings'}
|
|
24
|
+
</Nav.Link>
|
|
25
|
+
</Nav.Item>
|
|
26
|
+
<Nav.Item>
|
|
27
|
+
<Nav.Link eventKey="errors">
|
|
28
|
+
{hasErrors ? `❌ Errors (${logs.errors.length})` : 'Errors'}
|
|
29
|
+
</Nav.Link>
|
|
30
|
+
</Nav.Item>
|
|
31
|
+
</Nav>
|
|
32
|
+
|
|
33
|
+
<Tab.Content>
|
|
34
|
+
<Tab.Pane eventKey="summary">
|
|
35
|
+
<Card>
|
|
36
|
+
<Card.Header className="d-flex justify-content-between align-items-center">
|
|
37
|
+
<h5>Build Summary</h5>
|
|
38
|
+
<div>
|
|
39
|
+
{hasErrors && (
|
|
40
|
+
<Badge bg="danger" className="me-2">
|
|
41
|
+
{logs.errors.length} Error{logs.errors.length !== 1 ? 's' : ''}
|
|
42
|
+
</Badge>
|
|
43
|
+
)}
|
|
44
|
+
{hasWarnings && (
|
|
45
|
+
<Badge bg="warning" text="dark">
|
|
46
|
+
{logs.warnings.length} Warning{logs.warnings.length !== 1 ? 's' : ''}
|
|
47
|
+
</Badge>
|
|
48
|
+
)}
|
|
49
|
+
{!hasErrors && !hasWarnings && (
|
|
50
|
+
<Badge bg="success">Build Successful</Badge>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
</Card.Header>
|
|
54
|
+
<Card.Body>
|
|
55
|
+
<div className="mb-3">
|
|
56
|
+
<h6>Input Files ({Object.keys(logs.metafile?.inputs || {}).length})</h6>
|
|
57
|
+
<ListGroup className="max-h-200 overflow-auto">
|
|
58
|
+
{Object.keys(logs.metafile?.inputs || {}).map((file) => (
|
|
59
|
+
<ListGroup.Item key={file} className="py-2">
|
|
60
|
+
<code>{file}</code>
|
|
61
|
+
<div className="text-muted small">
|
|
62
|
+
{logs.metafile.inputs[file].bytes} bytes
|
|
63
|
+
</div>
|
|
64
|
+
</ListGroup.Item>
|
|
65
|
+
))}
|
|
66
|
+
</ListGroup>
|
|
67
|
+
</div>
|
|
68
|
+
<div>
|
|
69
|
+
<h6>Output Files ({Object.keys(logs.metafile?.outputs || {}).length})</h6>
|
|
70
|
+
<ListGroup className="max-h-200 overflow-auto">
|
|
71
|
+
{Object.keys(logs.metafile?.outputs || {}).map((file) => (
|
|
72
|
+
<ListGroup.Item key={file} className="py-2">
|
|
73
|
+
<code>{file}</code>
|
|
74
|
+
<div className="text-muted small">
|
|
75
|
+
{logs.metafile.outputs[file].bytes} bytes
|
|
76
|
+
{logs.metafile.outputs[file].entryPoint && (
|
|
77
|
+
<span className="ms-2 badge bg-info">Entry Point</span>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
</ListGroup.Item>
|
|
81
|
+
))}
|
|
82
|
+
</ListGroup>
|
|
83
|
+
</div>
|
|
84
|
+
</Card.Body>
|
|
85
|
+
</Card>
|
|
86
|
+
|
|
87
|
+
</Tab.Pane>
|
|
88
|
+
<Tab.Pane eventKey="warnings">
|
|
89
|
+
{hasWarnings ? (
|
|
90
|
+
<Card className="border-warning">
|
|
91
|
+
<Card.Header className="bg-warning text-white d-flex justify-content-between align-items-center">
|
|
92
|
+
<span>Build Warnings ({logs.warnings.length})</span>
|
|
93
|
+
<Badge bg="light" text="dark">
|
|
94
|
+
{new Date().toLocaleString()}
|
|
95
|
+
</Badge>
|
|
96
|
+
</Card.Header>
|
|
97
|
+
<Card.Body className="p-0">
|
|
98
|
+
<ListGroup variant="flush">
|
|
99
|
+
{logs.warnings.map((warn: any, i: number) => (
|
|
100
|
+
<ListGroup.Item key={i} className="text-warning">
|
|
101
|
+
<div className="d-flex justify-content-between">
|
|
102
|
+
<strong>
|
|
103
|
+
{warn.location?.file || 'Unknown file'}
|
|
104
|
+
{warn.location?.line && `:${warn.location.line}`}
|
|
105
|
+
</strong>
|
|
106
|
+
<small className="text-muted">
|
|
107
|
+
{warn.pluginName ? `[${warn.pluginName}]` : ''}
|
|
108
|
+
</small>
|
|
109
|
+
</div>
|
|
110
|
+
<div className="mt-1">
|
|
111
|
+
<pre className="mb-0 p-2 bg-light rounded">
|
|
112
|
+
{warn.text || warn.message || JSON.stringify(warn)}
|
|
113
|
+
</pre>
|
|
114
|
+
</div>
|
|
115
|
+
{warn.detail && (
|
|
116
|
+
<div className="mt-1 small text-muted">
|
|
117
|
+
<pre className="mb-0 p-2 bg-light rounded">
|
|
118
|
+
{warn.detail}
|
|
119
|
+
</pre>
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
122
|
+
</ListGroup.Item>
|
|
123
|
+
))}
|
|
124
|
+
</ListGroup>
|
|
125
|
+
</Card.Body>
|
|
126
|
+
</Card>
|
|
127
|
+
) : (
|
|
128
|
+
<Alert variant="info">No warnings found</Alert>
|
|
129
|
+
)}
|
|
130
|
+
</Tab.Pane>
|
|
131
|
+
<Tab.Pane eventKey="errors">
|
|
132
|
+
{hasErrors ? (
|
|
133
|
+
<Card className="border-danger">
|
|
134
|
+
<Card.Header className="bg-danger text-white d-flex justify-content-between align-items-center">
|
|
135
|
+
<span>Build Errors ({logs.errors.length})</span>
|
|
136
|
+
<Badge bg="light" text="dark">
|
|
137
|
+
{new Date().toLocaleString()}
|
|
138
|
+
</Badge>
|
|
139
|
+
</Card.Header>
|
|
140
|
+
<Card.Body className="p-0">
|
|
141
|
+
<ListGroup variant="flush">
|
|
142
|
+
{logs.errors.map((err: any, i: number) => (
|
|
143
|
+
<ListGroup.Item key={i} className="text-danger">
|
|
144
|
+
<div className="d-flex justify-content-between">
|
|
145
|
+
<strong>
|
|
146
|
+
{err.location?.file || 'Unknown file'}
|
|
147
|
+
{err.location?.line && `:${err.location.line}`}
|
|
148
|
+
</strong>
|
|
149
|
+
<small className="text-muted">
|
|
150
|
+
{err.pluginName ? `[${err.pluginName}]` : ''}
|
|
151
|
+
</small>
|
|
152
|
+
</div>
|
|
153
|
+
<div className="mt-1">
|
|
154
|
+
<pre className="mb-0 p-2 bg-light rounded">
|
|
155
|
+
{err.text || err.message || JSON.stringify(err)}
|
|
156
|
+
</pre>
|
|
157
|
+
</div>
|
|
158
|
+
{err.detail && (
|
|
159
|
+
<div className="mt-1 small text-muted">
|
|
160
|
+
<pre className="mb-0 p-2 bg-light rounded">
|
|
161
|
+
{err.detail}
|
|
162
|
+
</pre>
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
165
|
+
</ListGroup.Item>
|
|
166
|
+
))}
|
|
167
|
+
</ListGroup>
|
|
168
|
+
</Card.Body>
|
|
169
|
+
</Card>
|
|
170
|
+
) : (
|
|
171
|
+
<Alert variant="success">
|
|
172
|
+
<h5>No Errors Found</h5>
|
|
173
|
+
<p className="mb-0">The build completed without any errors.</p>
|
|
174
|
+
</Alert>
|
|
175
|
+
)}
|
|
176
|
+
</Tab.Pane>
|
|
177
|
+
</Tab.Content>
|
|
178
|
+
</Tab.Container>
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
};
|
|
182
|
+
import { Navbar, Nav, Tab, Container, Alert, Badge, Table, Button, Card } from 'react-bootstrap';
|
|
3
183
|
import { useParams, useNavigate, useLocation, Link } from 'react-router-dom';
|
|
4
184
|
|
|
5
185
|
import { ISummary } from './Types';
|
|
@@ -95,24 +275,37 @@ export const ProjectPage = () => {
|
|
|
95
275
|
title={projectName}
|
|
96
276
|
backLink="/"
|
|
97
277
|
navItems={[
|
|
98
|
-
{
|
|
278
|
+
{
|
|
279
|
+
to: `#tests`,
|
|
280
|
+
label: Object.values(summary).some(t => t.runTimeErrors > 0) ? '❌ Tests' :
|
|
281
|
+
Object.values(summary).some(t => t.typeErrors > 0 || t.staticErrors > 0) ? '⚠️ Tests' : '✅ Tests',
|
|
282
|
+
active: route === 'tests',
|
|
283
|
+
className: Object.values(summary).some(t => t.runTimeErrors > 0) ? 'text-danger fw-bold' :
|
|
284
|
+
Object.values(summary).some(t => t.typeErrors > 0 || t.staticErrors > 0) ? 'text-warning fw-bold' : ''
|
|
285
|
+
},
|
|
99
286
|
{
|
|
100
287
|
to: `#node`,
|
|
101
|
-
label: nodeLogs?.errors?.length ? '❌ Node Build' :
|
|
288
|
+
label: nodeLogs?.errors?.length ? '❌ Node Build' :
|
|
289
|
+
nodeLogs?.warnings?.length ? '⚠️ Node Build' : 'Node Build',
|
|
102
290
|
active: route === 'node',
|
|
103
|
-
className: nodeLogs?.errors?.length ? 'text-danger fw-bold' :
|
|
291
|
+
className: nodeLogs?.errors?.length ? 'text-danger fw-bold' :
|
|
292
|
+
nodeLogs?.warnings?.length ? 'text-warning fw-bold' : ''
|
|
104
293
|
},
|
|
105
294
|
{
|
|
106
295
|
to: `#web`,
|
|
107
|
-
label: webLogs?.errors?.length ? '❌ Web Build' :
|
|
296
|
+
label: webLogs?.errors?.length ? '❌ Web Build' :
|
|
297
|
+
webLogs?.warnings?.length ? '⚠️ Web Build' : 'Web Build',
|
|
108
298
|
active: route === 'web',
|
|
109
|
-
className: webLogs?.errors?.length ? 'text-danger fw-bold' :
|
|
299
|
+
className: webLogs?.errors?.length ? 'text-danger fw-bold' :
|
|
300
|
+
webLogs?.warnings?.length ? 'text-warning fw-bold' : ''
|
|
110
301
|
},
|
|
111
302
|
{
|
|
112
303
|
to: `#pure`,
|
|
113
|
-
label: pureLogs?.errors?.length ? '❌ Pure Build' :
|
|
304
|
+
label: pureLogs?.errors?.length ? '❌ Pure Build' :
|
|
305
|
+
pureLogs?.warnings?.length ? '⚠️ Pure Build' : 'Pure Build',
|
|
114
306
|
active: route === 'pure',
|
|
115
|
-
className: pureLogs?.errors?.length ? 'text-danger fw-bold' :
|
|
307
|
+
className: pureLogs?.errors?.length ? 'text-danger fw-bold' :
|
|
308
|
+
pureLogs?.warnings?.length ? 'text-warning fw-bold' : ''
|
|
116
309
|
},
|
|
117
310
|
]}
|
|
118
311
|
/>
|
|
@@ -129,7 +322,6 @@ export const ProjectPage = () => {
|
|
|
129
322
|
<thead>
|
|
130
323
|
<tr>
|
|
131
324
|
<th>Test</th>
|
|
132
|
-
<th>Build logs</th>
|
|
133
325
|
<th>BDD Errors</th>
|
|
134
326
|
<th>Type Errors</th>
|
|
135
327
|
<th>Lint Errors</th>
|
|
@@ -147,11 +339,6 @@ export const ProjectPage = () => {
|
|
|
147
339
|
{testName}
|
|
148
340
|
</a>
|
|
149
341
|
</td>
|
|
150
|
-
<td>
|
|
151
|
-
<a href={`#/projects/${projectName}#${runTime}`}>
|
|
152
|
-
{runTime} {testData.runTimeErrors === 0 ? '✅' : '❌'}
|
|
153
|
-
</a>
|
|
154
|
-
</td>
|
|
155
342
|
<td>
|
|
156
343
|
<a href={`#/projects/${projectName}/tests/${encodeURIComponent(testName)}/${runTime}#results`}>
|
|
157
344
|
{testData.runTimeErrors === 0 ? '✅ Passed' :
|
|
@@ -176,34 +363,13 @@ export const ProjectPage = () => {
|
|
|
176
363
|
</Table>
|
|
177
364
|
</Tab.Pane>
|
|
178
365
|
<Tab.Pane eventKey="node">
|
|
179
|
-
<
|
|
180
|
-
{nodeLogs.errors.map((err, i) => (
|
|
181
|
-
<li key={i}>{err.text || err.message || JSON.stringify(err)}</li>
|
|
182
|
-
))}
|
|
183
|
-
</ul>
|
|
184
|
-
<pre className="bg-dark text-white p-3">
|
|
185
|
-
{nodeLogs ? JSON.stringify(nodeLogs, null, 2) : 'Loading node build logs...'}
|
|
186
|
-
</pre>
|
|
366
|
+
<BuildLogViewer logs={nodeLogs} runtime="Node" />
|
|
187
367
|
</Tab.Pane>
|
|
188
368
|
<Tab.Pane eventKey="web">
|
|
189
|
-
<
|
|
190
|
-
{webLogs.errors.map((err, i) => (
|
|
191
|
-
<li key={i}>{err.text || err.message || JSON.stringify(err)}</li>
|
|
192
|
-
))}
|
|
193
|
-
</ul>
|
|
194
|
-
<pre className="bg-dark text-white p-3">
|
|
195
|
-
{webLogs ? JSON.stringify(webLogs, null, 2) : 'Loading web build logs...'}
|
|
196
|
-
</pre>
|
|
369
|
+
<BuildLogViewer logs={webLogs} runtime="Web" />
|
|
197
370
|
</Tab.Pane>
|
|
198
371
|
<Tab.Pane eventKey="pure">
|
|
199
|
-
<
|
|
200
|
-
{pureLogs.errors.map((err, i) => (
|
|
201
|
-
<li key={i}>{err.text || err.message || JSON.stringify(err)}</li>
|
|
202
|
-
))}
|
|
203
|
-
</ul>
|
|
204
|
-
<pre className="bg-dark text-white p-3">
|
|
205
|
-
{pureLogs ? JSON.stringify(pureLogs, null, 2) : 'Loading pure build logs...'}
|
|
206
|
-
</pre>
|
|
372
|
+
<BuildLogViewer logs={pureLogs} runtime="Pure" />
|
|
207
373
|
</Tab.Pane>
|
|
208
374
|
</Tab.Content>
|
|
209
375
|
</Tab.Container>
|
package/src/ProjectsPage.tsx
CHANGED
|
@@ -54,9 +54,9 @@ export const ProjectsPage = () => {
|
|
|
54
54
|
return {
|
|
55
55
|
name,
|
|
56
56
|
testCount: Object.keys(summary).length,
|
|
57
|
-
nodeStatus: nodeData.errors?.length ? 'failed' : 'success',
|
|
58
|
-
webStatus: webData.errors?.length ? 'failed' : 'success',
|
|
59
|
-
pureStatus: pureData.errors?.length ? 'failed' : 'success',
|
|
57
|
+
nodeStatus: nodeData.errors?.length ? 'failed' : nodeData.warnings?.length ? 'warning' : 'success',
|
|
58
|
+
webStatus: webData.errors?.length ? 'failed' : webData.warnings?.length ? 'warning' : 'success',
|
|
59
|
+
pureStatus: pureData.errors?.length ? 'failed' : pureData.warnings?.length ? 'warning' : 'success',
|
|
60
60
|
config: Object.keys(configData).length,
|
|
61
61
|
};
|
|
62
62
|
})
|
|
@@ -77,6 +77,7 @@ export const ProjectsPage = () => {
|
|
|
77
77
|
switch (status) {
|
|
78
78
|
case 'success': return '✅';
|
|
79
79
|
case 'failed': return '❌';
|
|
80
|
+
case 'warning': return '⚠️';
|
|
80
81
|
default: return '❓';
|
|
81
82
|
}
|
|
82
83
|
};
|
|
@@ -112,11 +113,17 @@ export const ProjectsPage = () => {
|
|
|
112
113
|
<div style={{ maxHeight: '200px', overflowY: 'auto' }}>
|
|
113
114
|
{summaries[project.name] ? (
|
|
114
115
|
Object.keys(summaries[project.name]).map(testName => {
|
|
116
|
+
const testData = summaries[project.name][testName];
|
|
115
117
|
const runTime = configs[project.name].tests.find((t) => t[0] === testName)[1];
|
|
118
|
+
const hasRuntimeErrors = testData.runTimeErrors > 0;
|
|
119
|
+
const hasStaticErrors = testData.typeErrors > 0 || testData.staticErrors > 0;
|
|
116
120
|
|
|
117
121
|
return (
|
|
118
122
|
<div key={testName}>
|
|
119
|
-
<a
|
|
123
|
+
<a
|
|
124
|
+
href={`#/projects/${project.name}/tests/${encodeURIComponent(testName)}/${runTime}`}
|
|
125
|
+
>
|
|
126
|
+
{hasRuntimeErrors ? '❌ ' : hasStaticErrors ? '⚠️ ' : ''}
|
|
120
127
|
{testName.split('/').pop()}
|
|
121
128
|
</a>
|
|
122
129
|
</div>
|
|
@@ -128,27 +135,24 @@ export const ProjectsPage = () => {
|
|
|
128
135
|
</div>
|
|
129
136
|
</td>
|
|
130
137
|
<td>
|
|
131
|
-
<a
|
|
132
|
-
{
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
)}
|
|
138
|
+
<a
|
|
139
|
+
href={`#/projects/${project.name}#node`}
|
|
140
|
+
>
|
|
141
|
+
{getStatusIcon(project.nodeStatus)} Node build logs
|
|
136
142
|
</a>
|
|
137
143
|
</td>
|
|
138
144
|
<td>
|
|
139
|
-
<a
|
|
140
|
-
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
)}
|
|
145
|
+
<a
|
|
146
|
+
href={`#/projects/${project.name}#web`}
|
|
147
|
+
>
|
|
148
|
+
{getStatusIcon(project.webStatus)} Web build logs
|
|
144
149
|
</a>
|
|
145
150
|
</td>
|
|
146
151
|
<td>
|
|
147
|
-
<a
|
|
148
|
-
{
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
)}
|
|
152
|
+
<a
|
|
153
|
+
href={`#/projects/${project.name}#pure`}
|
|
154
|
+
>
|
|
155
|
+
{getStatusIcon(project.pureStatus)} Pure build logs
|
|
152
156
|
</a>
|
|
153
157
|
</td>
|
|
154
158
|
</tr>
|
package/src/TestPage.tsx
CHANGED
|
@@ -43,6 +43,13 @@ export const TestPage = () => {
|
|
|
43
43
|
const [lintErrors, setLintErrors] = useState<string>('');
|
|
44
44
|
const [loading, setLoading] = useState(true);
|
|
45
45
|
const [error, setError] = useState<string | null>(null);
|
|
46
|
+
const [testsExist, setTestsExist] = useState<boolean>(true);
|
|
47
|
+
const [errorCounts, setErrorCounts] = useState({
|
|
48
|
+
typeErrors: 0,
|
|
49
|
+
staticErrors: 0,
|
|
50
|
+
runTimeErrors: 0
|
|
51
|
+
});
|
|
52
|
+
const [summary, setSummary] = useState<any>(null);
|
|
46
53
|
|
|
47
54
|
const { projectName, '*': splat } = useParams();
|
|
48
55
|
const pathParts = splat ? splat.split('/') : [];
|
|
@@ -57,13 +64,36 @@ export const TestPage = () => {
|
|
|
57
64
|
|
|
58
65
|
const fetchData = async () => {
|
|
59
66
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
// First fetch test data
|
|
68
|
+
const testResponse = await fetchTestData(projectName, testPath, runtime);
|
|
69
|
+
setTestData(testResponse.testData);
|
|
70
|
+
setTestsExist(!!testResponse.testData);
|
|
71
|
+
setLogs(testResponse.logs);
|
|
72
|
+
setTypeErrors(testResponse.typeErrors);
|
|
73
|
+
setLintErrors(testResponse.lintErrors);
|
|
74
|
+
|
|
75
|
+
// Then fetch summary.json
|
|
76
|
+
try {
|
|
77
|
+
const summaryResponse = await fetch(`reports/${projectName}/summary.json`);
|
|
78
|
+
if (!summaryResponse.ok) throw new Error('Failed to fetch summary');
|
|
79
|
+
const allSummaries = await summaryResponse.json();
|
|
80
|
+
const testSummary = allSummaries[testPath];
|
|
81
|
+
|
|
82
|
+
console.log("testSummary", testSummary)
|
|
83
|
+
if (testSummary) {
|
|
84
|
+
setSummary(testSummary);
|
|
85
|
+
setErrorCounts({
|
|
86
|
+
typeErrors: testSummary.typeErrors || 0,
|
|
87
|
+
staticErrors: testSummary.staticErrors || 0,
|
|
88
|
+
runTimeErrors: testSummary.runTimeErrors || 0
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error('Failed to load summary:', err);
|
|
93
|
+
}
|
|
65
94
|
} catch (err) {
|
|
66
95
|
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
96
|
+
setTestsExist(false);
|
|
67
97
|
} finally {
|
|
68
98
|
setLoading(false);
|
|
69
99
|
}
|
|
@@ -83,30 +113,30 @@ export const TestPage = () => {
|
|
|
83
113
|
navItems={[
|
|
84
114
|
{
|
|
85
115
|
to: `#results`,
|
|
86
|
-
label:
|
|
116
|
+
label: !testsExist
|
|
87
117
|
? '❌ BDD'
|
|
88
|
-
:
|
|
118
|
+
: testData?.givens.some(g => g.whens.some(w => w.error) || g.thens.some(t => t.error))
|
|
119
|
+
? '❌ BDD'
|
|
120
|
+
: '✅ BDD',
|
|
89
121
|
active: route === 'results'
|
|
90
122
|
},
|
|
91
123
|
{
|
|
92
124
|
to: `#logs`,
|
|
93
|
-
label:
|
|
94
|
-
? '❌ Logs'
|
|
95
|
-
: '✅ Logs',
|
|
125
|
+
label: `Runtime logs`,
|
|
96
126
|
active: route === 'logs'
|
|
97
127
|
},
|
|
98
128
|
{
|
|
99
129
|
to: `#types`,
|
|
100
|
-
label: typeErrors
|
|
101
|
-
?
|
|
102
|
-
: '✅
|
|
130
|
+
label: errorCounts.typeErrors > 0
|
|
131
|
+
? `tsc (❌ * ${errorCounts.typeErrors})`
|
|
132
|
+
: 'tsc ✅ ',
|
|
103
133
|
active: route === 'types'
|
|
104
134
|
},
|
|
105
135
|
{
|
|
106
136
|
to: `#lint`,
|
|
107
|
-
label:
|
|
108
|
-
?
|
|
109
|
-
: '✅
|
|
137
|
+
label: errorCounts.staticErrors > 0
|
|
138
|
+
? `eslint (❌ *${errorCounts.staticErrors}) `
|
|
139
|
+
: 'eslint ✅',
|
|
110
140
|
active: route === 'lint'
|
|
111
141
|
},
|
|
112
142
|
|
|
@@ -114,7 +144,18 @@ export const TestPage = () => {
|
|
|
114
144
|
rightContent={
|
|
115
145
|
<Button
|
|
116
146
|
variant="info"
|
|
117
|
-
onClick={() =>
|
|
147
|
+
onClick={async () => {
|
|
148
|
+
try {
|
|
149
|
+
const promptPath = `testeranto/reports/${projectName}/${testPath.split('.').slice(0, -1).join('.')}/${runtime}/prompt.txt`;
|
|
150
|
+
const messagePath = `testeranto/reports/${projectName}/${testPath.split('.').slice(0, -1).join('.')}/${runtime}/message.txt`;
|
|
151
|
+
const command = `aider --load ${promptPath} --message-file ${messagePath}`;
|
|
152
|
+
await navigator.clipboard.writeText(command);
|
|
153
|
+
alert("Copied aider command to clipboard!");
|
|
154
|
+
} catch (err) {
|
|
155
|
+
alert("Failed to copy command to clipboard");
|
|
156
|
+
console.error("Copy failed:", err);
|
|
157
|
+
}
|
|
158
|
+
}}
|
|
118
159
|
className="ms-2"
|
|
119
160
|
>
|
|
120
161
|
🤖
|
|
@@ -132,7 +173,27 @@ export const TestPage = () => {
|
|
|
132
173
|
|
|
133
174
|
<Tab.Content className="mt-3">
|
|
134
175
|
<Tab.Pane eventKey="results">
|
|
135
|
-
{
|
|
176
|
+
{!testsExist ? (
|
|
177
|
+
<Alert variant="danger" className="mt-3">
|
|
178
|
+
<h4>Tests did not run to completion</h4>
|
|
179
|
+
<p>The test results file (tests.json) was not found or could not be loaded.</p>
|
|
180
|
+
<div className="mt-3">
|
|
181
|
+
<Button
|
|
182
|
+
variant="outline-light"
|
|
183
|
+
onClick={() => setRoute('logs')}
|
|
184
|
+
className="me-2"
|
|
185
|
+
>
|
|
186
|
+
View Runtime Logs
|
|
187
|
+
</Button>
|
|
188
|
+
<Button
|
|
189
|
+
variant="outline-light"
|
|
190
|
+
onClick={() => navigate(`/projects/${projectName}#${runtime}`)}
|
|
191
|
+
>
|
|
192
|
+
View Build Logs
|
|
193
|
+
</Button>
|
|
194
|
+
</div>
|
|
195
|
+
</Alert>
|
|
196
|
+
) : testData ? (
|
|
136
197
|
<div className="test-results">
|
|
137
198
|
<div className="mb-3">
|
|
138
199
|
|