@nbakka/mcp-appium 3.0.4 → 3.0.6
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 +258 -212
- package/package.json +1 -1
package/lib/server.js
CHANGED
|
@@ -806,33 +806,62 @@ tool(
|
|
|
806
806
|
async ({ testCases }) => {
|
|
807
807
|
try {
|
|
808
808
|
const app = express();
|
|
809
|
-
|
|
809
|
+
let port = 3001;
|
|
810
|
+
|
|
811
|
+
// Find an available port
|
|
812
|
+
const findAvailablePort = async (startPort) => {
|
|
813
|
+
const net = require('net');
|
|
814
|
+
return new Promise((resolve) => {
|
|
815
|
+
const server = net.createServer();
|
|
816
|
+
server.listen(startPort, () => {
|
|
817
|
+
const port = server.address().port;
|
|
818
|
+
server.close(() => resolve(port));
|
|
819
|
+
});
|
|
820
|
+
server.on('error', () => {
|
|
821
|
+
resolve(findAvailablePort(startPort + 1));
|
|
822
|
+
});
|
|
823
|
+
});
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
port = await findAvailablePort(port);
|
|
810
827
|
|
|
811
828
|
// Generate unique session ID
|
|
812
829
|
const sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
813
830
|
|
|
814
831
|
// Store approval status
|
|
815
832
|
let approvalStatus = 'pending';
|
|
816
|
-
let finalTestCases =
|
|
833
|
+
let finalTestCases = [];
|
|
817
834
|
|
|
818
|
-
app.use(express.json({ limit: '10mb' }));
|
|
835
|
+
app.use(express.json({ limit: '10mb' }));
|
|
819
836
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
820
837
|
|
|
821
|
-
//
|
|
822
|
-
|
|
823
|
-
//
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
838
|
+
// Process test cases - handle the specific format properly
|
|
839
|
+
const processedTestCases = testCases.map((testCase, index) => {
|
|
840
|
+
// Each testCase is an array like ["title", "description", "status", "originalCase"]
|
|
841
|
+
if (Array.isArray(testCase)) {
|
|
842
|
+
return {
|
|
843
|
+
title: testCase[0] || `Test Case ${index + 1}`,
|
|
844
|
+
description: testCase[1] || '',
|
|
845
|
+
status: testCase[2] || 'New',
|
|
846
|
+
originalCase: testCase[3] || '',
|
|
847
|
+
index: index
|
|
848
|
+
};
|
|
849
|
+
} else {
|
|
850
|
+
// Fallback for unexpected format
|
|
851
|
+
return {
|
|
852
|
+
title: String(testCase) || `Test Case ${index + 1}`,
|
|
853
|
+
description: '',
|
|
854
|
+
status: 'New',
|
|
855
|
+
originalCase: '',
|
|
856
|
+
index: index
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
});
|
|
834
860
|
|
|
835
|
-
|
|
861
|
+
// Main review page with proper handling
|
|
862
|
+
app.get('/', (req, res) => {
|
|
863
|
+
try {
|
|
864
|
+
const htmlContent = `
|
|
836
865
|
<!DOCTYPE html>
|
|
837
866
|
<html lang="en">
|
|
838
867
|
<head>
|
|
@@ -900,179 +929,154 @@ tool(
|
|
|
900
929
|
|
|
901
930
|
.stat-label {
|
|
902
931
|
color: #6c757d;
|
|
932
|
+
font-size: 0.9rem;
|
|
903
933
|
margin-top: 5px;
|
|
904
934
|
}
|
|
905
935
|
|
|
906
|
-
.
|
|
907
|
-
padding:
|
|
908
|
-
max-height: 70vh;
|
|
909
|
-
overflow-y: auto;
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
.test-case {
|
|
913
|
-
background: #fff;
|
|
914
|
-
border: 2px solid #e9ecef;
|
|
915
|
-
border-radius: 12px;
|
|
916
|
-
margin-bottom: 20px;
|
|
917
|
-
overflow: hidden;
|
|
918
|
-
transition: all 0.3s ease;
|
|
919
|
-
position: relative;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
.test-case:hover {
|
|
923
|
-
border-color: #4facfe;
|
|
924
|
-
box-shadow: 0 8px 25px rgba(79, 172, 254, 0.15);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
.test-case.deleted {
|
|
928
|
-
opacity: 0.5;
|
|
929
|
-
background: #f8f9fa;
|
|
930
|
-
border-color: #dc3545;
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
.test-case-header {
|
|
936
|
+
.controls {
|
|
937
|
+
padding: 20px;
|
|
934
938
|
background: #f8f9fa;
|
|
935
|
-
padding: 15px 20px;
|
|
936
|
-
border-bottom: 1px solid #e9ecef;
|
|
937
939
|
display: flex;
|
|
938
940
|
justify-content: space-between;
|
|
939
941
|
align-items: center;
|
|
942
|
+
flex-wrap: wrap;
|
|
943
|
+
gap: 10px;
|
|
940
944
|
}
|
|
941
945
|
|
|
942
|
-
.
|
|
946
|
+
.btn {
|
|
947
|
+
padding: 12px 24px;
|
|
948
|
+
border: none;
|
|
949
|
+
border-radius: 8px;
|
|
943
950
|
font-weight: 600;
|
|
944
|
-
|
|
951
|
+
cursor: pointer;
|
|
952
|
+
transition: all 0.3s ease;
|
|
953
|
+
text-decoration: none;
|
|
954
|
+
display: inline-block;
|
|
955
|
+
font-size: 14px;
|
|
945
956
|
}
|
|
946
957
|
|
|
947
|
-
.
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
font-size: 0.85rem;
|
|
951
|
-
font-weight: 500;
|
|
952
|
-
text-transform: uppercase;
|
|
958
|
+
.btn-primary {
|
|
959
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
960
|
+
color: white;
|
|
953
961
|
}
|
|
954
962
|
|
|
955
|
-
.
|
|
956
|
-
|
|
957
|
-
|
|
963
|
+
.btn-primary:hover {
|
|
964
|
+
transform: translateY(-2px);
|
|
965
|
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
958
966
|
}
|
|
959
967
|
|
|
960
|
-
.
|
|
961
|
-
background: #
|
|
962
|
-
color:
|
|
968
|
+
.btn-secondary {
|
|
969
|
+
background: #6c757d;
|
|
970
|
+
color: white;
|
|
963
971
|
}
|
|
964
972
|
|
|
965
|
-
.
|
|
966
|
-
background: #
|
|
967
|
-
color: #721c24;
|
|
973
|
+
.btn-secondary:hover {
|
|
974
|
+
background: #5a6268;
|
|
968
975
|
}
|
|
969
976
|
|
|
970
|
-
.
|
|
971
|
-
|
|
977
|
+
.btn-delete {
|
|
978
|
+
background: #dc3545;
|
|
979
|
+
color: white;
|
|
980
|
+
padding: 8px 16px;
|
|
981
|
+
font-size: 12px;
|
|
972
982
|
}
|
|
973
983
|
|
|
974
|
-
.
|
|
975
|
-
|
|
984
|
+
.btn-delete:hover {
|
|
985
|
+
background: #c82333;
|
|
976
986
|
}
|
|
977
987
|
|
|
978
|
-
.
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
font-size: 1rem;
|
|
984
|
-
transition: border-color 0.3s ease;
|
|
985
|
-
min-height: 100px;
|
|
986
|
-
resize: vertical;
|
|
987
|
-
font-family: inherit;
|
|
988
|
+
.btn-restore {
|
|
989
|
+
background: #28a745;
|
|
990
|
+
color: white;
|
|
991
|
+
padding: 8px 16px;
|
|
992
|
+
font-size: 12px;
|
|
988
993
|
}
|
|
989
994
|
|
|
990
|
-
.
|
|
991
|
-
|
|
992
|
-
border-color: #4facfe;
|
|
993
|
-
box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.1);
|
|
995
|
+
.btn-restore:hover {
|
|
996
|
+
background: #218838;
|
|
994
997
|
}
|
|
995
998
|
|
|
996
|
-
.
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
border-radius: 6px;
|
|
1000
|
-
margin-bottom: 15px;
|
|
1001
|
-
font-size: 0.9rem;
|
|
999
|
+
.test-cases {
|
|
1000
|
+
max-height: 70vh;
|
|
1001
|
+
overflow-y: auto;
|
|
1002
1002
|
}
|
|
1003
1003
|
|
|
1004
|
-
.test-case
|
|
1005
|
-
|
|
1006
|
-
|
|
1004
|
+
.test-case {
|
|
1005
|
+
border-bottom: 1px solid #e9ecef;
|
|
1006
|
+
padding: 20px;
|
|
1007
|
+
transition: all 0.3s ease;
|
|
1007
1008
|
}
|
|
1008
1009
|
|
|
1009
|
-
.
|
|
1010
|
-
|
|
1011
|
-
border: none;
|
|
1012
|
-
border-radius: 6px;
|
|
1013
|
-
cursor: pointer;
|
|
1014
|
-
font-size: 0.9rem;
|
|
1015
|
-
transition: all 0.3s ease;
|
|
1016
|
-
font-weight: 500;
|
|
1010
|
+
.test-case:hover {
|
|
1011
|
+
background: #f8f9fa;
|
|
1017
1012
|
}
|
|
1018
1013
|
|
|
1019
|
-
.
|
|
1020
|
-
|
|
1021
|
-
|
|
1014
|
+
.test-case.deleted {
|
|
1015
|
+
opacity: 0.5;
|
|
1016
|
+
background: #f8d7da;
|
|
1022
1017
|
}
|
|
1023
1018
|
|
|
1024
|
-
.
|
|
1025
|
-
|
|
1019
|
+
.test-case-header {
|
|
1020
|
+
display: flex;
|
|
1021
|
+
justify-content: space-between;
|
|
1022
|
+
align-items: center;
|
|
1023
|
+
margin-bottom: 15px;
|
|
1026
1024
|
}
|
|
1027
1025
|
|
|
1028
|
-
.
|
|
1029
|
-
|
|
1026
|
+
.test-case-meta {
|
|
1027
|
+
display: flex;
|
|
1028
|
+
gap: 15px;
|
|
1029
|
+
align-items: center;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
.test-case-index {
|
|
1033
|
+
background: #007bff;
|
|
1030
1034
|
color: white;
|
|
1035
|
+
padding: 4px 8px;
|
|
1036
|
+
border-radius: 4px;
|
|
1037
|
+
font-size: 12px;
|
|
1038
|
+
font-weight: bold;
|
|
1031
1039
|
}
|
|
1032
1040
|
|
|
1033
|
-
.
|
|
1034
|
-
|
|
1041
|
+
.test-case-status {
|
|
1042
|
+
padding: 4px 12px;
|
|
1043
|
+
border-radius: 12px;
|
|
1044
|
+
font-size: 12px;
|
|
1045
|
+
font-weight: 600;
|
|
1035
1046
|
}
|
|
1036
1047
|
|
|
1037
|
-
.
|
|
1038
|
-
background: #
|
|
1039
|
-
|
|
1040
|
-
text-align: center;
|
|
1041
|
-
border-top: 1px solid #e9ecef;
|
|
1048
|
+
.status-new {
|
|
1049
|
+
background: #d4edda;
|
|
1050
|
+
color: #155724;
|
|
1042
1051
|
}
|
|
1043
1052
|
|
|
1044
|
-
.
|
|
1045
|
-
background:
|
|
1046
|
-
color:
|
|
1047
|
-
padding: 15px 40px;
|
|
1048
|
-
font-size: 1.1rem;
|
|
1049
|
-
border: none;
|
|
1050
|
-
border-radius: 50px;
|
|
1051
|
-
cursor: pointer;
|
|
1052
|
-
transition: all 0.3s ease;
|
|
1053
|
-
margin: 0 10px;
|
|
1053
|
+
.status-modify {
|
|
1054
|
+
background: #fff3cd;
|
|
1055
|
+
color: #856404;
|
|
1054
1056
|
}
|
|
1055
1057
|
|
|
1056
|
-
.
|
|
1057
|
-
|
|
1058
|
-
|
|
1058
|
+
.status-remove {
|
|
1059
|
+
background: #f8d7da;
|
|
1060
|
+
color: #721c24;
|
|
1059
1061
|
}
|
|
1060
1062
|
|
|
1061
|
-
.
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
padding: 15px
|
|
1065
|
-
|
|
1066
|
-
border:
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1063
|
+
.test-case textarea {
|
|
1064
|
+
width: 100%;
|
|
1065
|
+
min-height: 100px;
|
|
1066
|
+
padding: 15px;
|
|
1067
|
+
border: 2px solid #e9ecef;
|
|
1068
|
+
border-radius: 8px;
|
|
1069
|
+
font-family: inherit;
|
|
1070
|
+
font-size: 14px;
|
|
1071
|
+
line-height: 1.5;
|
|
1072
|
+
resize: vertical;
|
|
1073
|
+
transition: border-color 0.3s ease;
|
|
1071
1074
|
}
|
|
1072
1075
|
|
|
1073
|
-
.
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
+
.test-case textarea:focus {
|
|
1077
|
+
outline: none;
|
|
1078
|
+
border-color: #007bff;
|
|
1079
|
+
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
|
|
1076
1080
|
}
|
|
1077
1081
|
|
|
1078
1082
|
.notification {
|
|
@@ -1080,81 +1084,94 @@ tool(
|
|
|
1080
1084
|
top: 20px;
|
|
1081
1085
|
right: 20px;
|
|
1082
1086
|
padding: 15px 25px;
|
|
1083
|
-
background: #28a745;
|
|
1084
|
-
color: white;
|
|
1085
1087
|
border-radius: 8px;
|
|
1086
|
-
|
|
1088
|
+
color: white;
|
|
1089
|
+
font-weight: 600;
|
|
1087
1090
|
display: none;
|
|
1088
1091
|
z-index: 1000;
|
|
1092
|
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
@media (max-width: 768px) {
|
|
1096
|
+
.container {
|
|
1097
|
+
margin: 10px;
|
|
1098
|
+
border-radius: 10px;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
.header h1 {
|
|
1102
|
+
font-size: 2rem;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
.stats {
|
|
1106
|
+
flex-direction: column;
|
|
1107
|
+
gap: 15px;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
.controls {
|
|
1111
|
+
flex-direction: column;
|
|
1112
|
+
gap: 15px;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
.test-case-header {
|
|
1116
|
+
flex-direction: column;
|
|
1117
|
+
align-items: flex-start;
|
|
1118
|
+
gap: 10px;
|
|
1119
|
+
}
|
|
1089
1120
|
}
|
|
1090
1121
|
</style>
|
|
1091
1122
|
</head>
|
|
1092
1123
|
<body>
|
|
1124
|
+
<div class="notification" id="notification"></div>
|
|
1125
|
+
|
|
1093
1126
|
<div class="container">
|
|
1094
1127
|
<div class="header">
|
|
1095
|
-
<h1
|
|
1096
|
-
<p>Review, edit, and approve test cases
|
|
1128
|
+
<h1>🔍 Test Cases Review & Approval</h1>
|
|
1129
|
+
<p>Review, edit, and approve your test cases. Make any necessary changes before final approval.</p>
|
|
1097
1130
|
</div>
|
|
1098
1131
|
|
|
1099
1132
|
<div class="stats">
|
|
1100
1133
|
<div class="stat-item">
|
|
1101
|
-
<div class="stat-number" id="totalCount">${
|
|
1102
|
-
<div class="stat-label">Total
|
|
1134
|
+
<div class="stat-number" id="totalCount">${processedTestCases.length}</div>
|
|
1135
|
+
<div class="stat-label">Total Cases</div>
|
|
1103
1136
|
</div>
|
|
1104
1137
|
<div class="stat-item">
|
|
1105
|
-
<div class="stat-number" id="activeCount">${
|
|
1106
|
-
<div class="stat-label">Active</div>
|
|
1138
|
+
<div class="stat-number" id="activeCount">${processedTestCases.length}</div>
|
|
1139
|
+
<div class="stat-label">Active Cases</div>
|
|
1107
1140
|
</div>
|
|
1108
1141
|
<div class="stat-item">
|
|
1109
1142
|
<div class="stat-number" id="deletedCount">0</div>
|
|
1110
|
-
<div class="stat-label">Deleted</div>
|
|
1143
|
+
<div class="stat-label">Deleted Cases</div>
|
|
1111
1144
|
</div>
|
|
1112
1145
|
</div>
|
|
1113
1146
|
|
|
1114
|
-
<div class="
|
|
1115
|
-
<div
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1147
|
+
<div class="controls">
|
|
1148
|
+
<div>
|
|
1149
|
+
<button class="btn btn-secondary" onclick="resetAll()">🔄 Reset All</button>
|
|
1150
|
+
</div>
|
|
1151
|
+
<div>
|
|
1152
|
+
<button class="btn btn-primary" onclick="approveTestCases()">✅ Approve Test Cases</button>
|
|
1153
|
+
</div>
|
|
1154
|
+
</div>
|
|
1155
|
+
|
|
1156
|
+
<div class="test-cases">
|
|
1157
|
+
${processedTestCases.map((testCase, index) => `
|
|
1119
1158
|
<div class="test-case" data-index="${index}">
|
|
1120
1159
|
<div class="test-case-header">
|
|
1121
|
-
<
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
<div class="test-case-title">
|
|
1126
|
-
<textarea data-index="${index}" placeholder="Enter test case title and steps...">${escapedContent}</textarea>
|
|
1127
|
-
</div>
|
|
1128
|
-
${testCase[2] === 'Modify' && testCase[3] ? `
|
|
1129
|
-
<div class="original-case">
|
|
1130
|
-
<small><strong>Original:</strong> ${testCase[3]}</small>
|
|
1131
|
-
</div>
|
|
1132
|
-
` : ''}
|
|
1133
|
-
${testCase[2] === 'Remove' ? `
|
|
1134
|
-
<div class="remove-case">
|
|
1135
|
-
<small><strong>Marked for Removal</strong></small>
|
|
1136
|
-
</div>
|
|
1137
|
-
` : ''}
|
|
1138
|
-
<div class="test-case-actions">
|
|
1139
|
-
<button class="btn btn-delete" onclick="toggleDelete(${index})">Delete</button>
|
|
1160
|
+
<div class="test-case-meta">
|
|
1161
|
+
<span class="test-case-index">#${index + 1}</span>
|
|
1162
|
+
<span class="test-case-status status-${testCase.status.toLowerCase()}">${testCase.status}</span>
|
|
1163
|
+
${testCase.originalCase ? `<span style="font-size: 12px; color: #6c757d;">Ref: ${testCase.originalCase}</span>` : ''}
|
|
1140
1164
|
</div>
|
|
1165
|
+
<button class="btn btn-delete" onclick="toggleDelete(${index})">Delete</button>
|
|
1141
1166
|
</div>
|
|
1167
|
+
<textarea data-index="${index}" placeholder="Enter test case details...">${testCase.title}${testCase.description ? '\n\nSteps:\n' + testCase.description : ''}</textarea>
|
|
1142
1168
|
</div>
|
|
1143
|
-
|
|
1144
|
-
}).join('')}
|
|
1145
|
-
</div>
|
|
1146
|
-
</div>
|
|
1147
|
-
|
|
1148
|
-
<div class="footer">
|
|
1149
|
-
<button class="btn-primary" onclick="approveTestCases()">Approve Test Cases</button>
|
|
1150
|
-
<button class="btn-secondary" onclick="resetAll()">Reset All</button>
|
|
1169
|
+
`).join('')}
|
|
1151
1170
|
</div>
|
|
1152
1171
|
</div>
|
|
1153
1172
|
|
|
1154
|
-
<div class="notification" id="notification"></div>
|
|
1155
|
-
|
|
1156
1173
|
<script>
|
|
1157
|
-
let testCases = ${JSON.stringify(
|
|
1174
|
+
let testCases = ${JSON.stringify(processedTestCases).replace(/</g, '\\u003c').replace(/>/g, '\\u003e')};
|
|
1158
1175
|
let deletedIndices = new Set();
|
|
1159
1176
|
|
|
1160
1177
|
function updateStats() {
|
|
@@ -1192,7 +1209,9 @@ tool(
|
|
|
1192
1209
|
|
|
1193
1210
|
// Reset textarea value
|
|
1194
1211
|
const textarea = el.querySelector('textarea');
|
|
1195
|
-
|
|
1212
|
+
const originalTestCase = testCases[index];
|
|
1213
|
+
const resetValue = originalTestCase.title + (originalTestCase.description ? '\\n\\nSteps:\\n' + originalTestCase.description : '');
|
|
1214
|
+
textarea.value = resetValue;
|
|
1196
1215
|
});
|
|
1197
1216
|
updateStats();
|
|
1198
1217
|
}
|
|
@@ -1215,9 +1234,17 @@ tool(
|
|
|
1215
1234
|
document.querySelectorAll('.test-case').forEach((el, index) => {
|
|
1216
1235
|
if (!deletedIndices.has(index)) {
|
|
1217
1236
|
const textarea = el.querySelector('textarea');
|
|
1218
|
-
const originalTestCase =
|
|
1219
|
-
|
|
1220
|
-
|
|
1237
|
+
const originalTestCase = testCases[index];
|
|
1238
|
+
|
|
1239
|
+
// Create the updated test case array in the original format
|
|
1240
|
+
const updatedCase = [
|
|
1241
|
+
textarea.value.trim(), // Updated title/content
|
|
1242
|
+
originalTestCase.description, // Keep original description
|
|
1243
|
+
originalTestCase.status, // Keep original status
|
|
1244
|
+
originalTestCase.originalCase // Keep original case reference
|
|
1245
|
+
];
|
|
1246
|
+
|
|
1247
|
+
updatedTestCases.push(updatedCase);
|
|
1221
1248
|
}
|
|
1222
1249
|
});
|
|
1223
1250
|
|
|
@@ -1230,7 +1257,7 @@ tool(
|
|
|
1230
1257
|
body: JSON.stringify({
|
|
1231
1258
|
sessionId: '${sessionId}',
|
|
1232
1259
|
testCases: updatedTestCases
|
|
1233
|
-
|
|
1260
|
+
})
|
|
1234
1261
|
})
|
|
1235
1262
|
.then(response => response.json())
|
|
1236
1263
|
.then(data => {
|
|
@@ -1258,14 +1285,23 @@ tool(
|
|
|
1258
1285
|
if (e.target.tagName === 'TEXTAREA') {
|
|
1259
1286
|
const index = parseInt(e.target.getAttribute('data-index'));
|
|
1260
1287
|
if (!isNaN(index) && testCases[index]) {
|
|
1261
|
-
testCases[index]
|
|
1288
|
+
testCases[index].title = e.target.value.split('\\n\\nSteps:\\n')[0];
|
|
1289
|
+
const stepsIndex = e.target.value.indexOf('\\n\\nSteps:\\n');
|
|
1290
|
+
if (stepsIndex !== -1) {
|
|
1291
|
+
testCases[index].description = e.target.value.substring(stepsIndex + 9);
|
|
1292
|
+
}
|
|
1262
1293
|
}
|
|
1263
1294
|
}
|
|
1264
1295
|
});
|
|
1265
1296
|
</script>
|
|
1266
1297
|
</body>
|
|
1267
1298
|
</html>
|
|
1268
|
-
|
|
1299
|
+
`;
|
|
1300
|
+
res.send(htmlContent);
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
console.error('Error rendering page:', error);
|
|
1303
|
+
res.status(500).send('Error rendering page');
|
|
1304
|
+
}
|
|
1269
1305
|
});
|
|
1270
1306
|
|
|
1271
1307
|
// Approval endpoint with better error handling
|
|
@@ -1292,7 +1328,9 @@ tool(
|
|
|
1292
1328
|
|
|
1293
1329
|
// Close server after approval
|
|
1294
1330
|
setTimeout(() => {
|
|
1295
|
-
server.
|
|
1331
|
+
if (server && server.listening) {
|
|
1332
|
+
server.close();
|
|
1333
|
+
}
|
|
1296
1334
|
}, 3000);
|
|
1297
1335
|
} catch (error) {
|
|
1298
1336
|
console.error('Approval error:', error);
|
|
@@ -1300,26 +1338,33 @@ tool(
|
|
|
1300
1338
|
}
|
|
1301
1339
|
});
|
|
1302
1340
|
|
|
1303
|
-
//
|
|
1304
|
-
|
|
1305
|
-
console.
|
|
1341
|
+
// Error handling middleware
|
|
1342
|
+
app.use((err, req, res, next) => {
|
|
1343
|
+
console.error('Express error:', err);
|
|
1344
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
1306
1345
|
});
|
|
1307
1346
|
|
|
1308
|
-
//
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
});
|
|
1321
|
-
|
|
1322
|
-
|
|
1347
|
+
// 404 handler
|
|
1348
|
+
app.use((req, res) => {
|
|
1349
|
+
res.status(404).json({ error: 'Not found' });
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
// Start server with promise-based approach
|
|
1353
|
+
const server = await new Promise((resolve, reject) => {
|
|
1354
|
+
const srv = app.listen(port, (err) => {
|
|
1355
|
+
if (err) {
|
|
1356
|
+
reject(err);
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
console.log(`✅ Test case review session started. Session ID: ${sessionId}.`);
|
|
1360
|
+
console.log(`Server running at http://localhost:${port}`);
|
|
1361
|
+
console.log(`Browser should open automatically.`);
|
|
1362
|
+
resolve(srv);
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
srv.on('error', (error) => {
|
|
1366
|
+
reject(error);
|
|
1367
|
+
});
|
|
1323
1368
|
});
|
|
1324
1369
|
|
|
1325
1370
|
// Open browser with proper error handling
|
|
@@ -1336,8 +1381,9 @@ tool(
|
|
|
1336
1381
|
global.approvalSessions = global.approvalSessions || {};
|
|
1337
1382
|
global.approvalSessions[sessionId] = {
|
|
1338
1383
|
status: 'pending',
|
|
1339
|
-
testCases:
|
|
1340
|
-
timestamp: Date.now()
|
|
1384
|
+
testCases: processedTestCases,
|
|
1385
|
+
timestamp: Date.now(),
|
|
1386
|
+
server: server
|
|
1341
1387
|
};
|
|
1342
1388
|
|
|
1343
1389
|
return `✅ Test case review session started. Session ID: ${sessionId}.\nServer running at http://localhost:${port}\n${openAttemptFailed ? 'Please manually open the URL in your browser.' : 'Browser should open automatically.'}`;
|