@nbakka/mcp-appium 2.0.88 → 2.0.90
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/lib/review-ui/index.html +391 -25
- package/lib/server.js +87 -73
- package/package.json +1 -1
package/lib/review-ui/index.html
CHANGED
|
@@ -1,38 +1,404 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
2
|
+
<html lang="en">
|
|
3
3
|
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
6
|
<title>Test Case Review & Approval</title>
|
|
5
|
-
<
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 20px;
|
|
12
|
+
background-color: #f5f5f5;
|
|
13
|
+
line-height: 1.6;
|
|
14
|
+
}
|
|
15
|
+
.container {
|
|
16
|
+
max-width: 1200px;
|
|
17
|
+
margin: 0 auto;
|
|
18
|
+
background: white;
|
|
19
|
+
padding: 30px;
|
|
20
|
+
border-radius: 12px;
|
|
21
|
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
22
|
+
}
|
|
23
|
+
.section {
|
|
24
|
+
margin-bottom: 30px;
|
|
25
|
+
padding: 25px;
|
|
26
|
+
border: 2px solid #ddd;
|
|
27
|
+
border-radius: 8px;
|
|
28
|
+
position: relative;
|
|
29
|
+
}
|
|
30
|
+
.section h2 {
|
|
31
|
+
margin-top: 0;
|
|
32
|
+
margin-bottom: 20px;
|
|
33
|
+
padding-bottom: 10px;
|
|
34
|
+
border-bottom: 2px solid;
|
|
35
|
+
}
|
|
36
|
+
.new {
|
|
37
|
+
background-color: #e8f5e8;
|
|
38
|
+
border-color: #4caf50;
|
|
39
|
+
}
|
|
40
|
+
.new h2 { border-bottom-color: #4caf50; color: #2e7d32; }
|
|
41
|
+
.modify {
|
|
42
|
+
background-color: #fff8e1;
|
|
43
|
+
border-color: #ff9800;
|
|
44
|
+
}
|
|
45
|
+
.modify h2 { border-bottom-color: #ff9800; color: #f57c00; }
|
|
46
|
+
.remove {
|
|
47
|
+
background-color: #ffebee;
|
|
48
|
+
border-color: #f44336;
|
|
49
|
+
}
|
|
50
|
+
.remove h2 { border-bottom-color: #f44336; color: #c62828; }
|
|
51
|
+
.test-case {
|
|
52
|
+
margin: 15px 0;
|
|
53
|
+
padding: 20px;
|
|
54
|
+
background: white;
|
|
55
|
+
border-radius: 6px;
|
|
56
|
+
border: 1px solid #e0e0e0;
|
|
57
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
|
58
|
+
}
|
|
59
|
+
.test-case-header {
|
|
60
|
+
display: flex;
|
|
61
|
+
justify-content: space-between;
|
|
62
|
+
align-items: center;
|
|
63
|
+
margin-bottom: 15px;
|
|
64
|
+
}
|
|
65
|
+
.test-case-id {
|
|
66
|
+
font-weight: bold;
|
|
67
|
+
color: #666;
|
|
68
|
+
font-size: 0.9em;
|
|
69
|
+
}
|
|
70
|
+
.edit-btn, .save-btn, .cancel-btn-inline {
|
|
71
|
+
padding: 6px 12px;
|
|
72
|
+
border: none;
|
|
73
|
+
border-radius: 4px;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
font-size: 0.85em;
|
|
76
|
+
margin-left: 5px;
|
|
77
|
+
}
|
|
78
|
+
.edit-btn {
|
|
79
|
+
background-color: #2196f3;
|
|
80
|
+
color: white;
|
|
81
|
+
}
|
|
82
|
+
.save-btn {
|
|
83
|
+
background-color: #4caf50;
|
|
84
|
+
color: white;
|
|
85
|
+
}
|
|
86
|
+
.cancel-btn-inline {
|
|
87
|
+
background-color: #757575;
|
|
88
|
+
color: white;
|
|
89
|
+
}
|
|
90
|
+
.original-text {
|
|
91
|
+
color: #999;
|
|
92
|
+
text-decoration: line-through;
|
|
93
|
+
font-style: italic;
|
|
94
|
+
margin-bottom: 10px;
|
|
95
|
+
}
|
|
96
|
+
.modified-text {
|
|
97
|
+
color: #000;
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
}
|
|
100
|
+
.editable-area {
|
|
101
|
+
width: 100%;
|
|
102
|
+
min-height: 60px;
|
|
103
|
+
padding: 10px;
|
|
104
|
+
border: 2px solid #ddd;
|
|
105
|
+
border-radius: 4px;
|
|
106
|
+
font-family: inherit;
|
|
107
|
+
font-size: 14px;
|
|
108
|
+
resize: vertical;
|
|
109
|
+
}
|
|
110
|
+
.editable-area:focus {
|
|
111
|
+
outline: none;
|
|
112
|
+
border-color: #2196f3;
|
|
113
|
+
}
|
|
114
|
+
.button-container {
|
|
115
|
+
text-align: center;
|
|
116
|
+
margin-top: 40px;
|
|
117
|
+
padding-top: 30px;
|
|
118
|
+
border-top: 2px solid #eee;
|
|
119
|
+
}
|
|
120
|
+
.main-button {
|
|
121
|
+
padding: 15px 30px;
|
|
122
|
+
margin: 0 15px;
|
|
123
|
+
border: none;
|
|
124
|
+
border-radius: 8px;
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
font-size: 16px;
|
|
127
|
+
font-weight: 600;
|
|
128
|
+
text-transform: uppercase;
|
|
129
|
+
letter-spacing: 0.5px;
|
|
130
|
+
transition: all 0.3s ease;
|
|
131
|
+
}
|
|
132
|
+
.approve {
|
|
133
|
+
background-color: #4caf50;
|
|
134
|
+
color: white;
|
|
135
|
+
}
|
|
136
|
+
.approve:hover {
|
|
137
|
+
background-color: #45a049;
|
|
138
|
+
transform: translateY(-2px);
|
|
139
|
+
}
|
|
140
|
+
.cancel {
|
|
141
|
+
background-color: #f44336;
|
|
142
|
+
color: white;
|
|
143
|
+
}
|
|
144
|
+
.cancel:hover {
|
|
145
|
+
background-color: #da190b;
|
|
146
|
+
transform: translateY(-2px);
|
|
147
|
+
}
|
|
148
|
+
.status {
|
|
149
|
+
margin-top: 20px;
|
|
150
|
+
padding: 15px;
|
|
151
|
+
border-radius: 6px;
|
|
152
|
+
text-align: center;
|
|
153
|
+
font-weight: 500;
|
|
154
|
+
}
|
|
155
|
+
.status.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
|
156
|
+
.status.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
|
157
|
+
.status.info { background-color: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }
|
|
158
|
+
#loading {
|
|
159
|
+
text-align: center;
|
|
160
|
+
padding: 50px;
|
|
161
|
+
font-size: 18px;
|
|
162
|
+
color: #666;
|
|
163
|
+
}
|
|
164
|
+
.empty-section {
|
|
165
|
+
text-align: center;
|
|
166
|
+
color: #999;
|
|
167
|
+
font-style: italic;
|
|
168
|
+
padding: 30px;
|
|
169
|
+
}
|
|
170
|
+
</style>
|
|
6
171
|
</head>
|
|
7
172
|
<body>
|
|
8
173
|
<div class="container">
|
|
9
|
-
<h1>Review
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
174
|
+
<h1>Test Case Review & Approval</h1>
|
|
175
|
+
<div id="loading">Loading test cases...</div>
|
|
176
|
+
<div id="content" style="display: none;">
|
|
177
|
+
<div id="new-section" class="section new">
|
|
178
|
+
<h2>New Test Cases</h2>
|
|
179
|
+
<div id="new-cases"></div>
|
|
180
|
+
</div>
|
|
181
|
+
<div id="modify-section" class="section modify">
|
|
182
|
+
<h2>Modified Test Cases</h2>
|
|
183
|
+
<div id="modify-cases"></div>
|
|
184
|
+
</div>
|
|
185
|
+
<div id="remove-section" class="section remove">
|
|
186
|
+
<h2>Test Cases to Remove</h2>
|
|
187
|
+
<div id="remove-cases"></div>
|
|
188
|
+
</div>
|
|
189
|
+
<div class="button-container">
|
|
190
|
+
<button class="main-button approve" onclick="approveTestCases()">✓ Approve Test Cases</button>
|
|
191
|
+
<button class="main-button cancel" onclick="cancelReview()">✗ Cancel Review</button>
|
|
192
|
+
<div id="status" class="status" style="display: none;"></div>
|
|
193
|
+
</div>
|
|
15
194
|
</div>
|
|
195
|
+
</div>
|
|
16
196
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
</div>
|
|
197
|
+
<script>
|
|
198
|
+
let sessionId;
|
|
199
|
+
let testCases;
|
|
200
|
+
let editingStates = {};
|
|
22
201
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
202
|
+
async function loadTestCases() {
|
|
203
|
+
try {
|
|
204
|
+
const response = await fetch('/api/testcases');
|
|
205
|
+
if (!response.ok) {
|
|
206
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
207
|
+
}
|
|
208
|
+
const data = await response.json();
|
|
209
|
+
sessionId = data.sessionId;
|
|
210
|
+
testCases = data.testCases;
|
|
28
211
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
</div
|
|
212
|
+
document.getElementById('loading').style.display = 'none';
|
|
213
|
+
document.getElementById('content').style.display = 'block';
|
|
214
|
+
|
|
215
|
+
renderTestCases();
|
|
216
|
+
} catch (error) {
|
|
217
|
+
document.getElementById('loading').innerHTML = `<div class="status error">Error loading test cases: ${error.message}</div>`;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function renderTestCases() {
|
|
222
|
+
renderNewCases();
|
|
223
|
+
renderModifyCases();
|
|
224
|
+
renderRemoveCases();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function renderNewCases() {
|
|
228
|
+
const container = document.getElementById('new-cases');
|
|
229
|
+
if (testCases.new && testCases.new.length > 0) {
|
|
230
|
+
container.innerHTML = testCases.new.map((tc, index) => `
|
|
231
|
+
<div class="test-case">
|
|
232
|
+
<div class="test-case-header">
|
|
233
|
+
<span class="test-case-id">ID: ${tc.id}</span>
|
|
234
|
+
<button class="edit-btn" onclick="toggleEdit('new', ${index})">Edit</button>
|
|
235
|
+
</div>
|
|
236
|
+
<div id="new-${index}-display" style="display: block;">
|
|
237
|
+
<strong>Description:</strong> ${tc.description}
|
|
238
|
+
</div>
|
|
239
|
+
<div id="new-${index}-edit" style="display: none;">
|
|
240
|
+
<label><strong>Description:</strong></label>
|
|
241
|
+
<textarea class="editable-area" id="new-${index}-desc">${tc.description}</textarea>
|
|
242
|
+
<div style="margin-top: 10px;">
|
|
243
|
+
<button class="save-btn" onclick="saveEdit('new', ${index})">Save</button>
|
|
244
|
+
<button class="cancel-btn-inline" onclick="cancelEdit('new', ${index})">Cancel</button>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
`).join('');
|
|
249
|
+
} else {
|
|
250
|
+
container.innerHTML = '<div class="empty-section">No new test cases</div>';
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function renderModifyCases() {
|
|
255
|
+
const container = document.getElementById('modify-cases');
|
|
256
|
+
if (testCases.modify && testCases.modify.length > 0) {
|
|
257
|
+
container.innerHTML = testCases.modify.map((tc, index) => `
|
|
258
|
+
<div class="test-case">
|
|
259
|
+
<div class="test-case-header">
|
|
260
|
+
<span class="test-case-id">ID: ${tc.id}</span>
|
|
261
|
+
<button class="edit-btn" onclick="toggleEdit('modify', ${index})">Edit</button>
|
|
262
|
+
</div>
|
|
263
|
+
<div id="modify-${index}-display" style="display: block;">
|
|
264
|
+
<div class="original-text"><strong>Original:</strong> ${tc.original}</div>
|
|
265
|
+
<div class="modified-text"><strong>Modified:</strong> ${tc.modified}</div>
|
|
266
|
+
</div>
|
|
267
|
+
<div id="modify-${index}-edit" style="display: none;">
|
|
268
|
+
<label><strong>Original:</strong></label>
|
|
269
|
+
<textarea class="editable-area" id="modify-${index}-orig" readonly style="background-color: #f5f5f5;">${tc.original}</textarea>
|
|
270
|
+
<label style="margin-top: 10px; display: block;"><strong>Modified:</strong></label>
|
|
271
|
+
<textarea class="editable-area" id="modify-${index}-mod">${tc.modified}</textarea>
|
|
272
|
+
<div style="margin-top: 10px;">
|
|
273
|
+
<button class="save-btn" onclick="saveEdit('modify', ${index})">Save</button>
|
|
274
|
+
<button class="cancel-btn-inline" onclick="cancelEdit('modify', ${index})">Cancel</button>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
`).join('');
|
|
279
|
+
} else {
|
|
280
|
+
container.innerHTML = '<div class="empty-section">No modified test cases</div>';
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function renderRemoveCases() {
|
|
285
|
+
const container = document.getElementById('remove-cases');
|
|
286
|
+
if (testCases.remove && testCases.remove.length > 0) {
|
|
287
|
+
container.innerHTML = testCases.remove.map((tc, index) => `
|
|
288
|
+
<div class="test-case">
|
|
289
|
+
<div class="test-case-header">
|
|
290
|
+
<span class="test-case-id">ID: ${tc.id}</span>
|
|
291
|
+
</div>
|
|
292
|
+
<div>
|
|
293
|
+
<strong>Description:</strong> ${tc.description}
|
|
294
|
+
<div style="margin-top: 10px; color: #c62828; font-weight: 500;">
|
|
295
|
+
⚠️ This test case will be removed
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
`).join('');
|
|
300
|
+
} else {
|
|
301
|
+
container.innerHTML = '<div class="empty-section">No test cases to remove</div>';
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function toggleEdit(type, index) {
|
|
306
|
+
const displayDiv = document.getElementById(`${type}-${index}-display`);
|
|
307
|
+
const editDiv = document.getElementById(`${type}-${index}-edit`);
|
|
308
|
+
|
|
309
|
+
if (displayDiv.style.display === 'block') {
|
|
310
|
+
displayDiv.style.display = 'none';
|
|
311
|
+
editDiv.style.display = 'block';
|
|
312
|
+
editingStates[`${type}-${index}`] = true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function saveEdit(type, index) {
|
|
317
|
+
if (type === 'new') {
|
|
318
|
+
const newDesc = document.getElementById(`new-${index}-desc`).value.trim();
|
|
319
|
+
if (newDesc) {
|
|
320
|
+
testCases.new[index].description = newDesc;
|
|
321
|
+
}
|
|
322
|
+
} else if (type === 'modify') {
|
|
323
|
+
const newMod = document.getElementById(`modify-${index}-mod`).value.trim();
|
|
324
|
+
if (newMod) {
|
|
325
|
+
testCases.modify[index].modified = newMod;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
cancelEdit(type, index);
|
|
330
|
+
renderTestCases();
|
|
331
|
+
showStatus('Changes saved successfully!', 'success');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function cancelEdit(type, index) {
|
|
335
|
+
const displayDiv = document.getElementById(`${type}-${index}-display`);
|
|
336
|
+
const editDiv = document.getElementById(`${type}-${index}-edit`);
|
|
337
|
+
|
|
338
|
+
displayDiv.style.display = 'block';
|
|
339
|
+
editDiv.style.display = 'none';
|
|
340
|
+
delete editingStates[`${type}-${index}`];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function showStatus(message, type) {
|
|
344
|
+
const statusDiv = document.getElementById('status');
|
|
345
|
+
statusDiv.textContent = message;
|
|
346
|
+
statusDiv.className = `status ${type}`;
|
|
347
|
+
statusDiv.style.display = 'block';
|
|
348
|
+
|
|
349
|
+
setTimeout(() => {
|
|
350
|
+
statusDiv.style.display = 'none';
|
|
351
|
+
}, 3000);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function approveTestCases() {
|
|
355
|
+
try {
|
|
356
|
+
showStatus('Processing approval...', 'info');
|
|
357
|
+
const response = await fetch(`/approve/${sessionId}`, {
|
|
358
|
+
method: 'POST',
|
|
359
|
+
headers: { 'Content-Type': 'application/json' },
|
|
360
|
+
body: JSON.stringify(testCases)
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
if (!response.ok) {
|
|
364
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const result = await response.json();
|
|
368
|
+
showStatus(result.message || 'Test cases approved successfully!', 'success');
|
|
369
|
+
|
|
370
|
+
setTimeout(() => {
|
|
371
|
+
window.close();
|
|
372
|
+
}, 2000);
|
|
373
|
+
} catch (error) {
|
|
374
|
+
showStatus(`Error approving test cases: ${error.message}`, 'error');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async function cancelReview() {
|
|
379
|
+
try {
|
|
380
|
+
showStatus('Cancelling review...', 'info');
|
|
381
|
+
const response = await fetch(`/cancel/${sessionId}`, {
|
|
382
|
+
method: 'POST'
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
if (!response.ok) {
|
|
386
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const result = await response.json();
|
|
390
|
+
showStatus(result.message || 'Review cancelled', 'info');
|
|
391
|
+
|
|
392
|
+
setTimeout(() => {
|
|
393
|
+
window.close();
|
|
394
|
+
}, 2000);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
showStatus(`Error cancelling review: ${error.message}`, 'error');
|
|
397
|
+
}
|
|
398
|
+
}
|
|
35
399
|
|
|
36
|
-
|
|
400
|
+
// Load test cases when page loads
|
|
401
|
+
window.onload = loadTestCases;
|
|
402
|
+
</script>
|
|
37
403
|
</body>
|
|
38
404
|
</html>
|
package/lib/server.js
CHANGED
|
@@ -754,7 +754,7 @@ ${guidelines}`
|
|
|
754
754
|
await fs.writeFile(testCasesFilePath, `Error generating test cases: ${error.message}`);
|
|
755
755
|
});
|
|
756
756
|
|
|
757
|
-
return "✅ Test case generation started. Use 'check_testcases_status' tool to check if generation is complete.";
|
|
757
|
+
return "✅ Test case generation started. Use 'check_testcases_status' tool to check if generation is complete. Try max 10 times";
|
|
758
758
|
} catch (err) {
|
|
759
759
|
return `❌ Error starting test case generation: ${err.message}`;
|
|
760
760
|
}
|
|
@@ -768,7 +768,7 @@ tool(
|
|
|
768
768
|
async () => {
|
|
769
769
|
try {
|
|
770
770
|
// Wait for 20 seconds before checking
|
|
771
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
771
|
+
await new Promise(resolve => setTimeout(resolve, 25000));
|
|
772
772
|
|
|
773
773
|
const testCasesFilePath = path.join(__dirname, 'generated-testcases.txt');
|
|
774
774
|
|
|
@@ -809,49 +809,49 @@ tool(
|
|
|
809
809
|
const path = require('path');
|
|
810
810
|
const sessionId = Date.now().toString();
|
|
811
811
|
|
|
812
|
-
//
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
if (type === 'modify' && tc.length >= 4) {
|
|
828
|
-
// Format: [originalDescription, newDescription, "Modify", testCaseId]
|
|
829
|
-
parsedTestCases.modify.push({
|
|
830
|
-
id: tc[3] || 'N/A',
|
|
831
|
-
original: tc[0], // Original description
|
|
832
|
-
modified: tc[1] // New/modified description
|
|
812
|
+
// Add the improved parseTestCases function here
|
|
813
|
+
function parseTestCases(testCasesArray) {
|
|
814
|
+
const parsed = {
|
|
815
|
+
new: [],
|
|
816
|
+
modify: [],
|
|
817
|
+
remove: []
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
testCasesArray.forEach(tc => {
|
|
821
|
+
if (tc.length === 4 && tc[2] === 'Modify') {
|
|
822
|
+
// Modify: [original, modified, "Modify", tcmsId]
|
|
823
|
+
parsed.modify.push({
|
|
824
|
+
id: tc[3],
|
|
825
|
+
original: tc[0],
|
|
826
|
+
modified: tc[1]
|
|
833
827
|
});
|
|
834
|
-
} else if (
|
|
835
|
-
//
|
|
836
|
-
|
|
837
|
-
id: tc
|
|
828
|
+
} else if (tc.length === 3 && tc[1] === 'Remove') {
|
|
829
|
+
// Remove: [description, "Remove", tcmsId]
|
|
830
|
+
parsed.remove.push({
|
|
831
|
+
id: tc[2],
|
|
838
832
|
description: tc[0]
|
|
839
833
|
});
|
|
834
|
+
} else if (tc.length === 2 && tc[1] === 'New') {
|
|
835
|
+
// New: [description, "New"]
|
|
836
|
+
parsed.new.push({
|
|
837
|
+
description: tc[0],
|
|
838
|
+
id: `NEW-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
|
|
839
|
+
});
|
|
840
840
|
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
description: tc[0]
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
});
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
return parsed;
|
|
844
|
+
}
|
|
849
845
|
|
|
846
|
+
// Use the improved parsing function
|
|
847
|
+
const parsedTestCases = parseTestCases(testCases);
|
|
848
|
+
|
|
849
|
+
// Remove the old parsing logic and console.log statements that cause JSON errors
|
|
850
850
|
// Initialize session state
|
|
851
851
|
approvalSessions.set(sessionId, {
|
|
852
852
|
status: 'pending',
|
|
853
853
|
testCases: parsedTestCases,
|
|
854
|
-
originalTestCases: JSON.parse(JSON.stringify(parsedTestCases)),
|
|
854
|
+
originalTestCases: JSON.parse(JSON.stringify(parsedTestCases)),
|
|
855
855
|
server: null,
|
|
856
856
|
startTime: Date.now()
|
|
857
857
|
});
|
|
@@ -859,25 +859,24 @@ tool(
|
|
|
859
859
|
const app = express();
|
|
860
860
|
const port = 3001;
|
|
861
861
|
|
|
862
|
-
// Middleware for JSON parsing
|
|
863
862
|
app.use(express.json());
|
|
864
863
|
app.use(express.static(path.join(__dirname, 'review-ui')));
|
|
865
864
|
|
|
866
|
-
// Main page
|
|
867
865
|
app.get("/", (req, res) => {
|
|
868
866
|
res.sendFile(path.join(__dirname, 'review-ui', 'index.html'));
|
|
869
867
|
});
|
|
870
868
|
|
|
871
|
-
// API to get test cases data
|
|
872
869
|
app.get("/api/testcases", (req, res) => {
|
|
873
870
|
const session = approvalSessions.get(sessionId);
|
|
871
|
+
if (!session) {
|
|
872
|
+
return res.status(404).json({ error: 'Session not found' });
|
|
873
|
+
}
|
|
874
874
|
res.json({
|
|
875
875
|
sessionId: sessionId,
|
|
876
|
-
testCases: session
|
|
876
|
+
testCases: session.testCases
|
|
877
877
|
});
|
|
878
878
|
});
|
|
879
879
|
|
|
880
|
-
// Approval endpoint
|
|
881
880
|
app.post(`/approve/${sessionId}`, (req, res) => {
|
|
882
881
|
const session = approvalSessions.get(sessionId);
|
|
883
882
|
if (session) {
|
|
@@ -885,38 +884,32 @@ tool(
|
|
|
885
884
|
session.finalTestCases = req.body;
|
|
886
885
|
approvalSessions.set(sessionId, session);
|
|
887
886
|
}
|
|
888
|
-
res.
|
|
887
|
+
res.json({ status: 'approved', message: 'Test cases approved successfully!' });
|
|
889
888
|
});
|
|
890
889
|
|
|
891
|
-
// Cancel endpoint
|
|
892
890
|
app.post(`/cancel/${sessionId}`, (req, res) => {
|
|
893
891
|
const session = approvalSessions.get(sessionId);
|
|
894
892
|
if (session) {
|
|
895
893
|
session.status = 'cancelled';
|
|
896
894
|
approvalSessions.set(sessionId, session);
|
|
897
895
|
}
|
|
898
|
-
res.
|
|
896
|
+
res.json({ status: 'cancelled', message: 'Review cancelled' });
|
|
899
897
|
});
|
|
900
898
|
|
|
901
|
-
// Start server
|
|
902
899
|
const server = app.listen(port, async () => {
|
|
903
|
-
console.log
|
|
904
|
-
|
|
900
|
+
// Remove console.log to prevent JSON parsing errors
|
|
905
901
|
try {
|
|
906
902
|
const { default: open } = await import('open');
|
|
907
903
|
await open(`http://localhost:${port}`);
|
|
908
|
-
console.log('Browser opened successfully');
|
|
909
904
|
} catch (openError) {
|
|
910
|
-
|
|
905
|
+
// Silent fail for browser opening
|
|
911
906
|
}
|
|
912
907
|
});
|
|
913
908
|
|
|
914
|
-
// Store server reference in session
|
|
915
909
|
const session = approvalSessions.get(sessionId);
|
|
916
910
|
session.server = server;
|
|
917
911
|
approvalSessions.set(sessionId, session);
|
|
918
912
|
|
|
919
|
-
// Auto cleanup after 5 minutes
|
|
920
913
|
setTimeout(() => {
|
|
921
914
|
const session = approvalSessions.get(sessionId);
|
|
922
915
|
if (session && session.status === 'pending') {
|
|
@@ -926,16 +919,23 @@ tool(
|
|
|
926
919
|
}
|
|
927
920
|
approvalSessions.set(sessionId, session);
|
|
928
921
|
}
|
|
929
|
-
}, 300000);
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
922
|
+
}, 300000);
|
|
923
|
+
|
|
924
|
+
// Return clean JSON response
|
|
925
|
+
return JSON.stringify({
|
|
926
|
+
status: "review_started",
|
|
927
|
+
sessionId: sessionId,
|
|
928
|
+
message: "Test case review interface opened in browser. Use check_approval_status tool to poll for approval.",
|
|
929
|
+
testCasesCount: testCases.length,
|
|
930
|
+
browserUrl: `http://localhost:${port}`,
|
|
931
|
+
instructions: "Poll every 25 seconds using check_approval_status tool until approved or timeout (5 minutes)"
|
|
932
|
+
});
|
|
936
933
|
|
|
937
934
|
} catch (err) {
|
|
938
|
-
return
|
|
935
|
+
return JSON.stringify({
|
|
936
|
+
status: "error",
|
|
937
|
+
message: `Error setting up review interface: ${err.message}`
|
|
938
|
+
});
|
|
939
939
|
}
|
|
940
940
|
}
|
|
941
941
|
);
|
|
@@ -948,9 +948,13 @@ tool(
|
|
|
948
948
|
},
|
|
949
949
|
async ({ sessionId }) => {
|
|
950
950
|
await new Promise(resolve => setTimeout(resolve, 25000));
|
|
951
|
+
|
|
951
952
|
const session = approvalSessions.get(sessionId);
|
|
952
953
|
if (!session) {
|
|
953
|
-
return
|
|
954
|
+
return JSON.stringify({
|
|
955
|
+
status: "error",
|
|
956
|
+
message: `Session not found. Invalid session ID: ${sessionId}`
|
|
957
|
+
});
|
|
954
958
|
}
|
|
955
959
|
|
|
956
960
|
const currentTime = Date.now();
|
|
@@ -959,36 +963,46 @@ tool(
|
|
|
959
963
|
if (session.status === 'approved') {
|
|
960
964
|
const approvedTestCases = session.finalTestCases || session.testCases;
|
|
961
965
|
|
|
962
|
-
// Clean up session and close server
|
|
963
966
|
if (session.server) {
|
|
964
967
|
session.server.close();
|
|
965
968
|
}
|
|
966
969
|
approvalSessions.delete(sessionId);
|
|
967
970
|
|
|
968
|
-
return
|
|
969
|
-
|
|
970
|
-
|
|
971
|
+
return JSON.stringify({
|
|
972
|
+
status: "approved",
|
|
973
|
+
message: "Test cases approved successfully!",
|
|
974
|
+
elapsedTime: elapsedTime,
|
|
975
|
+
approvedTestCases: approvedTestCases
|
|
976
|
+
});
|
|
971
977
|
} else if (session.status === 'cancelled') {
|
|
972
|
-
// Clean up session and close server
|
|
973
978
|
if (session.server) {
|
|
974
979
|
session.server.close();
|
|
975
980
|
}
|
|
976
981
|
approvalSessions.delete(sessionId);
|
|
977
982
|
|
|
978
|
-
return
|
|
979
|
-
|
|
980
|
-
|
|
983
|
+
return JSON.stringify({
|
|
984
|
+
status: "cancelled",
|
|
985
|
+
message: "Review was cancelled by the user",
|
|
986
|
+
elapsedTime: elapsedTime
|
|
987
|
+
});
|
|
988
|
+
} else if (session.status === 'timeout' || elapsedTime > 300) {
|
|
981
989
|
if (session.server) {
|
|
982
990
|
session.server.close();
|
|
983
991
|
}
|
|
984
992
|
approvalSessions.delete(sessionId);
|
|
985
993
|
|
|
986
|
-
return
|
|
994
|
+
return JSON.stringify({
|
|
995
|
+
status: "timeout",
|
|
996
|
+
message: "Review session timed out",
|
|
997
|
+
elapsedTime: elapsedTime
|
|
998
|
+
});
|
|
987
999
|
} else {
|
|
988
|
-
return
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1000
|
+
return JSON.stringify({
|
|
1001
|
+
status: "pending",
|
|
1002
|
+
message: "Test cases are still pending approval",
|
|
1003
|
+
elapsedTime: elapsedTime,
|
|
1004
|
+
remainingTime: Math.max(0, 300 - elapsedTime)
|
|
1005
|
+
});
|
|
992
1006
|
}
|
|
993
1007
|
}
|
|
994
1008
|
);
|