@nbakka/mcp-appium 3.0.4 → 3.0.5
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/server.js +119 -39
- package/package.json +1 -1
package/lib/server.js
CHANGED
|
@@ -818,21 +818,33 @@ tool(
|
|
|
818
818
|
app.use(express.json({ limit: '10mb' })); // Increase JSON limit
|
|
819
819
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
820
820
|
|
|
821
|
-
//
|
|
822
|
-
|
|
823
|
-
//
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
821
|
+
// Process test cases - handle the specific format properly
|
|
822
|
+
const processedTestCases = testCases.map((testCase, index) => {
|
|
823
|
+
// Each testCase is an array like ["title", "description", "status", "originalCase"]
|
|
824
|
+
if (Array.isArray(testCase)) {
|
|
825
|
+
return {
|
|
826
|
+
title: testCase[0] || `Test Case ${index + 1}`,
|
|
827
|
+
description: testCase[1] || '',
|
|
828
|
+
status: testCase[2] || 'New',
|
|
829
|
+
originalCase: testCase[3] || '',
|
|
830
|
+
index: index
|
|
831
|
+
};
|
|
832
|
+
} else {
|
|
833
|
+
// Fallback for unexpected format
|
|
834
|
+
return {
|
|
835
|
+
title: String(testCase) || `Test Case ${index + 1}`,
|
|
836
|
+
description: '',
|
|
837
|
+
status: 'New',
|
|
838
|
+
originalCase: '',
|
|
839
|
+
index: index
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
});
|
|
834
843
|
|
|
835
|
-
|
|
844
|
+
// Main review page with proper handling
|
|
845
|
+
app.get('/', (req, res) => {
|
|
846
|
+
try {
|
|
847
|
+
res.send(`
|
|
836
848
|
<!DOCTYPE html>
|
|
837
849
|
<html lang="en">
|
|
838
850
|
<head>
|
|
@@ -982,7 +994,7 @@ tool(
|
|
|
982
994
|
padding: 10px;
|
|
983
995
|
font-size: 1rem;
|
|
984
996
|
transition: border-color 0.3s ease;
|
|
985
|
-
min-height:
|
|
997
|
+
min-height: 120px;
|
|
986
998
|
resize: vertical;
|
|
987
999
|
font-family: inherit;
|
|
988
1000
|
}
|
|
@@ -993,8 +1005,18 @@ tool(
|
|
|
993
1005
|
box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.1);
|
|
994
1006
|
}
|
|
995
1007
|
|
|
996
|
-
.original-case
|
|
997
|
-
background: #
|
|
1008
|
+
.original-case {
|
|
1009
|
+
background: #fff3cd;
|
|
1010
|
+
border: 1px solid #ffeaa7;
|
|
1011
|
+
padding: 10px;
|
|
1012
|
+
border-radius: 6px;
|
|
1013
|
+
margin-bottom: 15px;
|
|
1014
|
+
font-size: 0.9rem;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
.remove-case {
|
|
1018
|
+
background: #f8d7da;
|
|
1019
|
+
border: 1px solid #f5c6cb;
|
|
998
1020
|
padding: 10px;
|
|
999
1021
|
border-radius: 6px;
|
|
1000
1022
|
margin-bottom: 15px;
|
|
@@ -1098,11 +1120,11 @@ tool(
|
|
|
1098
1120
|
|
|
1099
1121
|
<div class="stats">
|
|
1100
1122
|
<div class="stat-item">
|
|
1101
|
-
<div class="stat-number" id="totalCount">${
|
|
1123
|
+
<div class="stat-number" id="totalCount">${processedTestCases.length}</div>
|
|
1102
1124
|
<div class="stat-label">Total Test Cases</div>
|
|
1103
1125
|
</div>
|
|
1104
1126
|
<div class="stat-item">
|
|
1105
|
-
<div class="stat-number" id="activeCount">${
|
|
1127
|
+
<div class="stat-number" id="activeCount">${processedTestCases.length}</div>
|
|
1106
1128
|
<div class="stat-label">Active</div>
|
|
1107
1129
|
</div>
|
|
1108
1130
|
<div class="stat-item">
|
|
@@ -1113,26 +1135,49 @@ tool(
|
|
|
1113
1135
|
|
|
1114
1136
|
<div class="content">
|
|
1115
1137
|
<div id="testCases">
|
|
1116
|
-
${
|
|
1117
|
-
|
|
1138
|
+
${processedTestCases.map((testCase, index) => {
|
|
1139
|
+
// Safely escape HTML characters
|
|
1140
|
+
const safeTitle = testCase.title
|
|
1141
|
+
.replace(/&/g, '&')
|
|
1142
|
+
.replace(/</g, '<')
|
|
1143
|
+
.replace(/>/g, '>')
|
|
1144
|
+
.replace(/"/g, '"')
|
|
1145
|
+
.replace(/'/g, ''');
|
|
1146
|
+
|
|
1147
|
+
const safeDescription = testCase.description
|
|
1148
|
+
.replace(/&/g, '&')
|
|
1149
|
+
.replace(/</g, '<')
|
|
1150
|
+
.replace(/>/g, '>')
|
|
1151
|
+
.replace(/"/g, '"')
|
|
1152
|
+
.replace(/'/g, ''');
|
|
1153
|
+
|
|
1154
|
+
const safeOriginal = testCase.originalCase
|
|
1155
|
+
.replace(/&/g, '&')
|
|
1156
|
+
.replace(/</g, '<')
|
|
1157
|
+
.replace(/>/g, '>')
|
|
1158
|
+
.replace(/"/g, '"')
|
|
1159
|
+
.replace(/'/g, ''');
|
|
1160
|
+
|
|
1118
1161
|
return `
|
|
1119
1162
|
<div class="test-case" data-index="${index}">
|
|
1120
1163
|
<div class="test-case-header">
|
|
1121
1164
|
<span class="test-case-id">Test Case #${index + 1}</span>
|
|
1122
|
-
<span class="test-case-status status-${testCase
|
|
1165
|
+
<span class="test-case-status status-${testCase.status.toLowerCase()}">${testCase.status}</span>
|
|
1123
1166
|
</div>
|
|
1124
1167
|
<div class="test-case-content">
|
|
1125
1168
|
<div class="test-case-title">
|
|
1126
|
-
<textarea data-index="${index}" placeholder="Enter test case title and steps...">${
|
|
1169
|
+
<textarea data-index="${index}" placeholder="Enter test case title and steps...">${safeTitle}${safeDescription ? '\n\nSteps:\n' + safeDescription : ''}</textarea>
|
|
1127
1170
|
</div>
|
|
1128
|
-
${testCase
|
|
1171
|
+
${testCase.status === 'Modify' && testCase.originalCase ? `
|
|
1129
1172
|
<div class="original-case">
|
|
1130
|
-
<
|
|
1173
|
+
<strong>Original Test Case:</strong><br>
|
|
1174
|
+
${safeOriginal}
|
|
1131
1175
|
</div>
|
|
1132
1176
|
` : ''}
|
|
1133
|
-
${testCase
|
|
1177
|
+
${testCase.status === 'Remove' ? `
|
|
1134
1178
|
<div class="remove-case">
|
|
1135
|
-
<
|
|
1179
|
+
<strong>⚠️ Marked for Removal</strong><br>
|
|
1180
|
+
This test case is scheduled to be removed. You can restore it using the button below.
|
|
1136
1181
|
</div>
|
|
1137
1182
|
` : ''}
|
|
1138
1183
|
<div class="test-case-actions">
|
|
@@ -1154,7 +1199,7 @@ tool(
|
|
|
1154
1199
|
<div class="notification" id="notification"></div>
|
|
1155
1200
|
|
|
1156
1201
|
<script>
|
|
1157
|
-
let testCases = ${JSON.stringify(
|
|
1202
|
+
let testCases = ${JSON.stringify(processedTestCases)};
|
|
1158
1203
|
let deletedIndices = new Set();
|
|
1159
1204
|
|
|
1160
1205
|
function updateStats() {
|
|
@@ -1192,7 +1237,9 @@ tool(
|
|
|
1192
1237
|
|
|
1193
1238
|
// Reset textarea value
|
|
1194
1239
|
const textarea = el.querySelector('textarea');
|
|
1195
|
-
|
|
1240
|
+
const originalTestCase = testCases[index];
|
|
1241
|
+
const resetValue = originalTestCase.title + (originalTestCase.description ? '\n\nSteps:\n' + originalTestCase.description : '');
|
|
1242
|
+
textarea.value = resetValue;
|
|
1196
1243
|
});
|
|
1197
1244
|
updateStats();
|
|
1198
1245
|
}
|
|
@@ -1215,9 +1262,17 @@ tool(
|
|
|
1215
1262
|
document.querySelectorAll('.test-case').forEach((el, index) => {
|
|
1216
1263
|
if (!deletedIndices.has(index)) {
|
|
1217
1264
|
const textarea = el.querySelector('textarea');
|
|
1218
|
-
const originalTestCase =
|
|
1219
|
-
|
|
1220
|
-
|
|
1265
|
+
const originalTestCase = testCases[index];
|
|
1266
|
+
|
|
1267
|
+
// Create the updated test case array in the original format
|
|
1268
|
+
const updatedCase = [
|
|
1269
|
+
textarea.value.trim(), // Updated title/content
|
|
1270
|
+
originalTestCase.description, // Keep original description
|
|
1271
|
+
originalTestCase.status, // Keep original status
|
|
1272
|
+
originalTestCase.originalCase // Keep original case reference
|
|
1273
|
+
];
|
|
1274
|
+
|
|
1275
|
+
updatedTestCases.push(updatedCase);
|
|
1221
1276
|
}
|
|
1222
1277
|
});
|
|
1223
1278
|
|
|
@@ -1258,14 +1313,22 @@ tool(
|
|
|
1258
1313
|
if (e.target.tagName === 'TEXTAREA') {
|
|
1259
1314
|
const index = parseInt(e.target.getAttribute('data-index'));
|
|
1260
1315
|
if (!isNaN(index) && testCases[index]) {
|
|
1261
|
-
testCases[index]
|
|
1316
|
+
testCases[index].title = e.target.value.split('\n\nSteps:\n')[0];
|
|
1317
|
+
const stepsIndex = e.target.value.indexOf('\n\nSteps:\n');
|
|
1318
|
+
if (stepsIndex !== -1) {
|
|
1319
|
+
testCases[index].description = e.target.value.substring(stepsIndex + 9);
|
|
1320
|
+
}
|
|
1262
1321
|
}
|
|
1263
1322
|
}
|
|
1264
1323
|
});
|
|
1265
1324
|
</script>
|
|
1266
1325
|
</body>
|
|
1267
1326
|
</html>
|
|
1268
|
-
|
|
1327
|
+
`);
|
|
1328
|
+
} catch (error) {
|
|
1329
|
+
console.error('Error rendering page:', error);
|
|
1330
|
+
res.status(500).send('Error rendering page');
|
|
1331
|
+
}
|
|
1269
1332
|
});
|
|
1270
1333
|
|
|
1271
1334
|
// Approval endpoint with better error handling
|
|
@@ -1300,9 +1363,25 @@ tool(
|
|
|
1300
1363
|
}
|
|
1301
1364
|
});
|
|
1302
1365
|
|
|
1303
|
-
//
|
|
1304
|
-
|
|
1305
|
-
console.
|
|
1366
|
+
// Error handling middleware
|
|
1367
|
+
app.use((err, req, res, next) => {
|
|
1368
|
+
console.error('Express error:', err);
|
|
1369
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
// 404 handler
|
|
1373
|
+
app.use((req, res) => {
|
|
1374
|
+
res.status(404).json({ error: 'Not found' });
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
// Start server with better error handling
|
|
1378
|
+
const server = app.listen(port, (err) => {
|
|
1379
|
+
if (err) {
|
|
1380
|
+
console.error('Failed to start server:', err);
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
console.log(`✅ Test case review session started. Session ID: ${sessionId}.`);
|
|
1384
|
+
console.log(`Server running at http://localhost:${port}`);
|
|
1306
1385
|
});
|
|
1307
1386
|
|
|
1308
1387
|
// Handle server errors
|
|
@@ -1310,7 +1389,7 @@ tool(
|
|
|
1310
1389
|
if (error.code === 'EADDRINUSE') {
|
|
1311
1390
|
// Try a different port
|
|
1312
1391
|
const newPort = port + Math.floor(Math.random() * 100);
|
|
1313
|
-
|
|
1392
|
+
const newServer = app.listen(newPort, () => {
|
|
1314
1393
|
console.log(`Test case review server running at http://localhost:${newPort}`);
|
|
1315
1394
|
try {
|
|
1316
1395
|
openBrowser(`http://localhost:${newPort}`).catch(err => console.error('Failed to open browser:', err));
|
|
@@ -1318,6 +1397,7 @@ tool(
|
|
|
1318
1397
|
console.error('Failed to open browser automatically:', e.message);
|
|
1319
1398
|
}
|
|
1320
1399
|
});
|
|
1400
|
+
return newServer;
|
|
1321
1401
|
}
|
|
1322
1402
|
throw error;
|
|
1323
1403
|
});
|
|
@@ -1336,7 +1416,7 @@ tool(
|
|
|
1336
1416
|
global.approvalSessions = global.approvalSessions || {};
|
|
1337
1417
|
global.approvalSessions[sessionId] = {
|
|
1338
1418
|
status: 'pending',
|
|
1339
|
-
testCases:
|
|
1419
|
+
testCases: processedTestCases,
|
|
1340
1420
|
timestamp: Date.now()
|
|
1341
1421
|
};
|
|
1342
1422
|
|