@nbakka/mcp-appium 3.0.5 → 3.0.7
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 +234 -235
- package/package.json +1 -1
package/lib/server.js
CHANGED
|
@@ -806,16 +806,33 @@ 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
838
|
// Process test cases - handle the specific format properly
|
|
@@ -841,10 +858,26 @@ tool(
|
|
|
841
858
|
}
|
|
842
859
|
});
|
|
843
860
|
|
|
861
|
+
// Helper function to get display text for test cases
|
|
862
|
+
const getTestCaseDisplayText = (testCase) => {
|
|
863
|
+
const status = testCase.status.toLowerCase();
|
|
864
|
+
|
|
865
|
+
if (status === 'modify') {
|
|
866
|
+
// For modify cases, show original → changed format
|
|
867
|
+
return `${testCase.title}\n\nOriginal: ${testCase.originalCase || 'Not specified'}\nChanged to: ${testCase.description || 'Not specified'}`;
|
|
868
|
+
} else if (status === 'remove') {
|
|
869
|
+
// For remove cases, just show the title
|
|
870
|
+
return testCase.title;
|
|
871
|
+
} else {
|
|
872
|
+
// For new cases, just show the title
|
|
873
|
+
return testCase.title;
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
|
|
844
877
|
// Main review page with proper handling
|
|
845
878
|
app.get('/', (req, res) => {
|
|
846
879
|
try {
|
|
847
|
-
|
|
880
|
+
const htmlContent = `
|
|
848
881
|
<!DOCTYPE html>
|
|
849
882
|
<html lang="en">
|
|
850
883
|
<head>
|
|
@@ -912,189 +945,154 @@ tool(
|
|
|
912
945
|
|
|
913
946
|
.stat-label {
|
|
914
947
|
color: #6c757d;
|
|
948
|
+
font-size: 0.9rem;
|
|
915
949
|
margin-top: 5px;
|
|
916
950
|
}
|
|
917
951
|
|
|
918
|
-
.
|
|
919
|
-
padding:
|
|
920
|
-
max-height: 70vh;
|
|
921
|
-
overflow-y: auto;
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
.test-case {
|
|
925
|
-
background: #fff;
|
|
926
|
-
border: 2px solid #e9ecef;
|
|
927
|
-
border-radius: 12px;
|
|
928
|
-
margin-bottom: 20px;
|
|
929
|
-
overflow: hidden;
|
|
930
|
-
transition: all 0.3s ease;
|
|
931
|
-
position: relative;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
.test-case:hover {
|
|
935
|
-
border-color: #4facfe;
|
|
936
|
-
box-shadow: 0 8px 25px rgba(79, 172, 254, 0.15);
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
.test-case.deleted {
|
|
940
|
-
opacity: 0.5;
|
|
941
|
-
background: #f8f9fa;
|
|
942
|
-
border-color: #dc3545;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
.test-case-header {
|
|
952
|
+
.controls {
|
|
953
|
+
padding: 20px;
|
|
946
954
|
background: #f8f9fa;
|
|
947
|
-
padding: 15px 20px;
|
|
948
|
-
border-bottom: 1px solid #e9ecef;
|
|
949
955
|
display: flex;
|
|
950
956
|
justify-content: space-between;
|
|
951
957
|
align-items: center;
|
|
958
|
+
flex-wrap: wrap;
|
|
959
|
+
gap: 10px;
|
|
952
960
|
}
|
|
953
961
|
|
|
954
|
-
.
|
|
962
|
+
.btn {
|
|
963
|
+
padding: 12px 24px;
|
|
964
|
+
border: none;
|
|
965
|
+
border-radius: 8px;
|
|
955
966
|
font-weight: 600;
|
|
956
|
-
|
|
967
|
+
cursor: pointer;
|
|
968
|
+
transition: all 0.3s ease;
|
|
969
|
+
text-decoration: none;
|
|
970
|
+
display: inline-block;
|
|
971
|
+
font-size: 14px;
|
|
957
972
|
}
|
|
958
973
|
|
|
959
|
-
.
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
font-size: 0.85rem;
|
|
963
|
-
font-weight: 500;
|
|
964
|
-
text-transform: uppercase;
|
|
974
|
+
.btn-primary {
|
|
975
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
976
|
+
color: white;
|
|
965
977
|
}
|
|
966
978
|
|
|
967
|
-
.
|
|
968
|
-
|
|
969
|
-
|
|
979
|
+
.btn-primary:hover {
|
|
980
|
+
transform: translateY(-2px);
|
|
981
|
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
970
982
|
}
|
|
971
983
|
|
|
972
|
-
.
|
|
973
|
-
background: #
|
|
974
|
-
color:
|
|
984
|
+
.btn-secondary {
|
|
985
|
+
background: #6c757d;
|
|
986
|
+
color: white;
|
|
975
987
|
}
|
|
976
988
|
|
|
977
|
-
.
|
|
978
|
-
background: #
|
|
979
|
-
color: #721c24;
|
|
989
|
+
.btn-secondary:hover {
|
|
990
|
+
background: #5a6268;
|
|
980
991
|
}
|
|
981
992
|
|
|
982
|
-
.
|
|
983
|
-
|
|
993
|
+
.btn-delete {
|
|
994
|
+
background: #dc3545;
|
|
995
|
+
color: white;
|
|
996
|
+
padding: 8px 16px;
|
|
997
|
+
font-size: 12px;
|
|
984
998
|
}
|
|
985
999
|
|
|
986
|
-
.
|
|
987
|
-
|
|
1000
|
+
.btn-delete:hover {
|
|
1001
|
+
background: #c82333;
|
|
988
1002
|
}
|
|
989
1003
|
|
|
990
|
-
.
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
font-size: 1rem;
|
|
996
|
-
transition: border-color 0.3s ease;
|
|
997
|
-
min-height: 120px;
|
|
998
|
-
resize: vertical;
|
|
999
|
-
font-family: inherit;
|
|
1004
|
+
.btn-restore {
|
|
1005
|
+
background: #28a745;
|
|
1006
|
+
color: white;
|
|
1007
|
+
padding: 8px 16px;
|
|
1008
|
+
font-size: 12px;
|
|
1000
1009
|
}
|
|
1001
1010
|
|
|
1002
|
-
.
|
|
1003
|
-
|
|
1004
|
-
border-color: #4facfe;
|
|
1005
|
-
box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.1);
|
|
1011
|
+
.btn-restore:hover {
|
|
1012
|
+
background: #218838;
|
|
1006
1013
|
}
|
|
1007
1014
|
|
|
1008
|
-
.
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
padding: 10px;
|
|
1012
|
-
border-radius: 6px;
|
|
1013
|
-
margin-bottom: 15px;
|
|
1014
|
-
font-size: 0.9rem;
|
|
1015
|
+
.test-cases {
|
|
1016
|
+
max-height: 70vh;
|
|
1017
|
+
overflow-y: auto;
|
|
1015
1018
|
}
|
|
1016
1019
|
|
|
1017
|
-
.
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
border-radius: 6px;
|
|
1022
|
-
margin-bottom: 15px;
|
|
1023
|
-
font-size: 0.9rem;
|
|
1020
|
+
.test-case {
|
|
1021
|
+
border-bottom: 1px solid #e9ecef;
|
|
1022
|
+
padding: 20px;
|
|
1023
|
+
transition: all 0.3s ease;
|
|
1024
1024
|
}
|
|
1025
1025
|
|
|
1026
|
-
.test-case
|
|
1027
|
-
|
|
1028
|
-
gap: 10px;
|
|
1026
|
+
.test-case:hover {
|
|
1027
|
+
background: #f8f9fa;
|
|
1029
1028
|
}
|
|
1030
1029
|
|
|
1031
|
-
.
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
border-radius: 6px;
|
|
1035
|
-
cursor: pointer;
|
|
1036
|
-
font-size: 0.9rem;
|
|
1037
|
-
transition: all 0.3s ease;
|
|
1038
|
-
font-weight: 500;
|
|
1030
|
+
.test-case.deleted {
|
|
1031
|
+
opacity: 0.5;
|
|
1032
|
+
background: #f8d7da;
|
|
1039
1033
|
}
|
|
1040
1034
|
|
|
1041
|
-
.
|
|
1042
|
-
|
|
1043
|
-
|
|
1035
|
+
.test-case-header {
|
|
1036
|
+
display: flex;
|
|
1037
|
+
justify-content: space-between;
|
|
1038
|
+
align-items: center;
|
|
1039
|
+
margin-bottom: 15px;
|
|
1044
1040
|
}
|
|
1045
1041
|
|
|
1046
|
-
.
|
|
1047
|
-
|
|
1042
|
+
.test-case-meta {
|
|
1043
|
+
display: flex;
|
|
1044
|
+
gap: 15px;
|
|
1045
|
+
align-items: center;
|
|
1048
1046
|
}
|
|
1049
1047
|
|
|
1050
|
-
.
|
|
1051
|
-
background: #
|
|
1048
|
+
.test-case-index {
|
|
1049
|
+
background: #007bff;
|
|
1052
1050
|
color: white;
|
|
1051
|
+
padding: 4px 8px;
|
|
1052
|
+
border-radius: 4px;
|
|
1053
|
+
font-size: 12px;
|
|
1054
|
+
font-weight: bold;
|
|
1053
1055
|
}
|
|
1054
1056
|
|
|
1055
|
-
.
|
|
1056
|
-
|
|
1057
|
+
.test-case-status {
|
|
1058
|
+
padding: 4px 12px;
|
|
1059
|
+
border-radius: 12px;
|
|
1060
|
+
font-size: 12px;
|
|
1061
|
+
font-weight: 600;
|
|
1057
1062
|
}
|
|
1058
1063
|
|
|
1059
|
-
.
|
|
1060
|
-
background: #
|
|
1061
|
-
|
|
1062
|
-
text-align: center;
|
|
1063
|
-
border-top: 1px solid #e9ecef;
|
|
1064
|
+
.status-new {
|
|
1065
|
+
background: #d4edda;
|
|
1066
|
+
color: #155724;
|
|
1064
1067
|
}
|
|
1065
1068
|
|
|
1066
|
-
.
|
|
1067
|
-
background:
|
|
1068
|
-
color:
|
|
1069
|
-
padding: 15px 40px;
|
|
1070
|
-
font-size: 1.1rem;
|
|
1071
|
-
border: none;
|
|
1072
|
-
border-radius: 50px;
|
|
1073
|
-
cursor: pointer;
|
|
1074
|
-
transition: all 0.3s ease;
|
|
1075
|
-
margin: 0 10px;
|
|
1069
|
+
.status-modify {
|
|
1070
|
+
background: #fff3cd;
|
|
1071
|
+
color: #856404;
|
|
1076
1072
|
}
|
|
1077
1073
|
|
|
1078
|
-
.
|
|
1079
|
-
|
|
1080
|
-
|
|
1074
|
+
.status-remove {
|
|
1075
|
+
background: #f8d7da;
|
|
1076
|
+
color: #721c24;
|
|
1081
1077
|
}
|
|
1082
1078
|
|
|
1083
|
-
.
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
padding: 15px
|
|
1087
|
-
|
|
1088
|
-
border:
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1079
|
+
.test-case textarea {
|
|
1080
|
+
width: 100%;
|
|
1081
|
+
min-height: 100px;
|
|
1082
|
+
padding: 15px;
|
|
1083
|
+
border: 2px solid #e9ecef;
|
|
1084
|
+
border-radius: 8px;
|
|
1085
|
+
font-family: inherit;
|
|
1086
|
+
font-size: 14px;
|
|
1087
|
+
line-height: 1.5;
|
|
1088
|
+
resize: vertical;
|
|
1089
|
+
transition: border-color 0.3s ease;
|
|
1093
1090
|
}
|
|
1094
1091
|
|
|
1095
|
-
.
|
|
1096
|
-
|
|
1097
|
-
|
|
1092
|
+
.test-case textarea:focus {
|
|
1093
|
+
outline: none;
|
|
1094
|
+
border-color: #007bff;
|
|
1095
|
+
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
|
|
1098
1096
|
}
|
|
1099
1097
|
|
|
1100
1098
|
.notification {
|
|
@@ -1102,104 +1100,104 @@ tool(
|
|
|
1102
1100
|
top: 20px;
|
|
1103
1101
|
right: 20px;
|
|
1104
1102
|
padding: 15px 25px;
|
|
1105
|
-
background: #28a745;
|
|
1106
|
-
color: white;
|
|
1107
1103
|
border-radius: 8px;
|
|
1108
|
-
|
|
1104
|
+
color: white;
|
|
1105
|
+
font-weight: 600;
|
|
1109
1106
|
display: none;
|
|
1110
1107
|
z-index: 1000;
|
|
1108
|
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
@media (max-width: 768px) {
|
|
1112
|
+
.container {
|
|
1113
|
+
margin: 10px;
|
|
1114
|
+
border-radius: 10px;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
.header h1 {
|
|
1118
|
+
font-size: 2rem;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
.stats {
|
|
1122
|
+
flex-direction: column;
|
|
1123
|
+
gap: 15px;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
.controls {
|
|
1127
|
+
flex-direction: column;
|
|
1128
|
+
gap: 15px;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
.test-case-header {
|
|
1132
|
+
flex-direction: column;
|
|
1133
|
+
align-items: flex-start;
|
|
1134
|
+
gap: 10px;
|
|
1135
|
+
}
|
|
1111
1136
|
}
|
|
1112
1137
|
</style>
|
|
1113
1138
|
</head>
|
|
1114
1139
|
<body>
|
|
1140
|
+
<div class="notification" id="notification"></div>
|
|
1141
|
+
|
|
1115
1142
|
<div class="container">
|
|
1116
1143
|
<div class="header">
|
|
1117
|
-
<h1
|
|
1118
|
-
<p>Review, edit, and approve test cases
|
|
1144
|
+
<h1>🔍 Test Cases Review & Approval</h1>
|
|
1145
|
+
<p>Review, edit, and approve your test cases. Make any necessary changes before final approval.</p>
|
|
1119
1146
|
</div>
|
|
1120
1147
|
|
|
1121
1148
|
<div class="stats">
|
|
1122
1149
|
<div class="stat-item">
|
|
1123
1150
|
<div class="stat-number" id="totalCount">${processedTestCases.length}</div>
|
|
1124
|
-
<div class="stat-label">Total
|
|
1151
|
+
<div class="stat-label">Total Cases</div>
|
|
1125
1152
|
</div>
|
|
1126
1153
|
<div class="stat-item">
|
|
1127
1154
|
<div class="stat-number" id="activeCount">${processedTestCases.length}</div>
|
|
1128
|
-
<div class="stat-label">Active</div>
|
|
1155
|
+
<div class="stat-label">Active Cases</div>
|
|
1129
1156
|
</div>
|
|
1130
1157
|
<div class="stat-item">
|
|
1131
1158
|
<div class="stat-number" id="deletedCount">0</div>
|
|
1132
|
-
<div class="stat-label">Deleted</div>
|
|
1159
|
+
<div class="stat-label">Deleted Cases</div>
|
|
1160
|
+
</div>
|
|
1161
|
+
</div>
|
|
1162
|
+
|
|
1163
|
+
<div class="controls">
|
|
1164
|
+
<div>
|
|
1165
|
+
<button class="btn btn-secondary" onclick="resetAll()">🔄 Reset All</button>
|
|
1166
|
+
</div>
|
|
1167
|
+
<div>
|
|
1168
|
+
<button class="btn btn-primary" onclick="approveTestCases()">✅ Approve Test Cases</button>
|
|
1133
1169
|
</div>
|
|
1134
1170
|
</div>
|
|
1135
1171
|
|
|
1136
|
-
<div class="
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
-
|
|
1161
|
-
return `
|
|
1172
|
+
<div class="test-cases">
|
|
1173
|
+
${processedTestCases.map((testCase, index) => {
|
|
1174
|
+
const displayText = getTestCaseDisplayText(testCase);
|
|
1175
|
+
const statusLabel = testCase.status === 'Remove' ? 'Remove' : testCase.status;
|
|
1176
|
+
const refText = testCase.originalCase && testCase.status.toLowerCase() === 'remove'
|
|
1177
|
+
? `Ref: ${testCase.originalCase}`
|
|
1178
|
+
: testCase.originalCase && testCase.status.toLowerCase() !== 'modify'
|
|
1179
|
+
? `Ref: ${testCase.originalCase}`
|
|
1180
|
+
: '';
|
|
1181
|
+
|
|
1182
|
+
return `
|
|
1162
1183
|
<div class="test-case" data-index="${index}">
|
|
1163
1184
|
<div class="test-case-header">
|
|
1164
|
-
<
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
<div class="test-case-title">
|
|
1169
|
-
<textarea data-index="${index}" placeholder="Enter test case title and steps...">${safeTitle}${safeDescription ? '\n\nSteps:\n' + safeDescription : ''}</textarea>
|
|
1170
|
-
</div>
|
|
1171
|
-
${testCase.status === 'Modify' && testCase.originalCase ? `
|
|
1172
|
-
<div class="original-case">
|
|
1173
|
-
<strong>Original Test Case:</strong><br>
|
|
1174
|
-
${safeOriginal}
|
|
1175
|
-
</div>
|
|
1176
|
-
` : ''}
|
|
1177
|
-
${testCase.status === 'Remove' ? `
|
|
1178
|
-
<div class="remove-case">
|
|
1179
|
-
<strong>⚠️ Marked for Removal</strong><br>
|
|
1180
|
-
This test case is scheduled to be removed. You can restore it using the button below.
|
|
1181
|
-
</div>
|
|
1182
|
-
` : ''}
|
|
1183
|
-
<div class="test-case-actions">
|
|
1184
|
-
<button class="btn btn-delete" onclick="toggleDelete(${index})">Delete</button>
|
|
1185
|
+
<div class="test-case-meta">
|
|
1186
|
+
<span class="test-case-index">#${index + 1}</span>
|
|
1187
|
+
<span class="test-case-status status-${testCase.status.toLowerCase()}">${statusLabel}</span>
|
|
1188
|
+
${refText ? `<span style="font-size: 12px; color: #6c757d;">${refText}</span>` : ''}
|
|
1185
1189
|
</div>
|
|
1190
|
+
<button class="btn btn-delete" onclick="toggleDelete(${index})">Delete</button>
|
|
1186
1191
|
</div>
|
|
1192
|
+
<textarea data-index="${index}" placeholder="Enter test case details...">${displayText}</textarea>
|
|
1187
1193
|
</div>
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
</div>
|
|
1191
|
-
</div>
|
|
1192
|
-
|
|
1193
|
-
<div class="footer">
|
|
1194
|
-
<button class="btn-primary" onclick="approveTestCases()">Approve Test Cases</button>
|
|
1195
|
-
<button class="btn-secondary" onclick="resetAll()">Reset All</button>
|
|
1194
|
+
`;
|
|
1195
|
+
}).join('')}
|
|
1196
1196
|
</div>
|
|
1197
1197
|
</div>
|
|
1198
1198
|
|
|
1199
|
-
<div class="notification" id="notification"></div>
|
|
1200
|
-
|
|
1201
1199
|
<script>
|
|
1202
|
-
let testCases = ${JSON.stringify(processedTestCases)};
|
|
1200
|
+
let testCases = ${JSON.stringify(processedTestCases).replace(/</g, '\\u003c').replace(/>/g, '\\u003e')};
|
|
1203
1201
|
let deletedIndices = new Set();
|
|
1204
1202
|
|
|
1205
1203
|
function updateStats() {
|
|
@@ -1238,7 +1236,17 @@ tool(
|
|
|
1238
1236
|
// Reset textarea value
|
|
1239
1237
|
const textarea = el.querySelector('textarea');
|
|
1240
1238
|
const originalTestCase = testCases[index];
|
|
1241
|
-
|
|
1239
|
+
|
|
1240
|
+
// Use proper display format based on status
|
|
1241
|
+
let resetValue = '';
|
|
1242
|
+
const status = originalTestCase.status.toLowerCase();
|
|
1243
|
+
|
|
1244
|
+
if (status === 'modify') {
|
|
1245
|
+
resetValue = originalTestCase.title + '\\n\\nOriginal: ' + (originalTestCase.originalCase || 'Not specified') + '\\nChanged to: ' + (originalTestCase.description || 'Not specified');
|
|
1246
|
+
} else {
|
|
1247
|
+
resetValue = originalTestCase.title;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1242
1250
|
textarea.value = resetValue;
|
|
1243
1251
|
});
|
|
1244
1252
|
updateStats();
|
|
@@ -1285,7 +1293,7 @@ tool(
|
|
|
1285
1293
|
body: JSON.stringify({
|
|
1286
1294
|
sessionId: '${sessionId}',
|
|
1287
1295
|
testCases: updatedTestCases
|
|
1288
|
-
|
|
1296
|
+
})
|
|
1289
1297
|
})
|
|
1290
1298
|
.then(response => response.json())
|
|
1291
1299
|
.then(data => {
|
|
@@ -1313,18 +1321,16 @@ tool(
|
|
|
1313
1321
|
if (e.target.tagName === 'TEXTAREA') {
|
|
1314
1322
|
const index = parseInt(e.target.getAttribute('data-index'));
|
|
1315
1323
|
if (!isNaN(index) && testCases[index]) {
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
if (stepsIndex !== -1) {
|
|
1319
|
-
testCases[index].description = e.target.value.substring(stepsIndex + 9);
|
|
1320
|
-
}
|
|
1324
|
+
// Update the title with the textarea content
|
|
1325
|
+
testCases[index].title = e.target.value.trim();
|
|
1321
1326
|
}
|
|
1322
1327
|
}
|
|
1323
1328
|
});
|
|
1324
1329
|
</script>
|
|
1325
1330
|
</body>
|
|
1326
1331
|
</html>
|
|
1327
|
-
|
|
1332
|
+
`;
|
|
1333
|
+
res.send(htmlContent);
|
|
1328
1334
|
} catch (error) {
|
|
1329
1335
|
console.error('Error rendering page:', error);
|
|
1330
1336
|
res.status(500).send('Error rendering page');
|
|
@@ -1355,7 +1361,9 @@ tool(
|
|
|
1355
1361
|
|
|
1356
1362
|
// Close server after approval
|
|
1357
1363
|
setTimeout(() => {
|
|
1358
|
-
server.
|
|
1364
|
+
if (server && server.listening) {
|
|
1365
|
+
server.close();
|
|
1366
|
+
}
|
|
1359
1367
|
}, 3000);
|
|
1360
1368
|
} catch (error) {
|
|
1361
1369
|
console.error('Approval error:', error);
|
|
@@ -1374,32 +1382,22 @@ tool(
|
|
|
1374
1382
|
res.status(404).json({ error: 'Not found' });
|
|
1375
1383
|
});
|
|
1376
1384
|
|
|
1377
|
-
// Start server with
|
|
1378
|
-
const server =
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1385
|
+
// Start server with promise-based approach
|
|
1386
|
+
const server = await new Promise((resolve, reject) => {
|
|
1387
|
+
const srv = app.listen(port, (err) => {
|
|
1388
|
+
if (err) {
|
|
1389
|
+
reject(err);
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
console.log(`✅ Test case review session started. Session ID: ${sessionId}.`);
|
|
1393
|
+
console.log(`Server running at http://localhost:${port}`);
|
|
1394
|
+
console.log(`Browser should open automatically.`);
|
|
1395
|
+
resolve(srv);
|
|
1396
|
+
});
|
|
1386
1397
|
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
// Try a different port
|
|
1391
|
-
const newPort = port + Math.floor(Math.random() * 100);
|
|
1392
|
-
const newServer = app.listen(newPort, () => {
|
|
1393
|
-
console.log(`Test case review server running at http://localhost:${newPort}`);
|
|
1394
|
-
try {
|
|
1395
|
-
openBrowser(`http://localhost:${newPort}`).catch(err => console.error('Failed to open browser:', err));
|
|
1396
|
-
} catch (e) {
|
|
1397
|
-
console.error('Failed to open browser automatically:', e.message);
|
|
1398
|
-
}
|
|
1399
|
-
});
|
|
1400
|
-
return newServer;
|
|
1401
|
-
}
|
|
1402
|
-
throw error;
|
|
1398
|
+
srv.on('error', (error) => {
|
|
1399
|
+
reject(error);
|
|
1400
|
+
});
|
|
1403
1401
|
});
|
|
1404
1402
|
|
|
1405
1403
|
// Open browser with proper error handling
|
|
@@ -1417,7 +1415,8 @@ tool(
|
|
|
1417
1415
|
global.approvalSessions[sessionId] = {
|
|
1418
1416
|
status: 'pending',
|
|
1419
1417
|
testCases: processedTestCases,
|
|
1420
|
-
timestamp: Date.now()
|
|
1418
|
+
timestamp: Date.now(),
|
|
1419
|
+
server: server
|
|
1421
1420
|
};
|
|
1422
1421
|
|
|
1423
1422
|
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.'}`;
|