|
1 | 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; |
2 | | -import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; |
3 | | -import { z } from "zod"; |
4 | | -import { trackMCP } from "../lib/instrumentation.js"; |
5 | | -import { getSDKPrefixCommand } from "./sdk-utils/commands.js"; |
6 | | - |
7 | | -import { |
8 | | - SDKSupportedBrowserAutomationFramework, |
9 | | - SDKSupportedLanguage, |
10 | | - SDKSupportedTestingFramework, |
11 | | - SDKSupportedLanguageEnum, |
12 | | - SDKSupportedBrowserAutomationFrameworkEnum, |
13 | | - SDKSupportedTestingFrameworkEnum, |
14 | | -} from "./sdk-utils/types.js"; |
15 | | - |
16 | | -import { |
17 | | - generateBrowserStackYMLInstructions, |
18 | | - getInstructionsForProjectConfiguration, |
19 | | - formatInstructionsWithNumbers, |
20 | | -} from "./sdk-utils/instructions.js"; |
21 | | - |
22 | | -import { |
23 | | - formatPercyInstructions, |
24 | | - getPercyInstructions, |
25 | | -} from "./sdk-utils/percy/instructions.js"; |
26 | | -import { getBrowserStackAuth } from "../lib/get-auth.js"; |
27 | 2 | import { BrowserStackConfig } from "../lib/types.js"; |
| 3 | +import { RunTestsOnBrowserStackParamsShape } from "./sdk-utils/common/schema.js"; |
| 4 | +import { runTestsOnBrowserStackHandler } from "./sdk-utils/handler.js"; |
| 5 | +import { RUN_ON_BROWSERSTACK_DESCRIPTION } from "./sdk-utils/common/constants.js"; |
28 | 6 |
|
29 | | -/** |
30 | | - * BrowserStack SDK hooks into your test framework to seamlessly run tests on BrowserStack. |
31 | | - * This tool gives instructions to setup a browserstack.yml file in the project root and installs the necessary dependencies. |
32 | | - */ |
33 | | -export async function bootstrapProjectWithSDK({ |
34 | | - detectedBrowserAutomationFramework, |
35 | | - detectedTestingFramework, |
36 | | - detectedLanguage, |
37 | | - desiredPlatforms, |
38 | | - enablePercy, |
39 | | - config, |
40 | | -}: { |
41 | | - detectedBrowserAutomationFramework: SDKSupportedBrowserAutomationFramework; |
42 | | - detectedTestingFramework: SDKSupportedTestingFramework; |
43 | | - detectedLanguage: SDKSupportedLanguage; |
44 | | - desiredPlatforms: string[]; |
45 | | - enablePercy: boolean; |
46 | | - config: BrowserStackConfig; |
47 | | -}): Promise<CallToolResult> { |
48 | | - // Get credentials from config |
49 | | - const authString = getBrowserStackAuth(config); |
50 | | - const [username, accessKey] = authString.split(":"); |
51 | | - |
52 | | - // Handle frameworks with unique setup instructions that don't use browserstack.yml |
53 | | - if ( |
54 | | - detectedBrowserAutomationFramework === "cypress" || |
55 | | - detectedTestingFramework === "webdriverio" |
56 | | - ) { |
57 | | - let combinedInstructions = getInstructionsForProjectConfiguration( |
58 | | - detectedBrowserAutomationFramework, |
59 | | - detectedTestingFramework, |
60 | | - detectedLanguage, |
61 | | - username, |
62 | | - accessKey, |
63 | | - ); |
64 | | - |
65 | | - if (enablePercy) { |
66 | | - const percyInstructions = getPercyInstructions( |
67 | | - detectedLanguage, |
68 | | - detectedBrowserAutomationFramework, |
69 | | - detectedTestingFramework, |
70 | | - ); |
71 | | - |
72 | | - if (percyInstructions) { |
73 | | - combinedInstructions += |
74 | | - "\n\n" + formatPercyInstructions(percyInstructions); |
75 | | - } else { |
76 | | - throw new Error( |
77 | | - `Percy is currently not supported through MCP for ${detectedLanguage} with ${detectedTestingFramework}. If you want to run the test cases without Percy, disable Percy and run it again.`, |
78 | | - ); |
79 | | - } |
80 | | - } |
81 | | - |
82 | | - // Apply consistent formatting for all configurations |
83 | | - return formatFinalInstructions(combinedInstructions); |
84 | | - } |
85 | | - |
86 | | - // Handle default flow using browserstack.yml |
87 | | - const sdkSetupCommand = getSDKPrefixCommand( |
88 | | - detectedLanguage, |
89 | | - detectedTestingFramework, |
90 | | - username, |
91 | | - accessKey, |
92 | | - ); |
93 | | - |
94 | | - const ymlInstructions = generateBrowserStackYMLInstructions( |
95 | | - desiredPlatforms, |
96 | | - enablePercy, |
97 | | - ); |
98 | | - |
99 | | - const instructionsForProjectConfiguration = |
100 | | - getInstructionsForProjectConfiguration( |
101 | | - detectedBrowserAutomationFramework, |
102 | | - detectedTestingFramework, |
103 | | - detectedLanguage, |
104 | | - username, |
105 | | - accessKey, |
106 | | - ); |
107 | | - |
108 | | - let combinedInstructions = ""; |
109 | | - |
110 | | - // Step 1: Add SDK setup command |
111 | | - if (sdkSetupCommand) { |
112 | | - combinedInstructions += sdkSetupCommand; |
113 | | - } |
114 | | - |
115 | | - // Step 2: Add browserstack.yml setup |
116 | | - if (ymlInstructions) { |
117 | | - combinedInstructions += "\n\n---STEP---\n" + ymlInstructions; |
118 | | - } |
119 | | - |
120 | | - // Step 3: Add language/framework-specific setup |
121 | | - if (instructionsForProjectConfiguration) { |
122 | | - combinedInstructions += "\n\n" + instructionsForProjectConfiguration; |
123 | | - } |
124 | | - |
125 | | - // Step 4: Add Percy setup if applicable |
126 | | - if (enablePercy) { |
127 | | - const percyInstructions = getPercyInstructions( |
128 | | - detectedLanguage, |
129 | | - detectedBrowserAutomationFramework, |
130 | | - detectedTestingFramework, |
131 | | - ); |
132 | | - |
133 | | - if (percyInstructions) { |
134 | | - combinedInstructions += |
135 | | - "\n\n" + formatPercyInstructions(percyInstructions); |
136 | | - } else { |
137 | | - throw new Error( |
138 | | - `Percy is currently not supported through MCP for ${detectedLanguage} with ${detectedTestingFramework}. If you want to run the test cases without Percy, disable Percy and run it again.`, |
139 | | - ); |
140 | | - } |
141 | | - } |
142 | | - |
143 | | - // Apply consistent formatting for all configurations |
144 | | - return formatFinalInstructions(combinedInstructions); |
145 | | -} |
146 | | - |
147 | | -// Helper function to apply consistent formatting to all instruction types |
148 | | -function formatFinalInstructions(combinedInstructions: string): CallToolResult { |
149 | | - const fullInstructions = `⚠️ IMPORTANT: DO NOT SKIP ANY STEP |
150 | | - All the setup steps described in this file MUST be executed regardless of any existing configuration or setup. |
151 | | - This ensures proper BrowserStack SDK setup. |
152 | | -
|
153 | | - ${formatInstructionsWithNumbers(combinedInstructions)}`; |
154 | | - |
155 | | - return { |
156 | | - content: [ |
157 | | - { |
158 | | - type: "text", |
159 | | - text: fullInstructions, |
160 | | - isError: false, |
161 | | - }, |
162 | | - ], |
163 | | - }; |
164 | | -} |
165 | | - |
166 | | -export default function addSDKTools( |
| 7 | +export function registerRunBrowserStackTestsTool( |
167 | 8 | server: McpServer, |
168 | 9 | config: BrowserStackConfig, |
169 | 10 | ) { |
170 | 11 | const tools: Record<string, any> = {}; |
171 | 12 |
|
172 | 13 | tools.setupBrowserStackAutomateTests = server.tool( |
173 | 14 | "setupBrowserStackAutomateTests", |
174 | | - "Set up and run automated web-based tests on BrowserStack using the BrowserStack SDK. Use for functional or integration tests on BrowserStack, with optional Percy visual testing for supported frameworks. Example prompts: run this test on browserstack; run this test on browserstack with Percy; set up this project for browserstack with Percy. Integrate BrowserStack SDK into your project", |
175 | | - { |
176 | | - detectedBrowserAutomationFramework: z |
177 | | - .nativeEnum(SDKSupportedBrowserAutomationFrameworkEnum) |
178 | | - .describe( |
179 | | - "The automation framework configured in the project. Example: 'playwright', 'selenium'", |
180 | | - ), |
181 | | - |
182 | | - detectedTestingFramework: z |
183 | | - .nativeEnum(SDKSupportedTestingFrameworkEnum) |
184 | | - .describe( |
185 | | - "The testing framework used in the project. Be precise with framework selection Example: 'webdriverio', 'jest', 'pytest', 'junit4', 'junit5', 'mocha'", |
186 | | - ), |
187 | | - |
188 | | - detectedLanguage: z |
189 | | - .nativeEnum(SDKSupportedLanguageEnum) |
190 | | - .describe( |
191 | | - "The programming language used in the project. Example: 'nodejs', 'python', 'java', 'csharp'", |
192 | | - ), |
193 | | - |
194 | | - desiredPlatforms: z |
195 | | - .array(z.enum(["windows", "macos", "android", "ios"])) |
196 | | - .describe( |
197 | | - "The platforms the user wants to test on. Always ask this to the user, do not try to infer this.", |
198 | | - ), |
199 | | - |
200 | | - enablePercy: z |
201 | | - .boolean() |
202 | | - .optional() |
203 | | - .default(false) |
204 | | - .describe( |
205 | | - "Set to true if the user wants to enable Percy for visual testing. Defaults to false.", |
206 | | - ), |
207 | | - }, |
208 | | - |
| 15 | + RUN_ON_BROWSERSTACK_DESCRIPTION, |
| 16 | + RunTestsOnBrowserStackParamsShape, |
209 | 17 | async (args) => { |
210 | | - try { |
211 | | - trackMCP( |
212 | | - "runTestsOnBrowserStack", |
213 | | - server.server.getClientVersion()!, |
214 | | - undefined, |
215 | | - config, |
216 | | - ); |
217 | | - |
218 | | - return await bootstrapProjectWithSDK({ |
219 | | - detectedBrowserAutomationFramework: |
220 | | - args.detectedBrowserAutomationFramework as SDKSupportedBrowserAutomationFramework, |
221 | | - |
222 | | - detectedTestingFramework: |
223 | | - args.detectedTestingFramework as SDKSupportedTestingFramework, |
224 | | - |
225 | | - detectedLanguage: args.detectedLanguage as SDKSupportedLanguage, |
226 | | - |
227 | | - desiredPlatforms: args.desiredPlatforms, |
228 | | - enablePercy: args.enablePercy, |
229 | | - config, |
230 | | - }); |
231 | | - } catch (error) { |
232 | | - trackMCP( |
233 | | - "runTestsOnBrowserStack", |
234 | | - server.server.getClientVersion()!, |
235 | | - error, |
236 | | - config, |
237 | | - ); |
238 | | - |
239 | | - return { |
240 | | - content: [ |
241 | | - { |
242 | | - type: "text", |
243 | | - text: `Failed to bootstrap project with BrowserStack SDK. Error: ${error}. Please open an issue on GitHub if the problem persists`, |
244 | | - isError: true, |
245 | | - }, |
246 | | - ], |
247 | | - isError: true, |
248 | | - }; |
249 | | - } |
| 18 | + return runTestsOnBrowserStackHandler(args, config); |
250 | 19 | }, |
251 | 20 | ); |
252 | 21 |
|
253 | 22 | return tools; |
254 | 23 | } |
| 24 | + |
| 25 | +export default registerRunBrowserStackTestsTool; |
0 commit comments