@nbakka/mcp-appium 2.0.52 → 2.0.54
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 +300 -30
- package/package.json +1 -1
package/lib/server.js
CHANGED
|
@@ -476,20 +476,33 @@ const createMcpServer = () => {
|
|
|
476
476
|
}
|
|
477
477
|
|
|
478
478
|
// ----------------------
|
|
479
|
-
// Helper: Extract File ID
|
|
479
|
+
// Helper: Extract File ID & Node ID
|
|
480
480
|
// ----------------------
|
|
481
|
-
function
|
|
481
|
+
function extractFileAndNodeId(url) {
|
|
482
482
|
const patterns = [
|
|
483
483
|
/figma\.com\/file\/([a-zA-Z0-9]+)/,
|
|
484
484
|
/figma\.com\/design\/([a-zA-Z0-9]+)/,
|
|
485
485
|
/figma\.com\/proto\/([a-zA-Z0-9]+)/
|
|
486
486
|
];
|
|
487
487
|
|
|
488
|
+
let fileId = null;
|
|
488
489
|
for (const pattern of patterns) {
|
|
489
490
|
const match = url.match(pattern);
|
|
490
|
-
if (match)
|
|
491
|
+
if (match) {
|
|
492
|
+
fileId = match[1];
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Extract node-id if present
|
|
498
|
+
const nodeMatch = url.match(/[?&]node-id=([^&]+)/);
|
|
499
|
+
let nodeId = null;
|
|
500
|
+
if (nodeMatch) {
|
|
501
|
+
// Replace dash with colon (Figma expects 13:5951 instead of 13-5951)
|
|
502
|
+
nodeId = decodeURIComponent(nodeMatch[1]).replace(/-/g, ":");
|
|
491
503
|
}
|
|
492
|
-
|
|
504
|
+
|
|
505
|
+
return { fileId, nodeId };
|
|
493
506
|
}
|
|
494
507
|
|
|
495
508
|
// ----------------------
|
|
@@ -510,31 +523,38 @@ tool(
|
|
|
510
523
|
|
|
511
524
|
if (!figmaToken) throw new Error("Figma API token missing in figma.json");
|
|
512
525
|
|
|
513
|
-
// Extract
|
|
514
|
-
const fileId =
|
|
526
|
+
// Extract fileId and nodeId from URL
|
|
527
|
+
const { fileId, nodeId } = extractFileAndNodeId(figmaUrl);
|
|
515
528
|
if (!fileId) throw new Error("Invalid Figma URL - cannot extract fileId");
|
|
516
529
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
530
|
+
let idsToExport = [];
|
|
531
|
+
|
|
532
|
+
if (nodeId) {
|
|
533
|
+
// Use node-id directly from URL
|
|
534
|
+
idsToExport = [nodeId];
|
|
535
|
+
} else {
|
|
536
|
+
// Fallback: scan file to collect all top-level frames
|
|
537
|
+
const fileResponse = await axios.get(
|
|
538
|
+
`https://api.figma.com/v1/files/${fileId}`,
|
|
539
|
+
{ headers: { "X-Figma-Token": figmaToken } }
|
|
540
|
+
);
|
|
522
541
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
542
|
+
fileResponse.data.document.children?.forEach(page => {
|
|
543
|
+
page.children?.forEach(child => {
|
|
544
|
+
if (child.type === "FRAME") idsToExport.push(child.id);
|
|
545
|
+
});
|
|
527
546
|
});
|
|
528
|
-
});
|
|
529
547
|
|
|
530
|
-
|
|
548
|
+
if (idsToExport.length === 0)
|
|
549
|
+
throw new Error("No frames found in Figma file");
|
|
550
|
+
}
|
|
531
551
|
|
|
532
552
|
// Request PDF export
|
|
533
553
|
const exportResponse = await axios.get(
|
|
534
554
|
`https://api.figma.com/v1/images/${fileId}`,
|
|
535
555
|
{
|
|
536
556
|
headers: { "X-Figma-Token": figmaToken },
|
|
537
|
-
params: { ids:
|
|
557
|
+
params: { ids: idsToExport.join(","), format: "pdf" }
|
|
538
558
|
}
|
|
539
559
|
);
|
|
540
560
|
|
|
@@ -558,12 +578,13 @@ tool(
|
|
|
558
578
|
}
|
|
559
579
|
);
|
|
560
580
|
|
|
581
|
+
|
|
561
582
|
// ----------------------
|
|
562
|
-
// TOOL
|
|
583
|
+
// TOOL: Generate Manual Test Cases from PDF + JIRA
|
|
563
584
|
// ----------------------
|
|
564
585
|
tool(
|
|
565
586
|
"upload_pdf_to_openai",
|
|
566
|
-
"
|
|
587
|
+
"Generate manual test cases by analyzing PDF design with JIRA requirements",
|
|
567
588
|
{
|
|
568
589
|
jiraSummary: zod_1.z.string().describe("Jira issue summary"),
|
|
569
590
|
jiraDescription: zod_1.z.string().describe("Jira issue description"),
|
|
@@ -580,28 +601,277 @@ tool(
|
|
|
580
601
|
|
|
581
602
|
const client = new OpenAI({ apiKey: openaiKey });
|
|
582
603
|
|
|
583
|
-
//
|
|
584
|
-
|
|
604
|
+
// Validate file exists
|
|
605
|
+
if (!await fs.access(pdfPath).then(() => true).catch(() => false)) {
|
|
606
|
+
throw new Error(`PDF file not found at path: ${pdfPath}`);
|
|
607
|
+
}
|
|
585
608
|
|
|
586
|
-
|
|
609
|
+
const stats = await fs.stat(pdfPath);
|
|
610
|
+
const fileName = path.basename(pdfPath);
|
|
611
|
+
|
|
612
|
+
console.log(`📄 Processing ${fileName} for test case generation...`);
|
|
613
|
+
|
|
614
|
+
// Upload PDF to OpenAI
|
|
587
615
|
const fileUpload = await client.files.create({
|
|
588
|
-
file:
|
|
616
|
+
file: fs.createReadStream(pdfPath),
|
|
589
617
|
purpose: "assistants"
|
|
590
618
|
});
|
|
591
619
|
|
|
592
|
-
|
|
593
|
-
|
|
620
|
+
console.log(`✅ PDF uploaded: ${fileUpload.id}`);
|
|
621
|
+
|
|
622
|
+
// Create specialized assistant for test case generation
|
|
623
|
+
const assistant = await client.beta.assistants.create({
|
|
624
|
+
name: "Manual Test Case Generator",
|
|
625
|
+
instructions: `You are an expert QA engineer specializing in creating comprehensive manual test cases.
|
|
626
|
+
You analyze design documents and requirements to generate detailed, actionable test cases that cover:
|
|
627
|
+
- Functional testing scenarios
|
|
628
|
+
- UI/UX validation tests
|
|
629
|
+
- Edge cases and negative scenarios
|
|
630
|
+
- Cross-browser/device compatibility tests
|
|
631
|
+
- Accessibility testing scenarios`,
|
|
632
|
+
model: "gpt-4o", // Best model for complex reasoning
|
|
633
|
+
tools: [{ type: "file_search" }],
|
|
634
|
+
tool_resources: {
|
|
635
|
+
file_search: {
|
|
636
|
+
vector_stores: [{
|
|
637
|
+
file_ids: [fileUpload.id]
|
|
638
|
+
}]
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// Create comprehensive test case generation prompt
|
|
644
|
+
const testCasePrompt = `
|
|
645
|
+
**JIRA REQUIREMENTS:**
|
|
646
|
+
**Summary:** ${jiraSummary}
|
|
647
|
+
**Description:** ${jiraDescription}
|
|
648
|
+
|
|
649
|
+
**TASK:** Generate comprehensive manual test cases based on the attached PDF design and above JIRA requirements.
|
|
650
|
+
|
|
651
|
+
**OUTPUT FORMAT REQUIRED:**
|
|
652
|
+
Please structure your response as follows:
|
|
653
|
+
|
|
654
|
+
## MANUAL TEST CASES
|
|
655
|
+
|
|
656
|
+
### 1. FUNCTIONAL TEST CASES
|
|
657
|
+
**TC-F-001: [Test Case Title]**
|
|
658
|
+
- **Objective:** What this test validates
|
|
659
|
+
- **Preconditions:** Setup required before testing
|
|
660
|
+
- **Test Steps:**
|
|
661
|
+
1. Step 1 action
|
|
662
|
+
2. Step 2 action
|
|
663
|
+
3. Step 3 action
|
|
664
|
+
- **Expected Result:** What should happen
|
|
665
|
+
- **Priority:** High/Medium/Low
|
|
666
|
+
|
|
667
|
+
### 2. UI/UX TEST CASES
|
|
668
|
+
**TC-UI-001: [Test Case Title]**
|
|
669
|
+
[Same format as above]
|
|
670
|
+
|
|
671
|
+
### 3. EDGE CASE & NEGATIVE TEST CASES
|
|
672
|
+
**TC-E-001: [Test Case Title]**
|
|
673
|
+
[Same format as above]
|
|
674
|
+
|
|
675
|
+
### 4. COMPATIBILITY TEST CASES
|
|
676
|
+
**TC-C-001: [Test Case Title]**
|
|
677
|
+
[Same format as above]
|
|
678
|
+
|
|
679
|
+
### 5. ACCESSIBILITY TEST CASES
|
|
680
|
+
**TC-A-001: [Test Case Title]**
|
|
681
|
+
[Same format as above]
|
|
682
|
+
|
|
683
|
+
**IMPORTANT GUIDELINES:**
|
|
684
|
+
1. Create 15-25 test cases total covering all aspects
|
|
685
|
+
2. Reference specific UI elements from the PDF design
|
|
686
|
+
3. Include both positive and negative test scenarios
|
|
687
|
+
4. Consider different user roles if applicable
|
|
688
|
+
5. Include performance and usability aspects
|
|
689
|
+
6. Make test steps specific and actionable
|
|
690
|
+
7. Consider mobile/responsive design if shown in PDF
|
|
691
|
+
8. Include data validation test cases
|
|
692
|
+
9. Cover error handling scenarios
|
|
693
|
+
10. Include integration testing if multiple components interact
|
|
694
|
+
|
|
695
|
+
Generate detailed test cases now based on the PDF design and JIRA requirements.
|
|
696
|
+
`;
|
|
697
|
+
|
|
698
|
+
// Create thread and run analysis
|
|
699
|
+
const thread = await client.beta.threads.create({
|
|
700
|
+
messages: [
|
|
701
|
+
{
|
|
702
|
+
role: "user",
|
|
703
|
+
content: testCasePrompt
|
|
704
|
+
}
|
|
705
|
+
]
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
console.log(`🔄 Generating test cases...`);
|
|
709
|
+
|
|
710
|
+
const run = await client.beta.threads.runs.create(thread.id, {
|
|
711
|
+
assistant_id: assistant.id,
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
// Wait for completion with progress tracking
|
|
715
|
+
let runStatus = await client.beta.threads.runs.retrieve(thread.id, run.id);
|
|
716
|
+
let attempts = 0;
|
|
717
|
+
const maxAttempts = 60; // 60 seconds for complex test case generation
|
|
718
|
+
|
|
719
|
+
while (runStatus.status !== 'completed' && attempts < maxAttempts) {
|
|
720
|
+
if (runStatus.status === 'failed') {
|
|
721
|
+
throw new Error(`Test case generation failed: ${runStatus.last_error?.message}`);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (attempts % 5 === 0) {
|
|
725
|
+
console.log(`⏳ Still generating... (${attempts}s elapsed)`);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
729
|
+
runStatus = await client.beta.threads.runs.retrieve(thread.id, run.id);
|
|
730
|
+
attempts++;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (runStatus.status !== 'completed') {
|
|
734
|
+
throw new Error('Test case generation timed out after 60 seconds');
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Get the generated test cases
|
|
738
|
+
const messages = await client.beta.threads.messages.list(thread.id);
|
|
739
|
+
const testCases = messages.data
|
|
740
|
+
.filter(msg => msg.role === 'assistant')
|
|
741
|
+
.map(msg => msg.content[0]?.text?.value)
|
|
742
|
+
.join('\n\n');
|
|
743
|
+
|
|
744
|
+
// Parse and structure the response
|
|
745
|
+
const testCaseCount = (testCases.match(/\*\*TC-/g) || []).length;
|
|
746
|
+
|
|
747
|
+
// Clean up assistant
|
|
748
|
+
try {
|
|
749
|
+
await client.beta.assistants.del(assistant.id);
|
|
750
|
+
} catch (cleanupError) {
|
|
751
|
+
console.warn('⚠️ Assistant cleanup failed:', cleanupError.message);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Save test cases to file for easy reference
|
|
755
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
756
|
+
const outputFileName = `test_cases_${timestamp.substring(0, 19)}.md`;
|
|
757
|
+
const outputPath = path.join(path.dirname(pdfPath), outputFileName);
|
|
758
|
+
|
|
759
|
+
const fileContent = `# Manual Test Cases
|
|
760
|
+
Generated on: ${new Date().toLocaleString()}
|
|
761
|
+
|
|
762
|
+
## Source Information
|
|
763
|
+
- **JIRA Summary:** ${jiraSummary}
|
|
764
|
+
- **JIRA Description:** ${jiraDescription}
|
|
765
|
+
- **Design File:** ${fileName}
|
|
766
|
+
- **Total Test Cases:** ${testCaseCount}
|
|
767
|
+
|
|
768
|
+
---
|
|
769
|
+
|
|
770
|
+
${testCases}
|
|
771
|
+
|
|
772
|
+
---
|
|
773
|
+
|
|
774
|
+
## Test Execution Notes
|
|
775
|
+
- Execute test cases in the order listed
|
|
776
|
+
- Log defects with screenshots when issues found
|
|
777
|
+
- Update test results in your test management tool
|
|
778
|
+
- Verify on multiple browsers/devices for compatibility tests
|
|
779
|
+
`;
|
|
780
|
+
|
|
781
|
+
await fs.writeFile(outputPath, fileContent, 'utf-8');
|
|
782
|
+
|
|
783
|
+
console.log(`✅ Test cases generated and saved!`);
|
|
594
784
|
|
|
595
785
|
return {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
786
|
+
success: true,
|
|
787
|
+
message: `✅ Successfully generated ${testCaseCount} manual test cases!`,
|
|
788
|
+
results: {
|
|
789
|
+
testCases: testCases,
|
|
790
|
+
summary: {
|
|
791
|
+
totalTestCases: testCaseCount,
|
|
792
|
+
categories: [
|
|
793
|
+
"Functional Test Cases",
|
|
794
|
+
"UI/UX Test Cases",
|
|
795
|
+
"Edge Case & Negative Test Cases",
|
|
796
|
+
"Compatibility Test Cases",
|
|
797
|
+
"Accessibility Test Cases"
|
|
798
|
+
],
|
|
799
|
+
generatedFrom: {
|
|
800
|
+
jiraSummary: jiraSummary,
|
|
801
|
+
designFile: fileName,
|
|
802
|
+
fileSize: `${(stats.size / 1024).toFixed(2)} KB`
|
|
803
|
+
}
|
|
804
|
+
},
|
|
805
|
+
outputs: {
|
|
806
|
+
savedToFile: outputPath,
|
|
807
|
+
fileName: outputFileName,
|
|
808
|
+
generatedAt: new Date().toISOString()
|
|
809
|
+
},
|
|
810
|
+
execution: {
|
|
811
|
+
processingTime: `${attempts} seconds`,
|
|
812
|
+
model: "gpt-4o",
|
|
813
|
+
pdfAnalyzed: true
|
|
814
|
+
}
|
|
815
|
+
}
|
|
599
816
|
};
|
|
817
|
+
|
|
600
818
|
} catch (err) {
|
|
601
|
-
|
|
819
|
+
console.error("❌ Test Case Generation Error:", err);
|
|
820
|
+
|
|
821
|
+
let errorMessage = "❌ Failed to generate test cases: ";
|
|
822
|
+
|
|
823
|
+
if (err.message?.includes('quota')) {
|
|
824
|
+
errorMessage += "OpenAI quota exceeded. Check billing.";
|
|
825
|
+
} else if (err.message?.includes('timeout')) {
|
|
826
|
+
errorMessage += "Generation took too long. Try with smaller PDF.";
|
|
827
|
+
} else if (err.message?.includes('file')) {
|
|
828
|
+
errorMessage += "PDF processing error. Check file format.";
|
|
829
|
+
} else if (err.message?.includes('API key')) {
|
|
830
|
+
errorMessage += "Invalid OpenAI API key.";
|
|
831
|
+
} else {
|
|
832
|
+
errorMessage += err.message || "Unknown error occurred.";
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
return {
|
|
836
|
+
success: false,
|
|
837
|
+
message: errorMessage,
|
|
838
|
+
error: {
|
|
839
|
+
message: err.message,
|
|
840
|
+
timestamp: new Date().toISOString(),
|
|
841
|
+
attempted: {
|
|
842
|
+
jiraSummary: jiraSummary,
|
|
843
|
+
pdfPath: pdfPath
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
troubleshooting: [
|
|
847
|
+
"Verify OpenAI API key in ~/Desktop/openai.json",
|
|
848
|
+
"Check PDF file exists and is readable",
|
|
849
|
+
"Ensure sufficient OpenAI credits",
|
|
850
|
+
"Try with smaller PDF if timeout occurs"
|
|
851
|
+
]
|
|
852
|
+
};
|
|
602
853
|
}
|
|
603
854
|
}
|
|
604
855
|
);
|
|
856
|
+
|
|
857
|
+
// ----------------------
|
|
858
|
+
// USAGE EXAMPLE
|
|
859
|
+
// ----------------------
|
|
860
|
+
|
|
861
|
+
/*
|
|
862
|
+
const result = await tools.uploadPdfToOpenai({
|
|
863
|
+
jiraSummary: "User Login Page Implementation",
|
|
864
|
+
jiraDescription: "Create a responsive login page with email/password fields, remember me checkbox, forgot password link, and social login options. Must support mobile and desktop views.",
|
|
865
|
+
pdfPath: "/path/to/login-design.pdf"
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
// Expected Output:
|
|
869
|
+
// - 15-25 detailed manual test cases
|
|
870
|
+
// - Categories: Functional, UI/UX, Edge Cases, Compatibility, Accessibility
|
|
871
|
+
// - Saved to markdown file for easy reference
|
|
872
|
+
// - Structured format ready for test management tools
|
|
873
|
+
*/
|
|
874
|
+
|
|
605
875
|
return server;
|
|
606
876
|
};
|
|
607
877
|
|