@nbakka/mcp-appium 2.0.98 → 2.0.99

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.
@@ -497,28 +497,28 @@
497
497
  return;
498
498
  }
499
499
 
500
- container.innerHTML = cases.map((tc, index) => `
501
- <div class="test-case" id="new-${index}">
500
+ container.innerHTML = cases.map((tc) => `
501
+ <div class="test-case" id="new-${tc.id}">
502
502
  <div class="test-case-header">
503
503
  <span class="test-case-id">${tc.id}</span>
504
504
  <div class="test-case-actions">
505
- <button class="btn btn-edit" onclick="startEdit('new', ${index})">✏️ Edit</button>
505
+ <button class="btn btn-edit" onclick="startEdit('new', '${tc.id}')">✏️ Edit</button>
506
506
  <button class="btn btn-delete" onclick="deleteTestCase('new', '${tc.id}')">🗑️ Delete</button>
507
507
  </div>
508
508
  </div>
509
509
  <div class="test-case-content">
510
- <div id="new-${index}-display">
510
+ <div id="new-${tc.id}-display">
511
511
  <div class="description-text">${escapeHtml(tc.description)}</div>
512
512
  </div>
513
- <div id="new-${index}-edit" style="display: none;">
513
+ <div id="new-${tc.id}-edit" style="display: none;">
514
514
  <div class="edit-form">
515
515
  <div class="form-group">
516
516
  <label class="form-label">Description:</label>
517
- <textarea class="form-control" id="new-${index}-desc">${escapeHtml(tc.description)}</textarea>
517
+ <textarea class="form-control" id="new-${tc.id}-desc">${escapeHtml(tc.description)}</textarea>
518
518
  </div>
519
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>
520
+ <button class="btn btn-save" onclick="saveEdit('new', '${tc.id}')">💾 Save</button>
521
+ <button class="btn btn-cancel" onclick="cancelEdit('new', '${tc.id}')">❌ Cancel</button>
522
522
  </div>
523
523
  </div>
524
524
  </div>
@@ -536,17 +536,17 @@
536
536
  return;
537
537
  }
538
538
 
539
- container.innerHTML = cases.map((tc, index) => `
540
- <div class="test-case" id="modify-${index}">
539
+ container.innerHTML = cases.map((tc) => `
540
+ <div class="test-case" id="modify-${tc.id}">
541
541
  <div class="test-case-header">
542
542
  <span class="test-case-id">${tc.id}</span>
543
543
  <div class="test-case-actions">
544
- <button class="btn btn-edit" onclick="startEdit('modify', ${index})">✏️ Edit</button>
544
+ <button class="btn btn-edit" onclick="startEdit('modify', '${tc.id}')">✏️ Edit</button>
545
545
  <button class="btn btn-delete" onclick="deleteTestCase('modify', '${tc.id}')">🗑️ Delete</button>
546
546
  </div>
547
547
  </div>
548
548
  <div class="test-case-content">
549
- <div id="modify-${index}-display">
549
+ <div id="modify-${tc.id}-display">
550
550
  <div class="original-text">
551
551
  <strong>Original:</strong><br>${escapeHtml(tc.original)}
552
552
  </div>
@@ -554,7 +554,7 @@
554
554
  <strong>Modified:</strong><br>${escapeHtml(tc.modified)}
555
555
  </div>
556
556
  </div>
557
- <div id="modify-${index}-edit" style="display: none;">
557
+ <div id="modify-${tc.id}-edit" style="display: none;">
558
558
  <div class="edit-form">
559
559
  <div class="form-group">
560
560
  <label class="form-label">Original (Read-only):</label>
@@ -562,11 +562,11 @@
562
562
  </div>
563
563
  <div class="form-group">
564
564
  <label class="form-label">Modified:</label>
565
- <textarea class="form-control" id="modify-${index}-mod">${escapeHtml(tc.modified)}</textarea>
565
+ <textarea class="form-control" id="modify-${tc.id}-mod">${escapeHtml(tc.modified)}</textarea>
566
566
  </div>
567
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>
568
+ <button class="btn btn-save" onclick="saveEdit('modify', '${tc.id}')">💾 Save</button>
569
+ <button class="btn btn-cancel" onclick="cancelEdit('modify', '${tc.id}')">❌ Cancel</button>
570
570
  </div>
571
571
  </div>
572
572
  </div>
@@ -584,8 +584,8 @@
584
584
  return;
585
585
  }
586
586
 
587
- container.innerHTML = cases.map((tc, index) => `
588
- <div class="test-case" id="remove-${index}">
587
+ container.innerHTML = cases.map((tc) => `
588
+ <div class="test-case" id="remove-${tc.id}">
589
589
  <div class="test-case-header">
590
590
  <span class="test-case-id">${tc.id}</span>
591
591
  <div class="test-case-actions">
@@ -603,50 +603,63 @@
603
603
  }
604
604
 
605
605
  // Edit functions
