@nbakka/mcp-appium 2.0.55 → 2.0.57

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.
Files changed (2) hide show
  1. package/lib/server.js +52 -223
  2. package/package.json +1 -1
package/lib/server.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createMcpServer = void 0;
4
4
  const fs = require('fs').promises;
5
5
  const os = require('os');
6
+ const fsSync = require('fs');
6
7
  const path = require('path');
7
8
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
8
9
  const zod_1 = require("zod");
@@ -565,6 +566,25 @@ tool(
565
566
  const pdfResponse = await axios.get(pdfUrl, { responseType: "arraybuffer" });
566
567
 
567
568
  const exportPath = path.join(os.homedir(), "Desktop", "figma");
569
+
570
+ // Clear the folder before creating new PDF
571
+ try {
572
+ // Check if folder exists
573
+ await fs.access(exportPath);
574
+ // If folder exists, remove all contents
575
+ const files = await fs.readdir(exportPath);
576
+ await Promise.all(
577
+ files.map(file => fs.unlink(path.join(exportPath, file)))
578
+ );
579
+ console.log("Cleared existing files in figma folder");
580
+ } catch (err) {
581
+ // Folder doesn't exist or is empty, no need to clear
582
+ if (err.code !== 'ENOENT') {
583
+ console.log("⚠️ Warning: Could not clear folder:", err.message);
584
+ }
585
+ }
586
+
587
+ // Ensure directory exists
568
588
  await fs.mkdir(exportPath, { recursive: true });
569
589
 
570
590
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, -5);
@@ -578,251 +598,60 @@ tool(
578
598
  }
579
599
  );
580
600
 
