@nbakka/mcp-appium 2.0.97 → 2.0.98
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 +667 -289
- package/lib/server.js +56 -13
- package/package.json +1 -1
package/lib/review-ui/index.html
CHANGED
|
@@ -5,371 +5,749 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Test Case Review & Approval</title>
|
|
7
7
|
<style>
|
|
8
|
+
* {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
body {
|
|
9
15
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
10
|
-
|
|
11
|
-
padding: 20px;
|
|
12
|
-
background-color: #f5f5f5;
|
|
16
|
+
background-color: #f8f9fa;
|
|
13
17
|
line-height: 1.6;
|
|
18
|
+
color: #333;
|
|
14
19
|
}
|
|
20
|
+
|
|
15
21
|
.container {
|
|
16
|
-
max-width:
|
|
22
|
+
max-width: 1400px;
|
|
17
23
|
margin: 0 auto;
|
|
24
|
+
padding: 20px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.header {
|
|
28
|
+
text-align: center;
|
|
29
|
+
margin-bottom: 30px;
|
|
30
|
+
padding: 20px;
|
|
18
31
|
background: white;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
32
|
+
border-radius: 10px;
|
|
33
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.header h1 {
|
|
37
|
+
color: #2c3e50;
|
|
38
|
+
margin-bottom: 10px;
|
|
22
39
|
}
|
|
40
|
+
|
|
41
|
+
.session-info {
|
|
42
|
+
color: #7f8c8d;
|
|
43
|
+
font-size: 14px;
|
|
44
|
+
}
|
|
45
|
+
|
|
23
46
|
.section {
|
|
24
47
|
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 { background-color: #e8f5e8; border-color: #4caf50; }
|
|
37
|
-
.new h2 { border-bottom-color: #4caf50; color: #2e7d32; }
|
|
38
|
-
.modify { background-color: #fff8e1; border-color: #ff9800; }
|
|
39
|
-
.modify h2 { border-bottom-color: #ff9800; color: #f57c00; }
|
|
40
|
-
.remove { background-color: #ffebee; border-color: #f44336; }
|
|
41
|
-
.remove h2 { border-bottom-color: #f44336; color: #c62828; }
|
|
42
|
-
.test-case {
|
|
43
|
-
margin: 15px 0;
|
|
44
|
-
padding: 20px;
|
|
45
48
|
background: white;
|
|
46
|
-
border-radius:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
border-radius: 10px;
|
|
50
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.section-header {
|
|
55
|
+
padding: 20px;
|
|
56
|
+
font-weight: bold;
|
|
57
|
+
font-size: 18px;
|
|
58
|
+
border-bottom: 3px solid;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.new-section .section-header {
|
|
62
|
+
background-color: #d4edda;
|
|
63
|
+
color: #155724;
|
|
64
|
+
border-bottom-color: #28a745;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.modify-section .section-header {
|
|
68
|
+
background-color: #fff3cd;
|
|
69
|
+
color: #856404;
|
|
70
|
+
border-bottom-color: #ffc107;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.remove-section .section-header {
|
|
74
|
+
background-color: #f8d7da;
|
|
75
|
+
color: #721c24;
|
|
76
|
+
border-bottom-color: #dc3545;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.section-content {
|
|
80
|
+
padding: 20px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.test-case {
|
|
84
|
+
border: 1px solid #e9ecef;
|
|
85
|
+
border-radius: 8px;
|
|
86
|
+
margin-bottom: 15px;
|
|
87
|
+
background: #f8f9fa;
|
|
88
|
+
transition: all 0.3s ease;
|
|
49
89
|
}
|
|
90
|
+
|
|
91
|
+
.test-case:hover {
|
|
92
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
93
|
+
}
|
|
94
|
+
|
|
50
95
|
.test-case-header {
|
|
96
|
+
padding: 15px;
|
|
97
|
+
background: white;
|
|
98
|
+
border-bottom: 1px solid #e9ecef;
|
|
51
99
|
display: flex;
|
|
52
100
|
justify-content: space-between;
|
|
53
101
|
align-items: center;
|
|
54
|
-
|
|
102
|
+
border-radius: 8px 8px 0 0;
|
|
55
103
|
}
|
|
104
|
+
|
|
56
105
|
.test-case-id {
|
|
57
|
-
font-
|
|
58
|
-
|
|
59
|
-
|
|
106
|
+
font-family: 'Courier New', monospace;
|
|
107
|
+
background: #e9ecef;
|
|
108
|
+
padding: 4px 8px;
|
|
109
|
+
border-radius: 4px;
|
|
110
|
+
font-size: 12px;
|
|
111
|
+
color: #495057;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.test-case-actions {
|
|
115
|
+
display: flex;
|
|
116
|
+
gap: 8px;
|
|
60
117
|
}
|
|
61
|
-
|
|
62
|
-
|
|
118
|
+
|
|
119
|
+
.btn {
|
|
120
|
+
padding: 8px 16px;
|
|
63
121
|
border: none;
|
|
64
|
-
border-radius:
|
|
122
|
+
border-radius: 6px;
|
|
65
123
|
cursor: pointer;
|
|
66
|
-
font-size:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
.
|
|
124
|
+
font-size: 13px;
|
|
125
|
+
font-weight: 500;
|
|
126
|
+
transition: all 0.2s ease;
|
|
127
|
+
text-transform: uppercase;
|
|
128
|
+
letter-spacing: 0.5px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.btn:hover {
|
|
132
|
+
transform: translateY(-1px);
|
|
133
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.btn-edit {
|
|
137
|
+
background: #007bff;
|
|
138
|
+
color: white;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.btn-edit:hover {
|
|
142
|
+
background: #0056b3;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.btn-delete {
|
|
146
|
+
background: #dc3545;
|
|
147
|
+
color: white;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.btn-delete:hover {
|
|
151
|
+
background: #c82333;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.btn-save {
|
|
155
|
+
background: #28a745;
|
|
156
|
+
color: white;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.btn-save:hover {
|
|
160
|
+
background: #218838;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.btn-cancel {
|
|
164
|
+
background: #6c757d;
|
|
165
|
+
color: white;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.btn-cancel:hover {
|
|
169
|
+
background: #545b62;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.test-case-content {
|
|
173
|
+
padding: 15px;
|
|
174
|
+
}
|
|
175
|
+
|
|
74
176
|
.original-text {
|
|
75
|
-
|
|
177
|
+
background: #f8d7da;
|
|
178
|
+
border: 1px solid #f1aeb5;
|
|
179
|
+
border-radius: 4px;
|
|
180
|
+
padding: 10px;
|
|
181
|
+
margin-bottom: 10px;
|
|
76
182
|
text-decoration: line-through;
|
|
183
|
+
color: #721c24;
|
|
77
184
|
font-style: italic;
|
|
78
|
-
margin-bottom: 10px;
|
|
79
185
|
}
|
|
80
|
-
|
|
81
|
-
.
|
|
82
|
-
|
|
83
|
-
|
|
186
|
+
|
|
187
|
+
.modified-text {
|
|
188
|
+
background: #d1ecf1;
|
|
189
|
+
border: 1px solid #b8daff;
|
|
190
|
+
border-radius: 4px;
|
|
191
|
+
padding: 10px;
|
|
192
|
+
color: #0c5460;
|
|
193
|
+
font-weight: 500;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.description-text {
|
|
197
|
+
background: #d4edda;
|
|
198
|
+
border: 1px solid #c3e6cb;
|
|
199
|
+
border-radius: 4px;
|
|
84
200
|
padding: 10px;
|
|
85
|
-
|
|
201
|
+
color: #155724;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.remove-text {
|
|
205
|
+
background: #f8d7da;
|
|
206
|
+
border: 1px solid #f1aeb5;
|
|
86
207
|
border-radius: 4px;
|
|
208
|
+
padding: 10px;
|
|
209
|
+
color: #721c24;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.edit-form {
|
|
213
|
+
background: #e9ecef;
|
|
214
|
+
border-radius: 6px;
|
|
215
|
+
padding: 15px;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.form-group {
|
|
219
|
+
margin-bottom: 15px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.form-label {
|
|
223
|
+
display: block;
|
|
224
|
+
margin-bottom: 5px;
|
|
225
|
+
font-weight: 600;
|
|
226
|
+
color: #495057;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.form-control {
|
|
230
|
+
width: 100%;
|
|
231
|
+
padding: 10px;
|
|
232
|
+
border: 2px solid #ced4da;
|
|
233
|
+
border-radius: 6px;
|
|
87
234
|
font-family: inherit;
|
|
88
235
|
font-size: 14px;
|
|
89
236
|
resize: vertical;
|
|
237
|
+
min-height: 80px;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.form-control:focus {
|
|
241
|
+
outline: none;
|
|
242
|
+
border-color: #007bff;
|
|
243
|
+
box-shadow: 0 0 0 3px rgba(0,123,255,0.1);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.form-control[readonly] {
|
|
247
|
+
background-color: #f8f9fa;
|
|
248
|
+
cursor: not-allowed;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.empty-section {
|
|
252
|
+
text-align: center;
|
|
253
|
+
padding: 40px;
|
|
254
|
+
color: #6c757d;
|
|
255
|
+
font-style: italic;
|
|
90
256
|
}
|
|
91
|
-
|
|
92
|
-
.
|
|
257
|
+
|
|
258
|
+
.approval-section {
|
|
259
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
260
|
+
color: white;
|
|
93
261
|
text-align: center;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
262
|
+
padding: 30px;
|
|
263
|
+
border-radius: 10px;
|
|
264
|
+
margin-top: 30px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.approval-buttons {
|
|
268
|
+
display: flex;
|
|
269
|
+
justify-content: center;
|
|
270
|
+
gap: 20px;
|
|
271
|
+
margin-top: 20px;
|
|
97
272
|
}
|
|
98
|
-
|
|
273
|
+
|
|
274
|
+
.btn-primary {
|
|
275
|
+
background: #28a745;
|
|
276
|
+
color: white;
|
|
99
277
|
padding: 15px 30px;
|
|
100
|
-
margin: 0 15px;
|
|
101
|
-
border: none;
|
|
102
|
-
border-radius: 8px;
|
|
103
|
-
cursor: pointer;
|
|
104
278
|
font-size: 16px;
|
|
105
|
-
font-weight:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
279
|
+
font-weight: bold;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.btn-primary:hover {
|
|
283
|
+
background: #218838;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.btn-danger {
|
|
287
|
+
background: #dc3545;
|
|
288
|
+
color: white;
|
|
289
|
+
padding: 15px 30px;
|
|
290
|
+
font-size: 16px;
|
|
291
|
+
font-weight: bold;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.btn-danger:hover {
|
|
295
|
+
background: #c82333;
|
|
109
296
|
}
|
|
110
|
-
|
|
111
|
-
.
|
|
112
|
-
.cancel { background-color: #f44336; color: white; }
|
|
113
|
-
.cancel:hover { background-color: #da190b; transform: translateY(-2px); }
|
|
114
|
-
.status {
|
|
297
|
+
|
|
298
|
+
.status-message {
|
|
115
299
|
margin-top: 20px;
|
|
116
300
|
padding: 15px;
|
|
117
301
|
border-radius: 6px;
|
|
118
|
-
text-align: center;
|
|
119
302
|
font-weight: 500;
|
|
303
|
+
text-align: center;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.status-success {
|
|
307
|
+
background: #d4edda;
|
|
308
|
+
color: #155724;
|
|
309
|
+
border: 1px solid #c3e6cb;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.status-error {
|
|
313
|
+
background: #f8d7da;
|
|
314
|
+
color: #721c24;
|
|
315
|
+
border: 1px solid #f1aeb5;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.status-info {
|
|
319
|
+
background: #d1ecf1;
|
|
320
|
+
color: #0c5460;
|
|
321
|
+
border: 1px solid #b8daff;
|
|
120
322
|
}
|
|
121
|
-
|
|
122
|
-
.
|
|
123
|
-
.status.info { background-color: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }
|
|
124
|
-
#loading {
|
|
323
|
+
|
|
324
|
+
.loading {
|
|
125
325
|
text-align: center;
|
|
126
|
-
padding:
|
|
326
|
+
padding: 60px;
|
|
127
327
|
font-size: 18px;
|
|
128
|
-
color: #
|
|
328
|
+
color: #6c757d;
|
|
129
329
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
330
|
+
|
|
331
|
+
.loading::after {
|
|
332
|
+
content: '...';
|
|
333
|
+
animation: dots 1.5s steps(5, end) infinite;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
@keyframes dots {
|
|
337
|
+
0%, 20% { color: rgba(0,0,0,0); text-shadow: .25em 0 0 rgba(0,0,0,0), .5em 0 0 rgba(0,0,0,0); }
|
|
338
|
+
40% { color: #6c757d; text-shadow: .25em 0 0 rgba(0,0,0,0), .5em 0 0 rgba(0,0,0,0); }
|
|
339
|
+
60% { text-shadow: .25em 0 0 #6c757d, .5em 0 0 rgba(0,0,0,0); }
|
|
340
|
+
80%, 100% { text-shadow: .25em 0 0 #6c757d, .5em 0 0 #6c757d; }
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.fade-in {
|
|
344
|
+
animation: fadeIn 0.5s ease-in;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
@keyframes fadeIn {
|
|
348
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
349
|
+
to { opacity: 1; transform: translateY(0); }
|
|
135
350
|
}
|
|
136
351
|
</style>
|
|
137
352
|
</head>
|
|
138
353
|
<body>
|
|
139
|
-
<div class="container">
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
<div id="new-cases"></div>
|
|
146
|
-
</div>
|
|
147
|
-
<div id="modify-section" class="section modify">
|
|
148
|
-
<h2>Modified Test Cases</h2>
|
|
149
|
-
<div id="modify-cases"></div>
|
|
354
|
+
<div class="container">
|
|
355
|
+
<div class="header">
|
|
356
|
+
<h1>🧪 Test Case Review & Approval</h1>
|
|
357
|
+
<div class="session-info" id="sessionInfo">
|
|
358
|
+
Loading session information...
|
|
359
|
+
</div>
|
|
150
360
|
</div>
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
361
|
+
|
|
362
|
+
<div id="loading" class="loading">
|
|
363
|
+
Loading test cases
|
|
154
364
|
</div>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
<
|
|
158
|
-
|
|
365
|
+
|
|
366
|
+
<div id="content" style="display: none;">
|
|
367
|
+
<div id="newSection" class="section new-section">
|
|
368
|
+
<div class="section-header">
|
|
369
|
+
✨ New Test Cases (<span id="newCount">0</span>)
|
|
370
|
+
</div>
|
|
371
|
+
<div class="section-content" id="newCases">
|
|
372
|
+
<div class="empty-section">No new test cases</div>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<div id="modifySection" class="section modify-section">
|
|
377
|
+
<div class="section-header">
|
|
378
|
+
🔄 Modified Test Cases (<span id="modifyCount">0</span>)
|
|
379
|
+
</div>
|
|
380
|
+
<div class="section-content" id="modifyCases">
|
|
381
|
+
<div class="empty-section">No modified test cases</div>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
|
|
385
|
+
<div id="removeSection" class="section remove-section">
|
|
386
|
+
<div class="section-header">
|
|
387
|
+
🗑️ Test Cases to Remove (<span id="removeCount">0</span>)
|
|
388
|
+
</div>
|
|
389
|
+
<div class="section-content" id="removeCases">
|
|
390
|
+
<div class="empty-section">No test cases to remove</div>
|
|
391
|
+
</div>
|
|
392
|
+
</div>
|
|
393
|
+
|
|
394
|
+
<div class="approval-section">
|
|
395
|
+
<h2>🎯 Review Complete?</h2>
|
|
396
|
+
<p>Review all test cases above and make any necessary changes, then approve or cancel.</p>
|
|
397
|
+
<div class="approval-buttons">
|
|
398
|
+
<button class="btn btn-primary" onclick="approveTestCases()">
|
|
399
|
+
✅ Approve All Test Cases
|
|
400
|
+
</button>
|
|
401
|
+
<button class="btn btn-danger" onclick="cancelReview()">
|
|
402
|
+
❌ Cancel Review
|
|
403
|
+
</button>
|
|
404
|
+
</div>
|
|
405
|
+
<div id="statusMessage" class="status-message" style="display: none;"></div>
|
|
406
|
+
</div>
|
|
159
407
|
</div>
|
|
160
408
|
</div>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
409
|
+
|
|
410
|
+
<script>
|
|
411
|
+
// Global state
|
|
412
|
+
let sessionId = null;
|
|
413
|
+
let testCases = { new: [], modify: [], remove: [] };
|
|
414
|
+
let editingStates = new Set();
|
|
415
|
+
|
|
416
|
+
// Utility functions
|
|
417
|
+
function showStatus(message, type = 'info') {
|
|
418
|
+
const statusEl = document.getElementById('statusMessage');
|
|
419
|
+
statusEl.textContent = message;
|
|
420
|
+
statusEl.className = `status-message status-${type}`;
|
|
421
|
+
statusEl.style.display = 'block';
|
|
422
|
+
|
|
423
|
+
if (type === 'success') {
|
|
424
|
+
setTimeout(() => {
|
|
425
|
+
statusEl.style.display = 'none';
|
|
426
|
+
}, 3000);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function updateCounts() {
|
|
431
|
+
document.getElementById('newCount').textContent = testCases.new?.length || 0;
|
|
432
|
+
document.getElementById('modifyCount').textContent = testCases.modify?.length || 0;
|
|
433
|
+
document.getElementById('removeCount').textContent = testCases.remove?.length || 0;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// API functions
|
|
437
|
+
async function apiCall(url, options = {}) {
|
|
438
|
+
try {
|
|
439
|
+
const response = await fetch(url, {
|
|
440
|
+
headers: {
|
|
441
|
+
'Content-Type': 'application/json',
|
|
442
|
+
...options.headers
|
|
443
|
+
},
|
|
444
|
+
...options
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
if (!response.ok) {
|
|
448
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return await response.json();
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error('API call failed:', error);
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Load initial data
|
|
459
|
+
async function loadTestCases() {
|
|
460
|
+
try {
|
|
461
|
+
const data = await apiCall('/api/testcases');
|
|
462
|
+
sessionId = data.sessionId;
|
|
463
|
+
testCases = data.testCases || { new: [], modify: [], remove: [] };
|
|
464
|
+
|
|
465
|
+
document.getElementById('sessionInfo').textContent =
|
|
466
|
+
`Session ID: ${sessionId} | Total Cases: ${
|
|
467
|
+
(testCases.new?.length || 0) +
|
|
468
|
+
(testCases.modify?.length || 0) +
|
|
469
|
+
(testCases.remove?.length || 0)
|
|
470
|
+
}`;
|
|
471
|
+
|
|
472
|
+
document.getElementById('loading').style.display = 'none';
|
|
473
|
+
document.getElementById('content').style.display = 'block';
|
|
474
|
+
document.getElementById('content').classList.add('fade-in');
|
|
475
|
+
|
|
476
|
+
renderAllSections();
|
|
477
|
+
updateCounts();
|
|
478
|
+
} catch (error) {
|
|
479
|
+
document.getElementById('loading').innerHTML =
|
|
480
|
+
`<div class="status-message status-error">❌ Error loading test cases: ${error.message}</div>`;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Render functions
|
|
485
|
+
function renderAllSections() {
|
|
486
|
+
renderNewCases();
|
|
487
|
+
renderModifyCases();
|
|
488
|
+
renderRemoveCases();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function renderNewCases() {
|
|
492
|
+
const container = document.getElementById('newCases');
|
|
493
|
+
const cases = testCases.new || [];
|
|
494
|
+
|
|
495
|
+
if (cases.length === 0) {
|
|
496
|
+
container.innerHTML = '<div class="empty-section">No new test cases</div>';
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
container.innerHTML = cases.map((tc, index) => `
|
|
501
|
+
<div class="test-case" id="new-${index}">
|
|
194
502
|
<div class="test-case-header">
|
|
195
|
-
<span class="test-case-id"
|
|
196
|
-
<div>
|
|
197
|
-
<button class="edit
|
|
198
|
-
<button class="delete
|
|
503
|
+
<span class="test-case-id">${tc.id}</span>
|
|
504
|
+
<div class="test-case-actions">
|
|
505
|
+
<button class="btn btn-edit" onclick="startEdit('new', ${index})">✏️ Edit</button>
|
|
506
|
+
<button class="btn btn-delete" onclick="deleteTestCase('new', '${tc.id}')">🗑️ Delete</button>
|
|
199
507
|
</div>
|
|
200
508
|
</div>
|
|
201
|
-
<div
|
|
202
|
-
<
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
<
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
509
|
+
<div class="test-case-content">
|
|
510
|
+
<div id="new-${index}-display">
|
|
511
|
+
<div class="description-text">${escapeHtml(tc.description)}</div>
|
|
512
|
+
</div>
|
|
513
|
+
<div id="new-${index}-edit" style="display: none;">
|
|
514
|
+
<div class="edit-form">
|
|
515
|
+
<div class="form-group">
|
|
516
|
+
<label class="form-label">Description:</label>
|
|
517
|
+
<textarea class="form-control" id="new-${index}-desc">${escapeHtml(tc.description)}</textarea>
|
|
518
|
+
</div>
|
|
519
|
+
<div class="test-case-actions">
|
|
520
|
+
<button class="btn btn-save" onclick="saveEdit('new', ${index})">💾 Save</button>
|
|
521
|
+
<button class="btn btn-cancel" onclick="cancelEdit('new', ${index})">❌ Cancel</button>
|
|
522
|
+
</div>
|
|
523
|
+
</div>
|
|
210
524
|
</div>
|
|
211
525
|
</div>
|
|
212
526
|
</div>
|
|
213
527
|
`).join('');
|
|
214
|
-
} else {
|
|
215
|
-
container.innerHTML = '<div class="empty-section">No new test cases</div>';
|
|
216
528
|
}
|
|
217
|
-
}
|
|
218
529
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
530
|
+
function renderModifyCases() {
|
|
531
|
+
const container = document.getElementById('modifyCases');
|
|
532
|
+
const cases = testCases.modify || [];
|
|
533
|
+
|
|
534
|
+
if (cases.length === 0) {
|
|
535
|
+
container.innerHTML = '<div class="empty-section">No modified test cases</div>';
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
container.innerHTML = cases.map((tc, index) => `
|
|
540
|
+
<div class="test-case" id="modify-${index}">
|
|
224
541
|
<div class="test-case-header">
|
|
225
|
-
<span class="test-case-id"
|
|
226
|
-
<div>
|
|
227
|
-
<button class="edit
|
|
228
|
-
<button class="delete
|
|
542
|
+
<span class="test-case-id">${tc.id}</span>
|
|
543
|
+
<div class="test-case-actions">
|
|
544
|
+
<button class="btn btn-edit" onclick="startEdit('modify', ${index})">✏️ Edit</button>
|
|
545
|
+
<button class="btn btn-delete" onclick="deleteTestCase('modify', '${tc.id}')">🗑️ Delete</button>
|
|
229
546
|
</div>
|
|
230
547
|
</div>
|
|
231
|
-
<div
|
|
232
|
-
<div
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
<div style="
|
|
241
|
-
<
|
|
242
|
-
|
|
548
|
+
<div class="test-case-content">
|
|
549
|
+
<div id="modify-${index}-display">
|
|
550
|
+
<div class="original-text">
|
|
551
|
+
<strong>Original:</strong><br>${escapeHtml(tc.original)}
|
|
552
|
+
</div>
|
|
553
|
+
<div class="modified-text">
|
|
554
|
+
<strong>Modified:</strong><br>${escapeHtml(tc.modified)}
|
|
555
|
+
</div>
|
|
556
|
+
</div>
|
|
557
|
+
<div id="modify-${index}-edit" style="display: none;">
|
|
558
|
+
<div class="edit-form">
|
|
559
|
+
<div class="form-group">
|
|
560
|
+
<label class="form-label">Original (Read-only):</label>
|
|
561
|
+
<textarea class="form-control" readonly>${escapeHtml(tc.original)}</textarea>
|
|
562
|
+
</div>
|
|
563
|
+
<div class="form-group">
|
|
564
|
+
<label class="form-label">Modified:</label>
|
|
565
|
+
<textarea class="form-control" id="modify-${index}-mod">${escapeHtml(tc.modified)}</textarea>
|
|
566
|
+
</div>
|
|
567
|
+
<div class="test-case-actions">
|
|
568
|
+
<button class="btn btn-save" onclick="saveEdit('modify', ${index})">💾 Save</button>
|
|
569
|
+
<button class="btn btn-cancel" onclick="cancelEdit('modify', ${index})">❌ Cancel</button>
|
|
570
|
+
</div>
|
|
571
|
+
</div>
|
|
243
572
|
</div>
|
|
244
573
|
</div>
|
|
245
574
|
</div>
|
|
246
575
|
`).join('');
|
|
247
|
-
} else {
|
|
248
|
-
container.innerHTML = '<div class="empty-section">No modified test cases</div>';
|
|
249
576
|
}
|
|
250
|
-
}
|
|
251
577
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
578
|
+
function renderRemoveCases() {
|
|
579
|
+
const container = document.getElementById('removeCases');
|
|
580
|
+
const cases = testCases.remove || [];
|
|
581
|
+
|
|
582
|
+
if (cases.length === 0) {
|
|
583
|
+
container.innerHTML = '<div class="empty-section">No test cases to remove</div>';
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
container.innerHTML = cases.map((tc, index) => `
|
|
588
|
+
<div class="test-case" id="remove-${index}">
|
|
257
589
|
<div class="test-case-header">
|
|
258
|
-
<span class="test-case-id"
|
|
590
|
+
<span class="test-case-id">${tc.id}</span>
|
|
591
|
+
<div class="test-case-actions">
|
|
592
|
+
<button class="btn btn-delete" onclick="deleteTestCase('remove', '${tc.id}')">🗑️ Remove</button>
|
|
593
|
+
</div>
|
|
259
594
|
</div>
|
|
260
|
-
<div>
|
|
261
|
-
<
|
|
262
|
-
|
|
263
|
-
|
|
595
|
+
<div class="test-case-content">
|
|
596
|
+
<div class="remove-text">
|
|
597
|
+
<strong>⚠️ This test case will be removed:</strong><br>
|
|
598
|
+
${escapeHtml(tc.description)}
|
|
264
599
|
</div>
|
|
265
600
|
</div>
|
|
266
601
|
</div>
|
|
267
602
|
`).join('');
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Edit functions
|
|
606
|
+
function startEdit(type, index) {
|
|
607
|
+
const editKey = `${type}-${index}`;
|
|
608
|
+
if (editingStates.has(editKey)) return;
|
|
609
|
+
|
|
610
|
+
editingStates.add(editKey);
|
|
611
|
+
document.getElementById(`${type}-${index}-display`).style.display = 'none';
|
|
612
|
+
document.getElementById(`${type}-${index}-edit`).style.display = 'block';
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function cancelEdit(type, index) {
|
|
616
|
+
const editKey = `${type}-${index}`;
|
|
617
|
+
editingStates.delete(editKey);
|
|
618
|
+
document.getElementById(`${type}-${index}-display`).style.display = 'block';
|
|
619
|
+
document.getElementById(`${type}-${index}-edit`).style.display = 'none';
|
|
620
|
+
|
|
621
|
+
// Reset form values
|
|
622
|
+
if (type === 'new') {
|
|
623
|
+
document.getElementById(`new-${index}-desc`).value = testCases.new[index].description;
|
|
624
|
+
} else if (type === 'modify') {
|
|
625
|
+
document.getElementById(`modify-${index}-mod`).value = testCases.modify[index].modified;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
async function saveEdit(type, index) {
|
|
630
|
+
try {
|
|
631
|
+
const editKey = `${type}-${index}`;
|
|
632
|
+
let updatedData = {};
|
|
633
|
+
|
|
634
|
+
if (type === 'new') {
|
|
635
|
+
const newDesc = document.getElementById(`new-${index}-desc`).value.trim();
|
|
636
|
+
if (!newDesc) {
|
|
637
|
+
showStatus('Description cannot be empty', 'error');
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
updatedData = { description: newDesc };
|
|
641
|
+
testCases.new[index].description = newDesc;
|
|
642
|
+
} else if (type === 'modify') {
|
|
643
|
+
const newMod = document.getElementById(`modify-${index}-mod`).value.trim();
|
|
644
|
+
if (!newMod) {
|
|
645
|
+
showStatus('Modified description cannot be empty', 'error');
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
updatedData = { modified: newMod };
|
|
649
|
+
testCases.modify[index].modified = newMod;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Send update to server
|
|
653
|
+
await apiCall(`/update/${sessionId}`, {
|
|
654
|
+
method: 'POST',
|
|
655
|
+
body: JSON.stringify({
|
|
656
|
+
type,
|
|
657
|
+
id: testCases[type][index].id,
|
|
658
|
+
data: updatedData
|
|
659
|
+
})
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
editingStates.delete(editKey);
|
|
663
|
+
document.getElementById(`${type}-${index}-display`).style.display = 'block';
|
|
664
|
+
document.getElementById(`${type}-${index}-edit`).style.display = 'none';
|
|
665
|
+
|
|
666
|
+
// Re-render the specific section
|
|
667
|
+
if (type === 'new') renderNewCases();
|
|
668
|
+
else if (type === 'modify') renderModifyCases();
|
|
669
|
+
|
|
670
|
+
showStatus('Changes saved successfully!', 'success');
|
|
671
|
+
} catch (error) {
|
|
672
|
+
showStatus(`Error saving changes: ${error.message}`, 'error');
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Delete function
|
|
677
|
+
async function deleteTestCase(type, id) {
|
|
678
|
+
if (!confirm(`Are you sure you want to delete this test case?`)) return;
|
|
679
|
+
|
|
680
|
+
try {
|
|
681
|
+
showStatus('Deleting test case...', 'info');
|
|
682
|
+
|
|
683
|
+
const result = await apiCall(`/delete/${sessionId}`, {
|
|
684
|
+
method: 'POST',
|
|
685
|
+
body: JSON.stringify({ type, id })
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
if (result.testCases) {
|
|
689
|
+
testCases = result.testCases;
|
|
690
|
+
renderAllSections();
|
|
691
|
+
updateCounts();
|
|
692
|
+
showStatus('Test case deleted successfully!', 'success');
|
|
693
|
+
}
|
|
694
|
+
} catch (error) {
|
|
695
|
+
showStatus(`Error deleting test case: ${error.message}`, 'error');
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Approval functions
|
|
700
|
+
async function approveTestCases() {
|
|
701
|
+
if (!confirm('Are you sure you want to approve all test cases? This action cannot be undone.')) return;
|
|
702
|
+
|
|
703
|
+
try {
|
|
704
|
+
showStatus('Processing approval...', 'info');
|
|
705
|
+
|
|
706
|
+
const result = await apiCall(`/approve/${sessionId}`, {
|
|
707
|
+
method: 'POST',
|
|
708
|
+
body: JSON.stringify(testCases)
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
showStatus('✅ Test cases approved successfully! You can close this window.', 'success');
|
|
712
|
+
|
|
713
|
+
// Disable all controls
|
|
714
|
+
document.querySelectorAll('button').forEach(btn => btn.disabled = true);
|
|
715
|
+
|
|
716
|
+
setTimeout(() => {
|
|
717
|
+
window.close();
|
|
718
|
+
}, 2000);
|
|
719
|
+
} catch (error) {
|
|
720
|
+
showStatus(`Error approving test cases: ${error.message}`, 'error');
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
async function cancelReview() {
|
|
725
|
+
if (!confirm('Are you sure you want to cancel this review?')) return;
|
|
726
|
+
|
|
727
|
+
try {
|
|
728
|
+
showStatus('Cancelling review...', 'info');
|
|
729
|
+
|
|
730
|
+
await apiCall(`/cancel/${sessionId}`, { method: 'POST' });
|
|
731
|
+
|
|
732
|
+
showStatus('Review cancelled. You can close this window.', 'info');
|
|
733
|
+
|
|
734
|
+
setTimeout(() => {
|
|
735
|
+
window.close();
|
|
736
|
+
}, 1500);
|
|
737
|
+
} catch (error) {
|
|
738
|
+
showStatus(`Error cancelling review: ${error.message}`, 'error');
|
|
334
739
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
headers: { 'Content-Type': 'application/json' },
|
|
348
|
-
body: JSON.stringify(testCases)
|
|
349
|
-
});
|
|
350
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
351
|
-
const result = await response.json();
|
|
352
|
-
showStatus(result.message || 'Test cases approved successfully!', 'success');
|
|
353
|
-
setTimeout(() => window.close(), 1200);
|
|
354
|
-
} catch (error) {
|
|
355
|
-
showStatus(`Error approving test cases: ${error.message}`, 'error');
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
async function cancelReview() {
|
|
360
|
-
try {
|
|
361
|
-
showStatus('Cancelling review...', 'info');
|
|
362
|
-
const response = await fetch(`/cancel/${sessionId}`, { method: 'POST' });
|
|
363
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
364
|
-
const result = await response.json();
|
|
365
|
-
showStatus(result.message || 'Review cancelled', 'info');
|
|
366
|
-
setTimeout(() => window.close(), 1500);
|
|
367
|
-
} catch (error) {
|
|
368
|
-
showStatus(`Error cancelling review: ${error.message}`, 'error');
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
window.onload = loadTestCases;
|
|
373
|
-
</script>
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Utility function
|
|
743
|
+
function escapeHtml(text) {
|
|
744
|
+
const div = document.createElement('div');
|
|
745
|
+
div.textContent = text;
|
|
746
|
+
return div.innerHTML;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// Initialize when page loads
|
|
750
|
+
window.addEventListener('load', loadTestCases);
|
|
751
|
+
</script>
|
|
374
752
|
</body>
|
|
375
753
|
</html>
|
package/lib/server.js
CHANGED
|
@@ -888,31 +888,73 @@ tool(
|
|
|
888
888
|
if (!session) return res.status(404).json({ error: 'Session not found' });
|
|
889
889
|
|
|
890
890
|
const { type, id } = req.body;
|
|
891
|
-
if (!['new', 'modify'].includes(type) || !id) {
|
|
891
|
+
if (!['new', 'modify', 'remove'].includes(type) || !id) {
|
|
892
892
|
return res.status(400).json({ error: 'Invalid delete request' });
|
|
893
893
|
}
|
|
894
894
|
try {
|
|
895
|
-
session.testCases[type]
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
895
|
+
if (session.testCases[type] && Array.isArray(session.testCases[type])) {
|
|
896
|
+
session.testCases[type] = session.testCases[type].filter(tc => tc.id !== id);
|
|
897
|
+
approvalSessions.set(sessionId, session);
|
|
898
|
+
return res.json({ status: 'deleted', testCases: session.testCases });
|
|
899
|
+
} else {
|
|
900
|
+
return res.status(400).json({ error: `Invalid type: ${type}` });
|
|
901
|
+
}
|
|
902
|
+
} catch (error) {
|
|
903
|
+
console.error('Delete error:', error);
|
|
899
904
|
return res.status(500).json({ error: 'Failed to delete test case' });
|
|
900
905
|
}
|
|
901
906
|
});
|
|
902
907
|
|
|
908
|
+
app.post(`/update/${sessionId}`, (req, res) => {
|
|
909
|
+
const session = approvalSessions.get(sessionId);
|
|
910
|
+
if (!session) return res.status(404).json({ error: 'Session not found' });
|
|
911
|
+
|
|
912
|
+
try {
|
|
913
|
+
const { type, id, data } = req.body;
|
|
914
|
+
if (!['new', 'modify'].includes(type) || !id || !data) {
|
|
915
|
+
return res.status(400).json({ error: 'Invalid update request' });
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const items = session.testCases[type];
|
|
919
|
+
const index = items.findIndex(tc => tc.id === id);
|
|
920
|
+
if (index === -1) {
|
|
921
|
+
return res.status(404).json({ error: 'Test case not found' });
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// Update the test case
|
|
925
|
+
if (type === 'new') {
|
|
926
|
+
items[index].description = data.description;
|
|
927
|
+
} else if (type === 'modify') {
|
|
928
|
+
items[index].modified = data.modified;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
approvalSessions.set(sessionId, session);
|
|
932
|
+
return res.json({ status: 'updated', testCases: session.testCases });
|
|
933
|
+
} catch (error) {
|
|
934
|
+
console.error('Update error:', error);
|
|
935
|
+
return res.status(500).json({ error: 'Failed to update test case' });
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
|
|
903
939
|
app.post(`/approve/${sessionId}`, (req, res) => {
|
|
904
940
|
const session = approvalSessions.get(sessionId);
|
|
905
941
|
if (!session) return res.status(404).json({ error: 'Session not found' });
|
|
906
942
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
943
|
+
try {
|
|
944
|
+
// Use atomic update with locking
|
|
945
|
+
session.locked = true;
|
|
946
|
+
session.testCases = req.body;
|
|
947
|
+
session.finalTestCases = JSON.parse(JSON.stringify(req.body)); // Deep copy
|
|
948
|
+
session.status = 'approved';
|
|
949
|
+
session.approvedAt = Date.now();
|
|
950
|
+
approvalSessions.set(sessionId, session);
|
|
914
951
|
|
|
915
|
-
|
|
952
|
+
console.log(`Session ${sessionId} approved at ${new Date().toISOString()}`);
|
|
953
|
+
res.json({ status: 'approved', message: 'Test cases approved successfully!' });
|
|
954
|
+
} catch (error) {
|
|
955
|
+
console.error('Approval error:', error);
|
|
956
|
+
res.status(500).json({ error: 'Failed to approve test cases' });
|
|
957
|
+
}
|
|
916
958
|
});
|
|
917
959
|
|
|
918
960
|
app.post(`/cancel/${sessionId}`, (_req, res) => {
|
|
@@ -921,6 +963,7 @@ tool(
|
|
|
921
963
|
session.status = 'cancelled';
|
|
922
964
|
session.cancelledAt = Date.now();
|
|
923
965
|
approvalSessions.set(sessionId, session);
|
|
966
|
+
console.log(`Session ${sessionId} cancelled at ${new Date().toISOString()}`);
|
|
924
967
|
}
|
|
925
968
|
res.json({ status: 'cancelled', message: 'Review cancelled' });
|
|
926
969
|
});
|