@nbakka/mcp-appium 4.0.8 → 4.0.9
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 +114 -0
- package/package.json +1 -1
package/lib/server.js
CHANGED
|
@@ -1677,6 +1677,120 @@ tool(
|
|
|
1677
1677
|
}
|
|
1678
1678
|
);
|
|
1679
1679
|
|
|
1680
|
+
// Helper: read BrowserStack credentials from ~/Desktop/browserstack-config.json
|
|
1681
|
+
const getBrowserStackAuth = async () => {
|
|
1682
|
+
const configPath = path.join(os.homedir(), "Desktop", "browserstack-config.json");
|
|
1683
|
+
const raw = await fs.readFile(configPath, "utf-8");
|
|
1684
|
+
const config = JSON.parse(raw);
|
|
1685
|
+
if (!config.username) throw new Error("'username' field missing in browserstack-config.json");
|
|
1686
|
+
if (!config.accessKey) throw new Error("'accessKey' field missing in browserstack-config.json");
|
|
1687
|
+
return { username: config.username, accessKey: config.accessKey };
|
|
1688
|
+
};
|
|
1689
|
+
|
|
1690
|
+
tool(
|
|
1691
|
+
"browserstack_create_subfolder",
|
|
1692
|
+
"Create a sub-folder inside a parent folder in BrowserStack Test Management. Use this after classifying test cases by feature to create a named sub-folder for that feature.",
|
|
1693
|
+
{
|
|
1694
|
+
projectId: zod_1.z.string().describe("BrowserStack project ID, e.g. PR-1"),
|
|
1695
|
+
parentFolderId: zod_1.z.number().int().describe("Numeric ID of the parent folder in which to create the sub-folder"),
|
|
1696
|
+
folderName: zod_1.z.string().describe("Name of the new sub-folder"),
|
|
1697
|
+
folderDescription: zod_1.z.string().optional().describe("Optional description of the sub-folder"),
|
|
1698
|
+
},
|
|
1699
|
+
async ({ projectId, parentFolderId, folderName, folderDescription }) => {
|
|
1700
|
+
const { username, accessKey } = await getBrowserStackAuth();
|
|
1701
|
+
const response = await axios.post(
|
|
1702
|
+
`https://test-management.browserstack.com/api/v2/projects/${projectId}/folders`,
|
|
1703
|
+
{
|
|
1704
|
+
folder: {
|
|
1705
|
+
name: folderName,
|
|
1706
|
+
description: folderDescription || "",
|
|
1707
|
+
parent_id: parentFolderId,
|
|
1708
|
+
},
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
auth: { username, password: accessKey },
|
|
1712
|
+
headers: { "Content-Type": "application/json" },
|
|
1713
|
+
}
|
|
1714
|
+
);
|
|
1715
|
+
const folder = response.data.folder;
|
|
1716
|
+
return `✅ Sub-folder created: "${folder.name}" (id: ${folder.id}) under parent folder ${parentFolderId}`;
|
|
1717
|
+
}
|
|
1718
|
+
);
|
|
1719
|
+
|
|
1720
|
+
tool(
|
|
1721
|
+
"browserstack_get_test_cases",
|
|
1722
|
+
"Fetch all test cases inside a folder from BrowserStack Test Management. Returns identifier, title, and folder_id for each test case so the LLM can classify them into features.",
|
|
1723
|
+
{
|
|
1724
|
+
projectId: zod_1.z.string().describe("BrowserStack project ID, e.g. PR-1"),
|
|
1725
|
+
folderId: zod_1.z.number().int().describe("Numeric ID of the folder whose test cases should be fetched"),
|
|
1726
|
+
},
|
|
1727
|
+
async ({ projectId, folderId }) => {
|
|
1728
|
+
const { username, accessKey } = await getBrowserStackAuth();
|
|
1729
|
+
const allTestCases = [];
|
|
1730
|
+
let page = 1;
|
|
1731
|
+
|
|
1732
|
+
while (true) {
|
|
1733
|
+
const response = await axios.get(
|
|
1734
|
+
`https://test-management.browserstack.com/api/v2/projects/${projectId}/test-cases`,
|
|
1735
|
+
{
|
|
1736
|
+
params: { folder_id: folderId, p: page },
|
|
1737
|
+
auth: { username, password: accessKey },
|
|
1738
|
+
}
|
|
1739
|
+
);
|
|
1740
|
+
const data = response.data;
|
|
1741
|
+
const cases = data.test_cases || [];
|
|
1742
|
+
allTestCases.push(...cases);
|
|
1743
|
+
// Stop when there is no next page
|
|
1744
|
+
if (!data.info || !data.info.next) break;
|
|
1745
|
+
page++;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
if (allTestCases.length === 0) {
|
|
1749
|
+
return `No test cases found in folder ${folderId}.`;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
const summary = allTestCases
|
|
1753
|
+
.map(tc => `- identifier: ${tc.identifier} | title: ${tc.title}`)
|
|
1754
|
+
.join("\n");
|
|
1755
|
+
return `Found ${allTestCases.length} test case(s) in folder ${folderId}:\n${summary}`;
|
|
1756
|
+
}
|
|
1757
|
+
);
|
|
1758
|
+
|
|
1759
|
+
tool(
|
|
1760
|
+
"browserstack_bulk_move_test_cases",
|
|
1761
|
+
"Bulk move test cases into a destination folder in BrowserStack Test Management. Use this after classifying test cases and creating the target sub-folder. Supports up to 1000 test case IDs per call.",
|
|
1762
|
+
{
|
|
1763
|
+
projectId: zod_1.z.string().describe("BrowserStack project ID, e.g. PR-1"),
|
|
1764
|
+
testCaseIds: zod_1.z.array(zod_1.z.string()).describe("Array of test case identifiers to move, e.g. [\"TC-0042\", \"TC-0188\"]"),
|
|
1765
|
+
destinationFolderId: zod_1.z.number().int().describe("Numeric ID of the destination folder"),
|
|
1766
|
+
},
|
|
1767
|
+
async ({ projectId, testCaseIds, destinationFolderId }) => {
|
|
1768
|
+
const { username, accessKey } = await getBrowserStackAuth();
|
|
1769
|
+
const response = await axios.post(
|
|
1770
|
+
`https://test-management.browserstack.com/api/v2/projects/${projectId}/test-cases/move`,
|
|
1771
|
+
{
|
|
1772
|
+
test_case: {
|
|
1773
|
+
ids: testCaseIds,
|
|
1774
|
+
destination_folder_id: destinationFolderId,
|
|
1775
|
+
},
|
|
1776
|
+
},
|
|
1777
|
+
{
|
|
1778
|
+
auth: { username, password: accessKey },
|
|
1779
|
+
headers: { "Content-Type": "application/json" },
|
|
1780
|
+
}
|
|
1781
|
+
);
|
|
1782
|
+
const results = response.data.results || {};
|
|
1783
|
+
const moved = results.moved || [];
|
|
1784
|
+
const errors = results.errors || [];
|
|
1785
|
+
let msg = `✅ Moved ${moved.length}/${testCaseIds.length} test case(s) to folder ${destinationFolderId}.`;
|
|
1786
|
+
if (errors.length > 0) {
|
|
1787
|
+
const errorDetails = errors.map(e => ` - ${e.id}: ${e.message}`).join("\n");
|
|
1788
|
+
msg += `\n⚠️ ${errors.length} error(s):\n${errorDetails}`;
|
|
1789
|
+
}
|
|
1790
|
+
return msg;
|
|
1791
|
+
}
|
|
1792
|
+
);
|
|
1793
|
+
|
|
1680
1794
|
return server;
|
|
1681
1795
|
};
|
|
1682
1796
|
|