@nbakka/mcp-appium 2.0.89 → 2.0.91
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 +357 -49
- package/lib/server.js +75 -57
- package/package.json +1 -1
package/lib/review-ui/index.html
CHANGED
|
@@ -3,26 +3,182 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Test Case Review</title>
|
|
6
|
+
<title>Test Case Review & Approval</title>
|
|
7
7
|
<style>
|
|
8
|
-
body {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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, .delete-btn {
|
|
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
|
+
.delete-btn {
|
|
91
|
+
background-color: #f44336;
|
|
92
|
+
color: white;
|
|
93
|
+
}
|
|
94
|
+
.delete-btn:hover {
|
|
95
|
+
background-color: #d32f2f;
|
|
96
|
+
}
|
|
97
|
+
.original-text {
|
|
98
|
+
color: #999;
|
|
99
|
+
text-decoration: line-through;
|
|
100
|
+
font-style: italic;
|
|
101
|
+
margin-bottom: 10px;
|
|
102
|
+
}
|
|
103
|
+
.modified-text {
|
|
104
|
+
color: #000;
|
|
105
|
+
font-weight: 500;
|
|
106
|
+
}
|
|
107
|
+
.editable-area {
|
|
108
|
+
width: 100%;
|
|
109
|
+
min-height: 60px;
|
|
110
|
+
padding: 10px;
|
|
111
|
+
border: 2px solid #ddd;
|
|
112
|
+
border-radius: 4px;
|
|
113
|
+
font-family: inherit;
|
|
114
|
+
font-size: 14px;
|
|
115
|
+
resize: vertical;
|
|
116
|
+
}
|
|
117
|
+
.editable-area:focus {
|
|
118
|
+
outline: none;
|
|
119
|
+
border-color: #2196f3;
|
|
120
|
+
}
|
|
121
|
+
.button-container {
|
|
122
|
+
text-align: center;
|
|
123
|
+
margin-top: 40px;
|
|
124
|
+
padding-top: 30px;
|
|
125
|
+
border-top: 2px solid #eee;
|
|
126
|
+
}
|
|
127
|
+
.main-button {
|
|
128
|
+
padding: 15px 30px;
|
|
129
|
+
margin: 0 15px;
|
|
130
|
+
border: none;
|
|
131
|
+
border-radius: 8px;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
font-size: 16px;
|
|
134
|
+
font-weight: 600;
|
|
135
|
+
text-transform: uppercase;
|
|
136
|
+
letter-spacing: 0.5px;
|
|
137
|
+
transition: all 0.3s ease;
|
|
138
|
+
}
|
|
139
|
+
.approve {
|
|
140
|
+
background-color: #4caf50;
|
|
141
|
+
color: white;
|
|
142
|
+
}
|
|
143
|
+
.approve:hover {
|
|
144
|
+
background-color: #45a049;
|
|
145
|
+
transform: translateY(-2px);
|
|
146
|
+
}
|
|
147
|
+
.cancel {
|
|
148
|
+
background-color: #f44336;
|
|
149
|
+
color: white;
|
|
150
|
+
}
|
|
151
|
+
.cancel:hover {
|
|
152
|
+
background-color: #da190b;
|
|
153
|
+
transform: translateY(-2px);
|
|
154
|
+
}
|
|
155
|
+
.status {
|
|
156
|
+
margin-top: 20px;
|
|
157
|
+
padding: 15px;
|
|
158
|
+
border-radius: 6px;
|
|
159
|
+
text-align: center;
|
|
160
|
+
font-weight: 500;
|
|
161
|
+
}
|
|
162
|
+
.status.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
|
163
|
+
.status.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
|
164
|
+
.status.info { background-color: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }
|
|
165
|
+
#loading {
|
|
166
|
+
text-align: center;
|
|
167
|
+
padding: 50px;
|
|
168
|
+
font-size: 18px;
|
|
169
|
+
color: #666;
|
|
170
|
+
}
|
|
171
|
+
.empty-section {
|
|
172
|
+
text-align: center;
|
|
173
|
+
color: #999;
|
|
174
|
+
font-style: italic;
|
|
175
|
+
padding: 30px;
|
|
176
|
+
}
|
|
21
177
|
</style>
|
|
22
178
|
</head>
|
|
23
179
|
<body>
|
|
24
180
|
<div class="container">
|
|
25
|
-
<h1>Test Case Review</h1>
|
|
181
|
+
<h1>Test Case Review & Approval</h1>
|
|
26
182
|
<div id="loading">Loading test cases...</div>
|
|
27
183
|
<div id="content" style="display: none;">
|
|
28
184
|
<div id="new-section" class="section new">
|
|
@@ -37,9 +193,10 @@
|
|
|
37
193
|
<h2>Test Cases to Remove</h2>
|
|
38
194
|
<div id="remove-cases"></div>
|
|
39
195
|
</div>
|
|
40
|
-
<div
|
|
41
|
-
<button class="approve" onclick="
|
|
42
|
-
<button class="cancel" onclick="
|
|
196
|
+
<div class="button-container">
|
|
197
|
+
<button class="main-button approve" onclick="approveTestCases()">✓ Approve Test Cases</button>
|
|
198
|
+
<button class="main-button cancel" onclick="cancelReview()">✗ Cancel Review</button>
|
|
199
|
+
<div id="status" class="status" style="display: none;"></div>
|
|
43
200
|
</div>
|
|
44
201
|
</div>
|
|
45
202
|
</div>
|
|
@@ -47,10 +204,14 @@
|
|
|
47
204
|
<script>
|
|
48
205
|
let sessionId;
|
|
49
206
|
let testCases;
|
|
207
|
+
let editingStates = {};
|
|
50
208
|
|
|
51
209
|
async function loadTestCases() {
|
|
52
210
|
try {
|
|
53
211
|
const response = await fetch('/api/testcases');
|
|
212
|
+
if (!response.ok) {
|
|
213
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
214
|
+
}
|
|
54
215
|
const data = await response.json();
|
|
55
216
|
sessionId = data.sessionId;
|
|
56
217
|
testCases = data.testCases;
|
|
@@ -60,71 +221,218 @@
|
|
|
60
221
|
|
|
61
222
|
renderTestCases();
|
|
62
223
|
} catch (error) {
|
|
63
|
-
document.getElementById('loading').innerHTML =
|
|
224
|
+
document.getElementById('loading').innerHTML = `<div class="status error">Error loading test cases: ${error.message}</div>`;
|
|
64
225
|
}
|
|
65
226
|
}
|
|
66
227
|
|
|
67
228
|
function renderTestCases() {
|
|
68
|
-
|
|
69
|
-
|
|
229
|
+
renderNewCases();
|
|
230
|
+
renderModifyCases();
|
|
231
|
+
renderRemoveCases();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function renderNewCases() {
|
|
235
|
+
const container = document.getElementById('new-cases');
|
|
70
236
|
if (testCases.new && testCases.new.length > 0) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
237
|
+
container.innerHTML = testCases.new.map((tc, index) => `
|
|
238
|
+
<div class="test-case">
|
|
239
|
+
<div class="test-case-header">
|
|
240
|
+
<span class="test-case-id">ID: ${tc.id}</span>
|
|
241
|
+
<button class="edit-btn" onclick="toggleEdit('new', ${index})">Edit</button>
|
|
242
|
+
<button class="delete-btn" onclick="deleteTestCase('new', ${index})">Delete</button>
|
|
243
|
+
</div>
|
|
244
|
+
<div id="new-${index}-display" style="display: block;">
|
|
245
|
+
<strong>Description:</strong> ${tc.description}
|
|
246
|
+
</div>
|
|
247
|
+
<div id="new-${index}-edit" style="display: none;">
|
|
248
|
+
<label><strong>Description:</strong></label>
|
|
249
|
+
<textarea class="editable-area" id="new-${index}-desc">${tc.description}</textarea>
|
|
250
|
+
<div style="margin-top: 10px;">
|
|
251
|
+
<button class="save-btn" onclick="saveEdit('new', ${index})">Save</button>
|
|
252
|
+
<button class="cancel-btn-inline" onclick="cancelEdit('new', ${index})">Cancel</button>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
`).join('');
|
|
74
257
|
} else {
|
|
75
|
-
|
|
258
|
+
container.innerHTML = '<div class="empty-section">No new test cases</div>';
|
|
76
259
|
}
|
|
260
|
+
}
|
|
77
261
|
|
|
78
|
-
|
|
79
|
-
const
|
|
262
|
+
function renderModifyCases() {
|
|
263
|
+
const container = document.getElementById('modify-cases');
|
|
80
264
|
if (testCases.modify && testCases.modify.length > 0) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
265
|
+
container.innerHTML = testCases.modify.map((tc, index) => `
|
|
266
|
+
<div class="test-case">
|
|
267
|
+
<div class="test-case-header">
|
|
268
|
+
<span class="test-case-id">ID: ${tc.id}</span>
|
|
269
|
+
<button class="edit-btn" onclick="toggleEdit('modify', ${index})">Edit</button>
|
|
270
|
+
<button class="delete-btn" onclick="deleteTestCase('modify', ${index})">Delete</button>
|
|
271
|
+
</div>
|
|
272
|
+
<div id="modify-${index}-display" style="display: block;">
|
|
273
|
+
<div class="original-text"><strong>Original:</strong> ${tc.original}</div>
|
|
274
|
+
<div class="modified-text"><strong>Modified:</strong> ${tc.modified}</div>
|
|
275
|
+
</div>
|
|
276
|
+
<div id="modify-${index}-edit" style="display: none;">
|
|
277
|
+
<label><strong>Original:</strong></label>
|
|
278
|
+
<textarea class="editable-area" id="modify-${index}-orig" readonly style="background-color: #f5f5f5;">${tc.original}</textarea>
|
|
279
|
+
<label style="margin-top: 10px; display: block;"><strong>Modified:</strong></label>
|
|
280
|
+
<textarea class="editable-area" id="modify-${index}-mod">${tc.modified}</textarea>
|
|
281
|
+
<div style="margin-top: 10px;">
|
|
282
|
+
<button class="save-btn" onclick="saveEdit('modify', ${index})">Save</button>
|
|
283
|
+
<button class="cancel-btn-inline" onclick="cancelEdit('modify', ${index})">Cancel</button>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
`).join('');
|
|
88
288
|
} else {
|
|
89
|
-
|
|
289
|
+
container.innerHTML = '<div class="empty-section">No modified test cases</div>';
|
|
90
290
|
}
|
|
291
|
+
}
|
|
91
292
|
|
|
92
|
-
|
|
93
|
-
const
|
|
293
|
+
function renderRemoveCases() {
|
|
294
|
+
const container = document.getElementById('remove-cases');
|
|
94
295
|
if (testCases.remove && testCases.remove.length > 0) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
296
|
+
container.innerHTML = testCases.remove.map((tc, index) => `
|
|
297
|
+
<div class="test-case">
|
|
298
|
+
<div class="test-case-header">
|
|
299
|
+
<span class="test-case-id">ID: ${tc.id}</span>
|
|
300
|
+
</div>
|
|
301
|
+
<div>
|
|
302
|
+
<strong>Description:</strong> ${tc.description}
|
|
303
|
+
<div style="margin-top: 10px; color: #c62828; font-weight: 500;">
|
|
304
|
+
⚠️ This test case will be removed
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
`).join('');
|
|
98
309
|
} else {
|
|
99
|
-
|
|
310
|
+
container.innerHTML = '<div class="empty-section">No test cases to remove</div>';
|
|
100
311
|
}
|
|
101
312
|
}
|
|
102
313
|
|
|
103
|
-
|
|
314
|
+
function toggleEdit(type, index) {
|
|
315
|
+
const displayDiv = document.getElementById(`${type}-${index}-display`);
|
|
316
|
+
const editDiv = document.getElementById(`${type}-${index}-edit`);
|
|
317
|
+
|
|
318
|
+
if (displayDiv.style.display === 'block') {
|
|
319
|
+
displayDiv.style.display = 'none';
|
|
320
|
+
editDiv.style.display = 'block';
|
|
321
|
+
editingStates[`${type}-${index}`] = true;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function saveEdit(type, index) {
|
|
326
|
+
if (type === 'new') {
|
|
327
|
+
const newDesc = document.getElementById(`new-${index}-desc`).value.trim();
|
|
328
|
+
if (newDesc) {
|
|
329
|
+
testCases.new[index].description = newDesc;
|
|
330
|
+
}
|
|
331
|
+
} else if (type === 'modify') {
|
|
332
|
+
const newMod = document.getElementById(`modify-${index}-mod`).value.trim();
|
|
333
|
+
if (newMod) {
|
|
334
|
+
testCases.modify[index].modified = newMod;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
cancelEdit(type, index);
|
|
339
|
+
renderTestCases();
|
|
340
|
+
showStatus('Changes saved successfully!', 'success');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function cancelEdit(type, index) {
|
|
344
|
+
const displayDiv = document.getElementById(`${type}-${index}-display`);
|
|
345
|
+
const editDiv = document.getElementById(`${type}-${index}-edit`);
|
|
346
|
+
|
|
347
|
+
displayDiv.style.display = 'block';
|
|
348
|
+
editDiv.style.display = 'none';
|
|
349
|
+
delete editingStates[`${type}-${index}`];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function showStatus(message, type) {
|
|
353
|
+
const statusDiv = document.getElementById('status');
|
|
354
|
+
statusDiv.textContent = message;
|
|
355
|
+
statusDiv.className = `status ${type}`;
|
|
356
|
+
statusDiv.style.display = 'block';
|
|
357
|
+
|
|
358
|
+
setTimeout(() => {
|
|
359
|
+
statusDiv.style.display = 'none';
|
|
360
|
+
}, 3000);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function approveTestCases() {
|
|
104
364
|
try {
|
|
365
|
+
showStatus('Processing approval...', 'info');
|
|
105
366
|
const response = await fetch(`/approve/${sessionId}`, {
|
|
106
367
|
method: 'POST',
|
|
107
368
|
headers: { 'Content-Type': 'application/json' },
|
|
108
369
|
body: JSON.stringify(testCases)
|
|
109
370
|
});
|
|
371
|
+
|
|
372
|
+
if (!response.ok) {
|
|
373
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
374
|
+
}
|
|
375
|
+
|
|
110
376
|
const result = await response.json();
|
|
111
|
-
|
|
112
|
-
|
|
377
|
+
showStatus(result.message || 'Test cases approved successfully!', 'success');
|
|
378
|
+
|
|
379
|
+
setTimeout(() => {
|
|
380
|
+
window.close();
|
|
381
|
+
}, 2000);
|
|
113
382
|
} catch (error) {
|
|
114
|
-
|
|
383
|
+
showStatus(`Error approving test cases: ${error.message}`, 'error');
|
|
115
384
|
}
|
|
116
385
|
}
|
|
117
386
|
|
|
118
|
-
async function
|
|
387
|
+
async function cancelReview() {
|
|
119
388
|
try {
|
|
389
|
+
showStatus('Cancelling review...', 'info');
|
|
120
390
|
const response = await fetch(`/cancel/${sessionId}`, {
|
|
121
391
|
method: 'POST'
|
|
122
392
|
});
|
|
393
|
+
|
|
394
|
+
if (!response.ok) {
|
|
395
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
396
|
+
}
|
|
397
|
+
|
|
123
398
|
const result = await response.json();
|
|
124
|
-
|
|
125
|
-
|
|
399
|
+
showStatus(result.message || 'Review cancelled', 'info');
|
|
400
|
+
|
|
401
|
+
setTimeout(() => {
|
|
402
|
+
window.close();
|
|
403
|
+
}, 2000);
|
|
404
|
+
} catch (error) {
|
|
405
|
+
showStatus(`Error cancelling review: ${error.message}`, 'error');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function deleteTestCase(type, index) {
|
|
410
|
+
const confirmation = confirm("Are you sure you want to delete this test case?");
|
|
411
|
+
if (!confirmation) return;
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
showStatus('Deleting test case...', 'info');
|
|
415
|
+
const response = await fetch(`/delete/${sessionId}`, {
|
|
416
|
+
method: 'POST',
|
|
417
|
+
headers: { 'Content-Type': 'application/json' },
|
|
418
|
+
body: JSON.stringify({ type, index })
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
if (!response.ok) {
|
|
422
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Remove the deleted test case from the local state
|
|
426
|
+
if (type === 'new') {
|
|
427
|
+
testCases.new.splice(index, 1);
|
|
428
|
+
} else if (type === 'modify') {
|
|
429
|
+
testCases.modify.splice(index, 1);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
renderTestCases();
|
|
433
|
+
showStatus('Test case deleted successfully!', 'success');
|
|
126
434
|
} catch (error) {
|
|
127
|
-
|
|
435
|
+
showStatus(`Error deleting test case: ${error.message}`, 'error');
|
|
128
436
|
}
|
|
129
437
|
}
|
|
130
438
|
|
package/lib/server.js
CHANGED
|
@@ -809,48 +809,44 @@ tool(
|
|
|
809
809
|
const path = require('path');
|
|
810
810
|
const sessionId = Date.now().toString();
|
|
811
811
|
|
|
812
|
-
//
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
812
|
+
// Add the improved parseTestCases function here
|
|
813
|
+
function parseTestCases(testCasesArray) {
|
|
814
|
+
const parsed = {
|
|
815
|
+
new: [],
|
|
816
|
+
modify: [],
|
|
817
|
+
remove: []
|
|
818
|
+
};
|
|
818
819
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
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]
|
|
827
|
+
});
|
|
828
|
+
} else if (tc.length === 3 && tc[1] === 'Remove') {
|
|
829
|
+
// Remove: [description, "Remove", tcmsId]
|
|
830
|
+
parsed.remove.push({
|
|
831
|
+
id: tc[2],
|
|
832
|
+
description: tc[0]
|
|
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
|
+
}
|
|
841
|
+
});
|
|
824
842
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (lastElement?.toLowerCase() === 'new') {
|
|
829
|
-
// Format: [description, "New"]
|
|
830
|
-
parsedTestCases.new.push({
|
|
831
|
-
description: tc[0],
|
|
832
|
-
id: `NEW-${index + 1}`
|
|
833
|
-
});
|
|
834
|
-
} else if (secondLastElement?.toLowerCase() === 'modify' && tc.length === 4) {
|
|
835
|
-
// Format: [originalDescription, newDescription, "Modify", testCaseId]
|
|
836
|
-
parsedTestCases.modify.push({
|
|
837
|
-
id: tc[3],
|
|
838
|
-
original: tc[0],
|
|
839
|
-
modified: tc[1]
|
|
840
|
-
});
|
|
841
|
-
} else if (secondLastElement?.toLowerCase() === 'remove' && tc.length === 3) {
|
|
842
|
-
// Format: [description, "Remove", testCaseId]
|
|
843
|
-
parsedTestCases.remove.push({
|
|
844
|
-
id: tc[2],
|
|
845
|
-
description: tc[0]
|
|
846
|
-
});
|
|
847
|
-
} else {
|
|
848
|
-
console.log(`Unrecognized test case format at index ${index}:`, tc);
|
|
849
|
-
}
|
|
850
|
-
});
|
|
843
|
+
return parsed;
|
|
844
|
+
}
|
|
851
845
|
|
|
852
|
-
|
|
846
|
+
// Use the improved parsing function
|
|
847
|
+
const parsedTestCases = parseTestCases(testCases);
|
|
853
848
|
|
|
849
|
+
// Remove the old parsing logic and console.log statements that cause JSON errors
|
|
854
850
|
// Initialize session state
|
|
855
851
|
approvalSessions.set(sessionId, {
|
|
856
852
|
status: 'pending',
|
|
@@ -900,13 +896,37 @@ tool(
|
|
|
900
896
|
res.json({ status: 'cancelled', message: 'Review cancelled' });
|
|
901
897
|
});
|
|
902
898
|
|
|
899
|
+
app.post(`/delete/${sessionId}`, (req, res) => {
|
|
900
|
+
const session = approvalSessions.get(sessionId);
|
|
901
|
+
if (!session) {
|
|
902
|
+
return res.status(404).json({ error: 'Session not found' });
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const { type, index } = req.body;
|
|
906
|
+
|
|
907
|
+
try {
|
|
908
|
+
if (type === 'new' && session.testCases.new && index < session.testCases.new.length) {
|
|
909
|
+
session.testCases.new.splice(index, 1);
|
|
910
|
+
} else if (type === 'modify' && session.testCases.modify && index < session.testCases.modify.length) {
|
|
911
|
+
session.testCases.modify.splice(index, 1);
|
|
912
|
+
} else {
|
|
913
|
+
return res.status(400).json({ error: 'Invalid type or index' });
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
approvalSessions.set(sessionId, session);
|
|
917
|
+
res.json({ status: 'deleted', message: 'Test case deleted successfully' });
|
|
918
|
+
} catch (error) {
|
|
919
|
+
res.status(500).json({ error: 'Failed to delete test case' });
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
|
|
903
923
|
const server = app.listen(port, async () => {
|
|
904
|
-
console.log
|
|
924
|
+
// Remove console.log to prevent JSON parsing errors
|
|
905
925
|
try {
|
|
906
926
|
const { default: open } = await import('open');
|
|
907
927
|
await open(`http://localhost:${port}`);
|
|
908
928
|
} catch (openError) {
|
|
909
|
-
|
|
929
|
+
// Silent fail for browser opening
|
|
910
930
|
}
|
|
911
931
|
});
|
|
912
932
|
|
|
@@ -925,23 +945,21 @@ tool(
|
|
|
925
945
|
}
|
|
926
946
|
}, 300000);
|
|
927
947
|
|
|
928
|
-
|
|
948
|
+
// Return clean JSON response
|
|
949
|
+
return JSON.stringify({
|
|
929
950
|
status: "review_started",
|
|
930
951
|
sessionId: sessionId,
|
|
931
952
|
message: "Test case review interface opened in browser. Use check_approval_status tool to poll for approval.",
|
|
932
953
|
testCasesCount: testCases.length,
|
|
933
954
|
browserUrl: `http://localhost:${port}`,
|
|
934
955
|
instructions: "Poll every 25 seconds using check_approval_status tool until approved or timeout (5 minutes)"
|
|
935
|
-
};
|
|
936
|
-
|
|
937
|
-
return JSON.stringify(response);
|
|
956
|
+
});
|
|
938
957
|
|
|
939
958
|
} catch (err) {
|
|
940
|
-
|
|
959
|
+
return JSON.stringify({
|
|
941
960
|
status: "error",
|
|
942
961
|
message: `Error setting up review interface: ${err.message}`
|
|
943
|
-
};
|
|
944
|
-
return JSON.stringify(errorResponse);
|
|
962
|
+
});
|
|
945
963
|
}
|
|
946
964
|
}
|
|
947
965
|
);
|
|
@@ -957,10 +975,10 @@ tool(
|
|
|
957
975
|
|
|
958
976
|
const session = approvalSessions.get(sessionId);
|
|
959
977
|
if (!session) {
|
|
960
|
-
return
|
|
978
|
+
return {
|
|
961
979
|
status: "error",
|
|
962
980
|
message: `Session not found. Invalid session ID: ${sessionId}`
|
|
963
|
-
}
|
|
981
|
+
};
|
|
964
982
|
}
|
|
965
983
|
|
|
966
984
|
const currentTime = Date.now();
|
|
@@ -974,41 +992,41 @@ tool(
|
|
|
974
992
|
}
|
|
975
993
|
approvalSessions.delete(sessionId);
|
|
976
994
|
|
|
977
|
-
return
|
|
995
|
+
return {
|
|
978
996
|
status: "approved",
|
|
979
997
|
message: "Test cases approved successfully!",
|
|
980
998
|
elapsedTime: elapsedTime,
|
|
981
999
|
approvedTestCases: approvedTestCases
|
|
982
|
-
}
|
|
1000
|
+
};
|
|
983
1001
|
} else if (session.status === 'cancelled') {
|
|
984
1002
|
if (session.server) {
|
|
985
1003
|
session.server.close();
|
|
986
1004
|
}
|
|
987
1005
|
approvalSessions.delete(sessionId);
|
|
988
1006
|
|
|
989
|
-
return
|
|
1007
|
+
return {
|
|
990
1008
|
status: "cancelled",
|
|
991
1009
|
message: "Review was cancelled by the user",
|
|
992
1010
|
elapsedTime: elapsedTime
|
|
993
|
-
}
|
|
1011
|
+
};
|
|
994
1012
|
} else if (session.status === 'timeout' || elapsedTime > 300) {
|
|
995
1013
|
if (session.server) {
|
|
996
1014
|
session.server.close();
|
|
997
1015
|
}
|
|
998
1016
|
approvalSessions.delete(sessionId);
|
|
999
1017
|
|
|
1000
|
-
return
|
|
1018
|
+
return {
|
|
1001
1019
|
status: "timeout",
|
|
1002
1020
|
message: "Review session timed out",
|
|
1003
1021
|
elapsedTime: elapsedTime
|
|
1004
|
-
}
|
|
1022
|
+
};
|
|
1005
1023
|
} else {
|
|
1006
|
-
return
|
|
1024
|
+
return {
|
|
1007
1025
|
status: "pending",
|
|
1008
1026
|
message: "Test cases are still pending approval",
|
|
1009
1027
|
elapsedTime: elapsedTime,
|
|
1010
1028
|
remainingTime: Math.max(0, 300 - elapsedTime)
|
|
1011
|
-
}
|
|
1029
|
+
};
|
|
1012
1030
|
}
|
|
1013
1031
|
}
|
|
1014
1032
|
);
|