606
- function startEdit(type, index) {
607
- const editKey = `${type}-${index}`;
606
+ function startEdit(type, id) {
607
+ const editKey = `${type}-${id}`;
608
608
  if (editingStates.has(editKey)) return;
609
609
 
610
610
  editingStates.add(editKey);
611
- document.getElementById(`${type}-${index}-display`).style.display = 'none';
612
- document.getElementById(`${type}-${index}-edit`).style.display = 'block';
611
+ document.getElementById(`${type}-${id}-display`).style.display = 'none';
612
+ document.getElementById(`${type}-${id}-edit`).style.display = 'block';
613
613
  }
614
614
 
615
- function cancelEdit(type, index) {
616
- const editKey = `${type}-${index}`;
615
+ function cancelEdit(type, id) {
616
+ const editKey = `${type}-${id}`;
617
617
  editingStates.delete(editKey);
618
- document.getElementById(`${type}-${index}-display`).style.display = 'block';
619
- document.getElementById(`${type}-${index}-edit`).style.display = 'none';
618
+ document.getElementById(`${type}-${id}-display`).style.display = 'block';
619
+ document.getElementById(`${type}-${id}-edit`).style.display = 'none';
620
620
 
621
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;
622
+ const testCase = findTestCase(type, id);
623
+ if (testCase) {
624
+ if (type === 'new') {
625
+ document.getElementById(`new-${id}-desc`).value = testCase.description;
626
+ } else if (type === 'modify') {
627
+ document.getElementById(`modify-${id}-mod`).value = testCase.modified;
628
+ }
626
629
  }
627
630
  }
628
631
 
629
- async function saveEdit(type, index) {
632
+ function findTestCase(type, id) {
633
+ return testCases[type]?.find(tc => tc.id === id);
634
+ }
635
+
636
+ async function saveEdit(type, id) {
630
637
  try {
631
- const editKey = `${type}-${index}`;
638
+ const editKey = `${type}-${id}`;
632
639
  let updatedData = {};
633
640
 
641
+ const testCase = findTestCase(type, id);
642
+ if (!testCase) {
643
+ showStatus('Test case not found', 'error');
644
+ return;
645
+ }
646
+
634
647
  if (type === 'new') {
635
- const newDesc = document.getElementById(`new-${index}-desc`).value.trim();
648
+ const newDesc = document.getElementById(`new-${id}-desc`).value.trim();
636
649
  if (!newDesc) {
637
650
  showStatus('Description cannot be empty', 'error');
638
651
  return;
639
652
  }
640
653
  updatedData = { description: newDesc };
641
- testCases.new[index].description = newDesc;
654
+ testCase.description = newDesc;
642
655
  } else if (type === 'modify') {
643
- const newMod = document.getElementById(`modify-${index}-mod`).value.trim();
656
+ const newMod = document.getElementById(`modify-${id}-mod`).value.trim();
644
657
  if (!newMod) {
645
658
  showStatus('Modified description cannot be empty', 'error');
646
659
  return;
647
660
  }
648
661
  updatedData = { modified: newMod };
649
- testCases.modify[index].modified = newMod;
662
+ testCase.modified = newMod;
650
663
  }
651
664
 
652
665
  // Send update to server
@@ -654,14 +667,14 @@
654
667
  method: 'POST',
655
668
  body: JSON.stringify({
656
669
  type,
657
- id: testCases[type][index].id,
670
+ id: id,
658
671
  data: updatedData
659
672
  })
660
673
  });
661
674
 
662
675
  editingStates.delete(editKey);
663
- document.getElementById(`${type}-${index}-display`).style.display = 'block';
664
- document.getElementById(`${type}-${index}-edit`).style.display = 'none';
676
+ document.getElementById(`${type}-${id}-display`).style.display = 'block';
677
+ document.getElementById(`${type}-${id}-edit`).style.display = 'none';
665
678
 
666
679
  // Re-render the specific section
667
680
  if (type === 'new') renderNewCases();
@@ -687,9 +700,17 @@
687
700
 
688
701
  if (result.testCases) {
689
702
  testCases = result.testCases;
703
+
704
+ // Ensure all arrays exist after update
705
+ if (!testCases.new) testCases.new = [];
706
+ if (!testCases.modify) testCases.modify = [];
707
+ if (!testCases.remove) testCases.remove = [];
708
+
690
709
  renderAllSections();
691
710
  updateCounts();
692
711
  showStatus('Test case deleted successfully!', 'success');
712
+ } else {
713
+ showStatus('Unexpected response from server', 'error');
693
714
  }
694
715
  } catch (error) {
695
716
  showStatus(`Error deleting test case: ${error.message}`, 'error');
package/lib/server.js CHANGED
@@ -1014,7 +1014,7 @@ tool(
1014
1014
  const now = Date.now();
1015
1015
  const elapsed = Math.floor((now - session.startTime) / 1000);
1016
1016
 
1017
- // Check if already approved (short-circuit without waiting)
1017
+ // Check if already approved BEFORE waiting
1018
1018
  if (session.status === 'approved') {
1019
1019
  const approvedTestCases = session.finalTestCases || session.testCases;
1020
1020
  session.server?.close();
@@ -1022,10 +1022,22 @@ tool(
1022
1022
  return `✅ Test cases approved. Elapsed: ${elapsed}s\n${JSON.stringify(approvedTestCases, null, 2)}`;
1023
1023
  }
1024
1024
 
1025
- // Wait 20 seconds before checking status
1025
+ if (session.status === 'cancelled') {
1026
+ session.server?.close();
1027
+ approvalSessions.delete(sessionId);
1028
+ return `❌ Review cancelled. Elapsed: ${elapsed}s`;
1029
+ }
1030
+
1031
+ if (session.status === 'timeout' || elapsed > 300) {
1032
+ session.server?.close();
1033
+ approvalSessions.delete(sessionId);
1034
+ return `⏰ Review session timed out. Elapsed: ${elapsed}s`;
1035
+ }
1036
+
1037
+ // Wait 20 seconds before checking status again
1026
1038
  await new Promise(r => setTimeout(r, 20000));
1027
1039
 
1028
- // Re-fetch session after wait
1040
+ // Re-fetch session after wait to check for status changes
1029
1041
  const refreshed = approvalSessions.get(sessionId);
1030
1042
  if (!refreshed) return `❌ Session expired during wait`;
1031
1043
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.98",
3
+ "version": "2.0.99",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"