581
-
582
- // ----------------------
583
- // FIXED TOOL: Generate Manual Test Cases from PDF + JIRA
584
- // ----------------------
585
601
  tool(
586
602
  "upload_pdf_to_openai",
587
603
  "Generate manual test cases by analyzing PDF design with JIRA requirements",
588
604
  {
589
605
  jiraSummary: zod_1.z.string().describe("Jira issue summary"),
590
- jiraDescription: zod_1.z.string().describe("Jira issue description"),
591
- pdfPath: zod_1.z.string().describe("Path to PDF file from tool 1")
606
+ jiraDescription: zod_1.z.string().describe("Jira issue description")
592
607
  },
593
- async ({ jiraSummary, jiraDescription, pdfPath }) => {
608
+ async ({ jiraSummary, jiraDescription }) => {
594
609
  try {
595
- // Load OpenAI API key
610
+ // Load OpenAI API key from Desktop/openai.json
596
611
  const openaiConfigPath = path.join(os.homedir(), "Desktop", "openai.json");
597
612
  const configContent = await fs.readFile(openaiConfigPath, "utf-8");
598
- const { apiKey: openaiKey } = JSON.parse(configContent);
599
-
600
- if (!openaiKey) throw new Error("OpenAI API key missing in openai.json");
613
+ const { apiKey } = JSON.parse(configContent);
601
614
 
602
- const client = new OpenAI({ apiKey: openaiKey });
603
-
604
- // Validate file exists
605
- try {
606
- await fs.access(pdfPath);
607
- } catch (error) {
608
- throw new Error(`PDF file not found at path: ${pdfPath}`);
609
- }
615
+ const figmaDir = path.join(os.homedir(), "Desktop", "figma");
616
+ const files = await fs.readdir(figmaDir);
617
+ const pdfFiles = files.filter(file => file.toLowerCase().endsWith('.pdf'));
610
618
 
611
- const stats = await fs.stat(pdfPath);
612
- const fileName = path.basename(pdfPath);
619
+ if (pdfFiles.length === 0) throw new Error("No PDF files found in figma folder");
613
620
 
614
- console.log(`📄 Processing ${fileName} for test case generation...`);
621
+ const latestPdf = pdfFiles.sort((a, b) => b.localeCompare(a))[0];
622
+ const pdfPath = path.join(figmaDir, latestPdf);
615
623
 
616
- // Use the require() fs for createReadStream (CommonJS style)
617
- const fsSync = require('fs');
624
+ const client = new OpenAI({ apiKey });
618
625
 
619
- // Upload PDF to OpenAI
620
- const fileUpload = await client.files.create({
621
- file: fsSync.createReadStream(pdfPath), // Use fsSync.createReadStream
622
- purpose: "assistants"
626
+ const file = await client.files.create({
627
+ file: fsSync.createReadStream(pdfPath),
628
+ purpose: "user_data",
623
629
  });
624
630
 
625
- console.log(`✅ PDF uploaded: ${fileUpload.id}`);
626
-
627
- // Create specialized assistant for test case generation
628
- const assistant = await client.beta.assistants.create({
629
- name: "Manual Test Case Generator",
630
- instructions: `You are an expert QA engineer specializing in creating comprehensive manual test cases.
631
- You analyze design documents and requirements to generate detailed, actionable test cases that cover:
632
- - Functional testing scenarios
633
- - UI/UX validation tests
634
- - Edge cases and negative scenarios
635
- - Cross-browser/device compatibility tests
636
- - Accessibility testing scenarios`,
631
+ const completion = await client.chat.completions.create({
637
632
  model: "gpt-4o",
638
- tools: [{ type: "file_search" }],
639
- tool_resources: {
640
- file_search: {
641
- vector_stores: [{
642
- file_ids: [fileUpload.id]
643
- }]
644
- }
645
- }
646
- });
647
-
648
- // Create comprehensive test case generation prompt
649
- const testCasePrompt = `
650
- **JIRA REQUIREMENTS:**
651
- **Summary:** ${jiraSummary}
652
- **Description:** ${jiraDescription}
653
-
654
- **TASK:** Generate comprehensive manual test cases based on the attached PDF design and above JIRA requirements.
655
-
656
- **OUTPUT FORMAT REQUIRED:**
657
- Please structure your response as follows:
658
-
659
- ## MANUAL TEST CASES
660
-
661
- ### 1. FUNCTIONAL TEST CASES
662
- **TC-F-001: [Test Case Title]**
663
- - **Objective:** What this test validates
664
- - **Preconditions:** Setup required before testing
665
- - **Test Steps:**
666
- 1. Step 1 action
667
- 2. Step 2 action
668
- 3. Step 3 action
669
- - **Expected Result:** What should happen
670
- - **Priority:** High/Medium/Low
671
-
672
- ### 2. UI/UX TEST CASES
673
- **TC-UI-001: [Test Case Title]**
674
- [Same format as above]
675
-
676
- ### 3. EDGE CASE & NEGATIVE TEST CASES
677
- **TC-E-001: [Test Case Title]**
678
- [Same format as above]
679
-
680
- ### 4. COMPATIBILITY TEST CASES
681
- **TC-C-001: [Test Case Title]**
682
- [Same format as above]
683
-
684
- ### 5. ACCESSIBILITY TEST CASES
685
- **TC-A-001: [Test Case Title]**
686
- [Same format as above]
687
-
688
- **IMPORTANT GUIDELINES:**
689
- 1. Create 15-20 test cases total covering all aspects
690
- 2. Reference specific UI elements from the PDF design
691
- 3. Include both positive and negative test scenarios
692
- 4. Consider different user roles if applicable
693
- 5. Include performance and usability aspects
694
- 6. Make test steps specific and actionable
695
- 7. Consider mobile/responsive design if shown in PDF
696
- 8. Include data validation test cases
697
- 9. Cover error handling scenarios
698
- 10. Include integration testing if multiple components interact
699
-
700
- Generate detailed test cases now based on the PDF design and JIRA requirements.
701
- `;
702
-
703
- // Create thread and run analysis
704
- const thread = await client.beta.threads.create({
705
- messages: [
706
- {
707
- role: "user",
708
- content: testCasePrompt
709
- }
710
- ]
711
- });
712
-
713
- console.log(`🔄 Generating test cases...`);
714
-
715
- const run = await client.beta.threads.runs.create(thread.id, {
716
- assistant_id: assistant.id,
633
+ messages: [{
634
+ role: "user",
635
+ content: [{
636
+ type: "file",
637
+ file: { file_id: file.id }
638
+ }, {
639
+ type: "text",
640
+ text: `Generate test cases based on this PDF design and JIRA requirements:
641
+
642
+ Summary: ${jiraSummary}
643
+ Description: ${jiraDescription}`
644
+ }]
645
+ }]
717
646
  });
718
647
 
719
- // Wait for completion with progress tracking
720
- let runStatus = await client.beta.threads.runs.retrieve(thread.id, run.id);
721
- let attempts = 0;
722
- const maxAttempts = 60;
723
-
724
- while (runStatus.status !== 'completed' && attempts < maxAttempts) {
725
- if (runStatus.status === 'failed') {
726
- throw new Error(`Test case generation failed: ${runStatus.last_error?.message}`);
727
- }
648
+ const testCases = completion.choices[0].message.content;
728
649
 
729
- if (attempts % 5 === 0) {
730
- console.log(`⏳ Still generating... (${attempts}s elapsed)`);
731
- }
732
-
733
- await new Promise(resolve => setTimeout(resolve, 1000));
734
- runStatus = await client.beta.threads.runs.retrieve(thread.id, run.id);
735
- attempts++;
736
- }
737
-
738
- if (runStatus.status !== 'completed') {
739
- throw new Error('Test case generation timed out after 60 seconds');
740
- }
741
-
742
- // Get the generated test cases
743
- const messages = await client.beta.threads.messages.list(thread.id);
744
- const testCases = messages.data
745
- .filter(msg => msg.role === 'assistant')
746
- .map(msg => msg.content[0]?.text?.value)
747
- .join('\n\n');
748
-
749
- // Parse test case count
750
- const testCaseCount = (testCases.match(/\*\*TC-/g) || []).length;
751
-
752
- // Clean up assistant
753
- try {
754
- await client.beta.assistants.del(assistant.id);
755
- } catch (cleanupError) {
756
- console.warn('⚠️ Assistant cleanup failed:', cleanupError.message);
757
- }
758
-
759
- // Save test cases to file
760
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
761
- const outputFileName = `test_cases_${timestamp.substring(0, 19)}.md`;
762
- const outputPath = path.join(path.dirname(pdfPath), outputFileName);
763
-
764
- const fileContent = `# Manual Test Cases
765
- Generated on: ${new Date().toLocaleString()}
766
-
767
- ## Source Information
768
- - **JIRA Summary:** ${jiraSummary}
769
- - **JIRA Description:** ${jiraDescription}
770
- - **Design File:** ${fileName}
771
- - **Total Test Cases:** ${testCaseCount}
772
-
773
- ---
774
-
775
- ${testCases}
776
-
777
- ---
778
-
779
- ## Test Execution Notes
780
- - Execute test cases in the order listed
781
- - Log defects with screenshots when issues found
782
- - Update test results in your test management tool
783
- - Verify on multiple browsers/devices for compatibility tests
784
- `;
785
-
786
- await fs.writeFile(outputPath, fileContent, 'utf-8');
787
-
788
- console.log(`✅ Test cases generated and saved!`);
789
-
790
- // CRITICAL: Return only a STRING, not an object
791
- // The MCP wrapper will handle the proper formatting
792
- return `✅ Successfully generated ${testCaseCount} manual test cases!
793
-
794
- 📁 Test cases saved to: ${outputFileName}
795
- 📂 Full path: ${outputPath}
796
-
797
- 📊 Generation Summary:
798
- - Total Test Cases: ${testCaseCount}
799
- - Categories: Functional, UI/UX, Edge Cases, Compatibility, Accessibility
800
- - Source File: ${fileName} (${(stats.size / 1024).toFixed(2)} KB)
801
- - Processing Time: ${attempts} seconds
802
- - Model Used: gpt-4o
803
-
804
- 🎯 Test Case Preview:
805
- ${testCases.substring(0, 1000)}...
806
-
807
- [Complete test cases saved to markdown file for easy reference and sharing]`;
650
+ await client.files.del(file.id);
808
651
 
652
+ return testCases;
809
653
  } catch (err) {
810
- console.error("❌ Test Case Generation Error:", err);
811
-
812
- // Return only error string, not object
813
- let errorMessage = `❌ Failed to generate test cases: ${err.message}`;
814
-
815
- if (err.message?.includes('quota')) {
816
- errorMessage += "\n\n💡 Troubleshooting: OpenAI quota exceeded. Check your billing at platform.openai.com";
817
- } else if (err.message?.includes('timeout')) {
818
- errorMessage += "\n\n💡 Troubleshooting: Generation took too long. Try with a smaller PDF file.";
819
- } else if (err.message?.includes('file')) {
820
- errorMessage += "\n\n💡 Troubleshooting: PDF processing error. Check file format and accessibility.";
821
- } else if (err.message?.includes('API key')) {
822
- errorMessage += "\n\n💡 Troubleshooting: Invalid OpenAI API key. Check ~/Desktop/openai.json file.";
823
- }
824
-
825
- return errorMessage;
654
+ return `Error: ${err.message}`;
826
655
  }
827
656
  }
828
657
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.55",
3
+ "version": "2.0.57",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"