@nbakka/mcp-appium 2.0.81 → 2.0.83
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 +38 -0
- package/lib/review-ui/script.js +176 -0
- package/lib/review-ui/styles.css +244 -0
- package/lib/server.js +84 -153
- package/lib/testcases-generation-context.txt +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Test Case Review & Approval</title>
|
|
5
|
+
<link rel="stylesheet" href="/styles.css">
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<div class="container">
|
|
9
|
+
<h1>Review Test Cases</h1>
|
|
10
|
+
|
|
11
|
+
<!-- New Test Cases Section -->
|
|
12
|
+
<div class="section" id="new-testcases-section">
|
|
13
|
+
<h2>New Test Cases</h2>
|
|
14
|
+
<div id="new-testcases"></div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- Modified Test Cases Section -->
|
|
18
|
+
<div class="section" id="modified-testcases-section">
|
|
19
|
+
<h2>Modified Test Cases</h2>
|
|
20
|
+
<div id="modified-testcases"></div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<!-- Test Cases to Remove Section -->
|
|
24
|
+
<div class="section" id="remove-testcases-section">
|
|
25
|
+
<h2>Test Cases to Remove</h2>
|
|
26
|
+
<div id="remove-testcases"></div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div class="button-container">
|
|
30
|
+
<button id="approve-btn" onclick="approveTestCases()">✓ Approve Test Cases</button>
|
|
31
|
+
<button id="cancel-btn" onclick="cancelReview()">✗ Cancel Review</button>
|
|
32
|
+
<div id="status" class="status"></div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<script src="/script.js"></script>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
let sessionId = '';
|
|
2
|
+
let testCasesData = {};
|
|
3
|
+
|
|
4
|
+
function initializeTestCases(data) {
|
|
5
|
+
sessionId = data.sessionId;
|
|
6
|
+
testCasesData = data.testCases;
|
|
7
|
+
|
|
8
|
+
renderNewTestCases();
|
|
9
|
+
renderModifiedTestCases();
|
|
10
|
+
renderRemoveTestCases();
|
|
11
|
+
|
|
12
|
+
// Hide empty sections
|
|
13
|
+
toggleSectionVisibility();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function renderNewTestCases() {
|
|
17
|
+
const container = document.getElementById('new-testcases');
|
|
18
|
+
const newCases = testCasesData.new || [];
|
|
19
|
+
|
|
20
|
+
if (newCases.length === 0) {
|
|
21
|
+
container.innerHTML = '<p class="no-cases">No new test cases</p>';
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
container.innerHTML = newCases.map((testCase, index) => `
|
|
26
|
+
<div class="test-case new" data-index="${index}" data-type="new">
|
|
27
|
+
<div class="test-case-header">
|
|
28
|
+
<span class="test-type new">New</span>
|
|
29
|
+
<button class="remove-btn" onclick="removeTestCase('new', ${index})">Remove</button>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="test-description">
|
|
32
|
+
<textarea onchange="updateTestCase('new', ${index}, this.value)">${testCase}</textarea>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
`).join('');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renderModifiedTestCases() {
|
|
39
|
+
const container = document.getElementById('modified-testcases');
|
|
40
|
+
const modifiedCases = testCasesData.modify || [];
|
|
41
|
+
|
|
42
|
+
if (modifiedCases.length === 0) {
|
|
43
|
+
container.innerHTML = '<p class="no-cases">No modified test cases</p>';
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
container.innerHTML = modifiedCases.map((testCase, index) => `
|
|
48
|
+
<div class="test-case modify" data-index="${index}" data-type="modify">
|
|
49
|
+
<div class="test-case-header">
|
|
50
|
+
<span class="test-id">${testCase.id}</span>
|
|
51
|
+
<div>
|
|
52
|
+
<span class="test-type modify">Modify</span>
|
|
53
|
+
<button class="remove-btn" onclick="removeTestCase('modify', ${index})">Remove</button>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="before-after">
|
|
57
|
+
<div class="before-content">
|
|
58
|
+
<h4>Before (Original)</h4>
|
|
59
|
+
<div>${testCase.original}</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="after-content">
|
|
62
|
+
<h4>After (Modified)</h4>
|
|
63
|
+
<textarea onchange="updateModifiedTestCase(${index}, this.value)">${testCase.modified}</textarea>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
`).join('');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function renderRemoveTestCases() {
|
|
71
|
+
const container = document.getElementById('remove-testcases');
|
|
72
|
+
const removeCases = testCasesData.remove || [];
|
|
73
|
+
|
|
74
|
+
if (removeCases.length === 0) {
|
|
75
|
+
container.innerHTML = '<p class="no-cases">No test cases to remove</p>';
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
container.innerHTML = removeCases.map((testCase, index) => `
|
|
80
|
+
<div class="test-case remove" data-index="${index}" data-type="remove">
|
|
81
|
+
<div class="test-case-header">
|
|
82
|
+
<span class="test-id">${testCase.id}</span>
|
|
83
|
+
<span class="test-type">Remove</span>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="test-description">
|
|
86
|
+
<div>${testCase.description}</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
`).join('');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function removeTestCase(type, index) {
|
|
93
|
+
if (confirm('Are you sure you want to remove this test case?')) {
|
|
94
|
+
testCasesData[type].splice(index, 1);
|
|
95
|
+
|
|
96
|
+
if (type === 'new') renderNewTestCases();
|
|
97
|
+
else if (type === 'modify') renderModifiedTestCases();
|
|
98
|
+
|
|
99
|
+
toggleSectionVisibility();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function updateTestCase(type, index, value) {
|
|
104
|
+
testCasesData[type][index] = value;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function updateModifiedTestCase(index, value) {
|
|
108
|
+
testCasesData.modify[index].modified = value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function toggleSectionVisibility() {
|
|
112
|
+
const newSection = document.getElementById('new-testcases-section');
|
|
113
|
+
const modifySection = document.getElementById('modified-testcases-section');
|
|
114
|
+
const removeSection = document.getElementById('remove-testcases-section');
|
|
115
|
+
|
|
116
|
+
newSection.classList.toggle('hidden', !testCasesData.new || testCasesData.new.length === 0);
|
|
117
|
+
modifySection.classList.toggle('hidden', !testCasesData.modify || testCasesData.modify.length === 0);
|
|
118
|
+
removeSection.classList.toggle('hidden', !testCasesData.remove || testCasesData.remove.length === 0);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function approveTestCases() {
|
|
122
|
+
const approveBtn = document.getElementById('approve-btn');
|
|
123
|
+
const cancelBtn = document.getElementById('cancel-btn');
|
|
124
|
+
const status = document.getElementById('status');
|
|
125
|
+
|
|
126
|
+
approveBtn.disabled = true;
|
|
127
|
+
cancelBtn.disabled = true;
|
|
128
|
+
approveBtn.innerHTML = 'Processing...';
|
|
129
|
+
approveBtn.style.background = '#6c757d';
|
|
130
|
+
|
|
131
|
+
status.style.display = 'block';
|
|
132
|
+
status.className = 'status success';
|
|
133
|
+
status.innerHTML = 'Test cases approved! Processing...';
|
|
134
|
+
|
|
135
|
+
fetch(`/approve/${sessionId}`, {
|
|
136
|
+
method: 'POST',
|
|
137
|
+
headers: {
|
|
138
|
+
'Content-Type': 'application/json',
|
|
139
|
+
},
|
|
140
|
+
body: JSON.stringify(testCasesData)
|
|
141
|
+
})
|
|
142
|
+
.then(response => response.text())
|
|
143
|
+
.then(data => {
|
|
144
|
+
status.innerHTML = data + ' You can close this window.';
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
window.close();
|
|
147
|
+
}, 3000);
|
|
148
|
+
})
|
|
149
|
+
.catch(error => {
|
|
150
|
+
status.className = 'status error';
|
|
151
|
+
status.innerHTML = 'Error: ' + error.message;
|
|
152
|
+
approveBtn.disabled = false;
|
|
153
|
+
cancelBtn.disabled = false;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function cancelReview() {
|
|
158
|
+
if (confirm('Are you sure you want to cancel the review? All changes will be lost.')) {
|
|
159
|
+
fetch(`/cancel/${sessionId}`, { method: 'POST' })
|
|
160
|
+
.then(() => {
|
|
161
|
+
window.close();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Load initial data when page loads
|
|
167
|
+
window.addEventListener('DOMContentLoaded', function() {
|
|
168
|
+
fetch('/api/testcases')
|
|
169
|
+
.then(response => response.json())
|
|
170
|
+
.then(data => {
|
|
171
|
+
initializeTestCases(data);
|
|
172
|
+
})
|
|
173
|
+
.catch(error => {
|
|
174
|
+
console.error('Error loading test cases:', error);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
body {
|
|
2
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
3
|
+
margin: 0;
|
|
4
|
+
padding: 20px;
|
|
5
|
+
background-color: #f8f9fa;
|
|
6
|
+
line-height: 1.6;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.container {
|
|
10
|
+
max-width: 1400px;
|
|
11
|
+
margin: 0 auto;
|
|
12
|
+
background: white;
|
|
13
|
+
padding: 30px;
|
|
14
|
+
border-radius: 12px;
|
|
15
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
h1 {
|
|
19
|
+
color: #2c3e50;
|
|
20
|
+
text-align: center;
|
|
21
|
+
border-bottom: 3px solid #3498db;
|
|
22
|
+
padding-bottom: 15px;
|
|
23
|
+
margin-bottom: 30px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
h2 {
|
|
27
|
+
color: #34495e;
|
|
28
|
+
border-left: 4px solid #3498db;
|
|
29
|
+
padding-left: 15px;
|
|
30
|
+
margin-top: 30px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.section {
|
|
34
|
+
margin-bottom: 40px;
|
|
35
|
+
padding: 20px;
|
|
36
|
+
border: 1px solid #e9ecef;
|
|
37
|
+
border-radius: 8px;
|
|
38
|
+
background: #fdfdfd;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.test-case {
|
|
42
|
+
margin: 15px 0;
|
|
43
|
+
padding: 20px;
|
|
44
|
+
border: 1px solid #dee2e6;
|
|
45
|
+
border-radius: 8px;
|
|
46
|
+
background: white;
|
|
47
|
+
position: relative;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.test-case.new {
|
|
51
|
+
border-left: 4px solid #28a745;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.test-case.modify {
|
|
55
|
+
border-left: 4px solid #ffc107;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.test-case.remove {
|
|
59
|
+
border-left: 4px solid #dc3545;
|
|
60
|
+
background: #fff5f5;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.test-case-header {
|
|
64
|
+
display: flex;
|
|
65
|
+
justify-content: space-between;
|
|
66
|
+
align-items: center;
|
|
67
|
+
margin-bottom: 15px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.test-id {
|
|
71
|
+
font-weight: bold;
|
|
72
|
+
color: #3498db;
|
|
73
|
+
font-size: 14px;
|
|
74
|
+
background: #ecf0f1;
|
|
75
|
+
padding: 4px 8px;
|
|
76
|
+
border-radius: 4px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.test-type {
|
|
80
|
+
padding: 4px 12px;
|
|
81
|
+
border-radius: 20px;
|
|
82
|
+
font-size: 12px;
|
|
83
|
+
font-weight: bold;
|
|
84
|
+
text-transform: uppercase;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.test-type.new {
|
|
88
|
+
background: #d4edda;
|
|
89
|
+
color: #155724;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.test-type.modify {
|
|
93
|
+
background: #fff3cd;
|
|
94
|
+
color: #856404;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.remove-btn {
|
|
98
|
+
background: #dc3545;
|
|
99
|
+
color: white;
|
|
100
|
+
border: none;
|
|
101
|
+
padding: 6px 12px;
|
|
102
|
+
border-radius: 4px;
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
font-size: 12px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.remove-btn:hover {
|
|
108
|
+
background: #c82333;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.test-description {
|
|
112
|
+
margin: 10px 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.test-description textarea {
|
|
116
|
+
width: 100%;
|
|
117
|
+
min-height: 60px;
|
|
118
|
+
padding: 10px;
|
|
119
|
+
border: 1px solid #ced4da;
|
|
120
|
+
border-radius: 4px;
|
|
121
|
+
font-family: inherit;
|
|
122
|
+
resize: vertical;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.test-description textarea:focus {
|
|
126
|
+
outline: none;
|
|
127
|
+
border-color: #3498db;
|
|
128
|
+
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.before-after {
|
|
132
|
+
display: grid;
|
|
133
|
+
grid-template-columns: 1fr 1fr;
|
|
134
|
+
gap: 15px;
|
|
135
|
+
margin-top: 15px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.before-content, .after-content {
|
|
139
|
+
padding: 10px;
|
|
140
|
+
border-radius: 4px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.before-content {
|
|
144
|
+
background: #f8f9fa;
|
|
145
|
+
border: 1px solid #dee2e6;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.after-content {
|
|
149
|
+
background: #fff;
|
|
150
|
+
border: 1px solid #ced4da;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.before-content h4, .after-content h4 {
|
|
154
|
+
margin: 0 0 8px 0;
|
|
155
|
+
font-size: 14px;
|
|
156
|
+
color: #495057;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.after-content textarea {
|
|
160
|
+
width: 100%;
|
|
161
|
+
min-height: 50px;
|
|
162
|
+
border: 1px solid #ced4da;
|
|
163
|
+
border-radius: 4px;
|
|
164
|
+
padding: 8px;
|
|
165
|
+
font-family: inherit;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.button-container {
|
|
169
|
+
text-align: center;
|
|
170
|
+
margin-top: 40px;
|
|
171
|
+
padding-top: 30px;
|
|
172
|
+
border-top: 2px solid #e9ecef;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
button {
|
|
176
|
+
padding: 15px 30px;
|
|
177
|
+
margin: 0 10px;
|
|
178
|
+
border: none;
|
|
179
|
+
border-radius: 6px;
|
|
180
|
+
cursor: pointer;
|
|
181
|
+
font-size: 16px;
|
|
182
|
+
font-weight: bold;
|
|
183
|
+
transition: all 0.3s ease;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
#approve-btn {
|
|
187
|
+
background: #28a745;
|
|
188
|
+
color: white;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#approve-btn:hover {
|
|
192
|
+
background: #218838;
|
|
193
|
+
transform: translateY(-1px);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
#cancel-btn {
|
|
197
|
+
background: #6c757d;
|
|
198
|
+
color: white;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
#cancel-btn:hover {
|
|
202
|
+
background: #5a6268;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.status {
|
|
206
|
+
text-align: center;
|
|
207
|
+
margin-top: 20px;
|
|
208
|
+
font-weight: bold;
|
|
209
|
+
display: none;
|
|
210
|
+
padding: 15px;
|
|
211
|
+
border-radius: 6px;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.status.success {
|
|
215
|
+
background: #d4edda;
|
|
216
|
+
color: #155724;
|
|
217
|
+
border: 1px solid #c3e6cb;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.status.error {
|
|
221
|
+
background: #f8d7da;
|
|
222
|
+
color: #721c24;
|
|
223
|
+
border: 1px solid #f5c6cb;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.hidden {
|
|
227
|
+
display: none !important;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@media (max-width: 768px) {
|
|
231
|
+
.before-after {
|
|
232
|
+
grid-template-columns: 1fr;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.container {
|
|
236
|
+
padding: 15px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
button {
|
|
240
|
+
display: block;
|
|
241
|
+
width: 100%;
|
|
242
|
+
margin: 5px 0;
|
|
243
|
+
}
|
|
244
|
+
}
|
package/lib/server.js
CHANGED
|
@@ -799,23 +799,48 @@ let approvalSessions = new Map();
|
|
|
799
799
|
|
|
800
800
|
tool(
|
|
801
801
|
"review_testcases",
|
|
802
|
-
"Open
|
|
802
|
+
"Open test cases in browser for manual approval. Accepts test cases in format: [description, type, id?]",
|
|
803
803
|
{
|
|
804
|
-
testCases: zod_1.z.array(zod_1.z.
|
|
805
|
-
id: zod_1.z.string(),
|
|
806
|
-
title: zod_1.z.string(),
|
|
807
|
-
description: zod_1.z.string(),
|
|
808
|
-
})).describe("Array of test cases to review")
|
|
804
|
+
testCases: zod_1.z.array(zod_1.z.array(zod_1.z.string())).describe("Array of test case arrays: [description, type, id?] where type is 'New', 'Modify', or 'Remove'")
|
|
809
805
|
},
|
|
810
806
|
async ({ testCases }) => {
|
|
811
807
|
try {
|
|
812
808
|
const express = require('express');
|
|
809
|
+
const path = require('path');
|
|
813
810
|
const sessionId = Date.now().toString();
|
|
814
811
|
|
|
812
|
+
// Parse test cases into categories
|
|
813
|
+
const parsedTestCases = {
|
|
814
|
+
new: [],
|
|
815
|
+
modify: [],
|
|
816
|
+
remove: []
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
testCases.forEach(tc => {
|
|
820
|
+
const [description, type, id] = tc;
|
|
821
|
+
const typeKey = type.toLowerCase();
|
|
822
|
+
|
|
823
|
+
if (typeKey === 'new') {
|
|
824
|
+
parsedTestCases.new.push(description);
|
|
825
|
+
} else if (typeKey === 'modify') {
|
|
826
|
+
parsedTestCases.modify.push({
|
|
827
|
+
id: id || 'N/A',
|
|
828
|
+
original: description,
|
|
829
|
+
modified: description // Default to original, user can edit
|
|
830
|
+
});
|
|
831
|
+
} else if (typeKey === 'remove') {
|
|
832
|
+
parsedTestCases.remove.push({
|
|
833
|
+
id: id || 'N/A',
|
|
834
|
+
description: description
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
|
|
815
839
|
// Initialize session state
|
|
816
840
|
approvalSessions.set(sessionId, {
|
|
817
841
|
status: 'pending',
|
|
818
|
-
testCases:
|
|
842
|
+
testCases: parsedTestCases,
|
|
843
|
+
originalTestCases: JSON.parse(JSON.stringify(parsedTestCases)), // Deep copy
|
|
819
844
|
server: null,
|
|
820
845
|
startTime: Date.now()
|
|
821
846
|
});
|
|
@@ -823,157 +848,55 @@ tool(
|
|
|
823
848
|
const app = express();
|
|
824
849
|
const port = 3001;
|
|
825
850
|
|
|
826
|
-
//
|
|
851
|
+
// Middleware for JSON parsing
|
|
852
|
+
app.use(express.json());
|
|
853
|
+
app.use(express.static(path.join(__dirname, 'review-ui')));
|
|
854
|
+
|
|
855
|
+
// Main page
|
|
827
856
|
app.get("/", (req, res) => {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
<head>
|
|
831
|
-
<title>Test Case Approval</title>
|
|
832
|
-
<style>
|
|
833
|
-
body {
|
|
834
|
-
font-family: Arial, sans-serif;
|
|
835
|
-
margin: 20px;
|
|
836
|
-
background-color: #f5f5f5;
|
|
837
|
-
}
|
|
838
|
-
.container {
|
|
839
|
-
max-width: 1200px;
|
|
840
|
-
margin: 0 auto;
|
|
841
|
-
background: white;
|
|
842
|
-
padding: 20px;
|
|
843
|
-
border-radius: 8px;
|
|
844
|
-
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
845
|
-
}
|
|
846
|
-
h1 {
|
|
847
|
-
color: #333;
|
|
848
|
-
text-align: center;
|
|
849
|
-
border-bottom: 2px solid #007cba;
|
|
850
|
-
padding-bottom: 10px;
|
|
851
|
-
}
|
|
852
|
-
.test-case {
|
|
853
|
-
margin: 15px 0;
|
|
854
|
-
padding: 15px;
|
|
855
|
-
border: 1px solid #ddd;
|
|
856
|
-
border-radius: 8px;
|
|
857
|
-
background: #fafafa;
|
|
858
|
-
}
|
|
859
|
-
.test-id {
|
|
860
|
-
font-weight: bold;
|
|
861
|
-
color: #007cba;
|
|
862
|
-
font-size: 16px;
|
|
863
|
-
}
|
|
864
|
-
.test-title {
|
|
865
|
-
font-weight: bold;
|
|
866
|
-
margin: 5px 0;
|
|
867
|
-
color: #333;
|
|
868
|
-
}
|
|
869
|
-
.test-description {
|
|
870
|
-
color: #666;
|
|
871
|
-
line-height: 1.4;
|
|
872
|
-
}
|
|
873
|
-
.button-container {
|
|
874
|
-
text-align: center;
|
|
875
|
-
margin-top: 30px;
|
|
876
|
-
padding-top: 20px;
|
|
877
|
-
border-top: 1px solid #ddd;
|
|
878
|
-
}
|
|
879
|
-
button {
|
|
880
|
-
padding: 15px 30px;
|
|
881
|
-
background: #28a745;
|
|
882
|
-
color: white;
|
|
883
|
-
border: none;
|
|
884
|
-
border-radius: 5px;
|
|
885
|
-
cursor: pointer;
|
|
886
|
-
font-size: 16px;
|
|
887
|
-
font-weight: bold;
|
|
888
|
-
}
|
|
889
|
-
button:hover {
|
|
890
|
-
background: #218838;
|
|
891
|
-
}
|
|
892
|
-
.status {
|
|
893
|
-
text-align: center;
|
|
894
|
-
margin-top: 20px;
|
|
895
|
-
font-weight: bold;
|
|
896
|
-
display: none;
|
|
897
|
-
}
|
|
898
|
-
</style>
|
|
899
|
-
</head>
|
|
900
|
-
<body>
|
|
901
|
-
<div class="container">
|
|
902
|
-
<h1>Review Test Cases</h1>
|
|
903
|
-
<div id="test-cases">
|
|
904
|
-
`;
|
|
905
|
-
|
|
906
|
-
testCases.forEach(tc => {
|
|
907
|
-
html += `
|
|
908
|
-
<div class="test-case">
|
|
909
|
-
<div class="test-id">${tc.id}</div>
|
|
910
|
-
<div class="test-title">${tc.title}</div>
|
|
911
|
-
<div class="test-description">${tc.description}</div>
|
|
912
|
-
</div>
|
|
913
|
-
`;
|
|
914
|
-
});
|
|
857
|
+
res.sendFile(path.join(__dirname, 'review-ui', 'index.html'));
|
|
858
|
+
});
|
|
915
859
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
<script>
|
|
925
|
-
function approveTestCases() {
|
|
926
|
-
document.querySelector('button').disabled = true;
|
|
927
|
-
document.querySelector('button').style.background = '#6c757d';
|
|
928
|
-
document.querySelector('button').innerHTML = 'Processing...';
|
|
929
|
-
|
|
930
|
-
const status = document.getElementById('status');
|
|
931
|
-
status.style.display = 'block';
|
|
932
|
-
status.style.color = '#28a745';
|
|
933
|
-
status.innerHTML = 'Test cases approved! You can close this window.';
|
|
934
|
-
|
|
935
|
-
fetch('/approve/${sessionId}')
|
|
936
|
-
.then(response => response.text())
|
|
937
|
-
.then(data => {
|
|
938
|
-
status.innerHTML = data + ' You can close this window.';
|
|
939
|
-
setTimeout(() => {
|
|
940
|
-
window.close();
|
|
941
|
-
}, 2000);
|
|
942
|
-
})
|
|
943
|
-
.catch(error => {
|
|
944
|
-
status.style.color = '#dc3545';
|
|
945
|
-
status.innerHTML = 'Error: ' + error.message;
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
</script>
|
|
949
|
-
</body>
|
|
950
|
-
</html>
|
|
951
|
-
`;
|
|
952
|
-
res.send(html);
|
|
860
|
+
// API to get test cases data
|
|
861
|
+
app.get("/api/testcases", (req, res) => {
|
|
862
|
+
const session = approvalSessions.get(sessionId);
|
|
863
|
+
res.json({
|
|
864
|
+
sessionId: sessionId,
|
|
865
|
+
testCases: session ? session.testCases : {}
|
|
866
|
+
});
|
|
953
867
|
});
|
|
954
868
|
|
|
955
869
|
// Approval endpoint
|
|
956
|
-
app.
|
|
870
|
+
app.post(`/approve/${sessionId}`, (req, res) => {
|
|
957
871
|
const session = approvalSessions.get(sessionId);
|
|
958
872
|
if (session) {
|
|
959
873
|
session.status = 'approved';
|
|
874
|
+
session.finalTestCases = req.body; // Store the final approved test cases
|
|
960
875
|
approvalSessions.set(sessionId, session);
|
|
961
876
|
}
|
|
962
877
|
res.send("✓ Test cases approved successfully!");
|
|
963
878
|
});
|
|
964
879
|
|
|
880
|
+
// Cancel endpoint
|
|
881
|
+
app.post(`/cancel/${sessionId}`, (req, res) => {
|
|
882
|
+
const session = approvalSessions.get(sessionId);
|
|
883
|
+
if (session) {
|
|
884
|
+
session.status = 'cancelled';
|
|
885
|
+
approvalSessions.set(sessionId, session);
|
|
886
|
+
}
|
|
887
|
+
res.send("Review cancelled");
|
|
888
|
+
});
|
|
889
|
+
|
|
965
890
|
// Start server
|
|
966
891
|
const server = app.listen(port, async () => {
|
|
967
892
|
console.log(`Test case review server started on http://localhost:${port}`);
|
|
968
893
|
|
|
969
894
|
try {
|
|
970
|
-
// Use dynamic import for the open package (ES module)
|
|
971
895
|
const { default: open } = await import('open');
|
|
972
896
|
await open(`http://localhost:${port}`);
|
|
973
897
|
console.log('Browser opened successfully');
|
|
974
898
|
} catch (openError) {
|
|
975
899
|
console.log('Failed to open browser automatically:', openError.message);
|
|
976
|
-
console.log(`Please manually open: http://localhost:${port}`);
|
|
977
900
|
}
|
|
978
901
|
});
|
|
979
902
|
|
|
@@ -1000,7 +923,7 @@ tool(
|
|
|
1000
923
|
message: "Test case review interface opened in browser. Use check_approval_status tool to poll for approval.",
|
|
1001
924
|
testCasesCount: testCases.length,
|
|
1002
925
|
browserUrl: `http://localhost:${port}`,
|
|
1003
|
-
instructions: "Poll every
|
|
926
|
+
instructions: "Poll every 25 seconds using check_approval_status tool until approved or timeout (5 minutes)"
|
|
1004
927
|
});
|
|
1005
928
|
|
|
1006
929
|
} catch (err) {
|
|
@@ -1014,14 +937,14 @@ tool(
|
|
|
1014
937
|
|
|
1015
938
|
tool(
|
|
1016
939
|
"check_approval_status",
|
|
1017
|
-
"Check the approval status of test cases review session",
|
|
940
|
+
"Check the approval status of test cases review session with 25 second wait",
|
|
1018
941
|
{
|
|
1019
942
|
sessionId: zod_1.z.string().describe("The session ID returned from review_testcases tool")
|
|
1020
|
-
|
|
1021
|
-
async ({ sessionId }) => {
|
|
943
|
+
|
|
1022
944
|
const session = approvalSessions.get(sessionId);
|
|
1023
945
|
|
|
1024
946
|
if (!session) {
|
|
947
|
+
},
|
|
1025
948
|
return JSON.stringify({
|
|
1026
949
|
status: "error",
|
|
1027
950
|
message: "Session not found. Invalid session ID."
|
|
@@ -1032,42 +955,50 @@ tool(
|
|
|
1032
955
|
const elapsedTime = Math.floor((currentTime - session.startTime) / 1000);
|
|
1033
956
|
|
|
1034
957
|
if (session.status === 'approved') {
|
|
958
|
+
|
|
959
|
+
const approvedTestCases = session.finalTestCases || session.testCases;
|
|
960
|
+
approvalSessions.delete(sessionId);
|
|
961
|
+
|
|
1035
962
|
// Clean up session and close server
|
|
1036
963
|
if (session.server) {
|
|
1037
964
|
session.server.close();
|
|
1038
965
|
}
|
|
966
|
+
return `Test cases have been approved by the user!
|
|
967
|
+
message: "Test cases have been approved by the user.",
|
|
968
|
+
approvedTestCases: approvedTestCases,
|
|
969
|
+
elapsedTime: elapsedTime
|
|
970
|
+
session.server.close();
|
|
971
|
+
}
|
|
1039
972
|
approvalSessions.delete(sessionId);
|
|
1040
973
|
|
|
1041
|
-
return JSON.stringify({
|
|
1042
|
-
status: "approved",
|
|
1043
|
-
message: "Test cases have been approved by the user. Continuing with next operations...",
|
|
1044
|
-
testCasesCount: session.testCases.length,
|
|
1045
|
-
elapsedTime: elapsedTime
|
|
1046
974
|
});
|
|
1047
|
-
} else if (session.status === '
|
|
975
|
+
} else if (session.status === 'cancelled') {
|
|
1048
976
|
// Clean up session and close server
|
|
1049
977
|
if (session.server) {
|
|
978
|
+
status: "cancelled",
|
|
979
|
+
message: "Review was cancelled by the user.",
|
|
980
|
+
elapsedTime: elapsedTime
|
|
1050
981
|
session.server.close();
|
|
1051
982
|
}
|
|
1052
983
|
approvalSessions.delete(sessionId);
|
|
1053
984
|
|
|
1054
|
-
return JSON.stringify({
|
|
1055
|
-
status: "timeout",
|
|
1056
|
-
message: "Review session timed out after 5 minutes. Test cases were not approved.",
|
|
1057
|
-
testCasesCount: session.testCases.length,
|
|
1058
|
-
elapsedTime: elapsedTime
|
|
1059
985
|
});
|
|
986
|
+
} else if (session.status === 'timeout' || elapsedTime > 300) { // 5 minutes
|
|
987
|
+
// Clean up session and close server
|
|
988
|
+
if (session.server) {
|
|
1060
989
|
} else {
|
|
1061
|
-
|
|
1062
|
-
|
|
990
|
+
Status: timeout
|
|
991
|
+
Elapsed time: ${elapsedTime} seconds`;
|
|
992
|
+
|
|
993
|
+
status: "timeout",
|
|
1063
994
|
message: "Test cases are still pending approval. Continue polling.",
|
|
1064
|
-
testCasesCount: session.testCases.length,
|
|
1065
995
|
elapsedTime: elapsedTime,
|
|
1066
996
|
remainingTime: Math.max(0, 300 - elapsedTime)
|
|
1067
997
|
});
|
|
1068
998
|
}
|
|
1069
999
|
}
|
|
1070
1000
|
);
|
|
1001
|
+
|
|
1071
1002
|
return server;
|
|
1072
1003
|
};
|
|
1073
1004
|
|
|
@@ -7,7 +7,7 @@ final output should be test in following format
|
|
|
7
7
|
"Verify pay on credit banner exits on PDP", "Existing"
|
|
8
8
|
"Verify overview tab on PDP", "New"
|
|
9
9
|
"Verify financial services are displayed under financial tab", "Remove", "SCRUM-TC-2"
|
|
10
|
-
"Verify xyz is moved to
|
|
10
|
+
"Verify xyz is present in overview section ", "Verify xyz is moved from overview to property tour section ", "Modify", "SCRUM-TC-2"
|
|
11
11
|
Existing - already exists in TCMS and no changes needed
|
|
12
12
|
New - doesn't exist in TCMS and needs to be created
|
|
13
13
|
Remove - to remove test cases from TCMS because it is no longer valid
|