@browserstack/mcp-server 1.2.4 → 1.2.5

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 (39) hide show
  1. package/README.md +9 -5
  2. package/dist/lib/apiClient.d.ts +7 -5
  3. package/dist/lib/apiClient.js +76 -15
  4. package/dist/lib/device-cache.d.ts +3 -1
  5. package/dist/lib/device-cache.js +4 -0
  6. package/dist/lib/instrumentation.js +6 -3
  7. package/dist/lib/utils.d.ts +75 -2
  8. package/dist/lib/utils.js +20 -0
  9. package/dist/lib/version-resolver.js +26 -14
  10. package/dist/tools/appautomate-utils/appium-sdk/config-generator.d.ts +7 -1
  11. package/dist/tools/appautomate-utils/appium-sdk/config-generator.js +46 -26
  12. package/dist/tools/appautomate-utils/appium-sdk/constants.d.ts +1 -1
  13. package/dist/tools/appautomate-utils/appium-sdk/constants.js +24 -3
  14. package/dist/tools/appautomate-utils/appium-sdk/handler.js +16 -2
  15. package/dist/tools/appautomate-utils/appium-sdk/languages/java.d.ts +2 -0
  16. package/dist/tools/appautomate-utils/appium-sdk/languages/java.js +63 -29
  17. package/dist/tools/appautomate-utils/appium-sdk/types.d.ts +2 -1
  18. package/dist/tools/appautomate-utils/appium-sdk/types.js +10 -1
  19. package/dist/tools/appautomate-utils/native-execution/constants.d.ts +2 -1
  20. package/dist/tools/appautomate-utils/native-execution/constants.js +24 -2
  21. package/dist/tools/appautomate.js +15 -2
  22. package/dist/tools/percy-sdk.js +30 -3
  23. package/dist/tools/run-percy-scan.js +1 -1
  24. package/dist/tools/sdk-utils/bstack/configUtils.d.ts +7 -4
  25. package/dist/tools/sdk-utils/bstack/configUtils.js +67 -20
  26. package/dist/tools/sdk-utils/bstack/sdkHandler.d.ts +1 -1
  27. package/dist/tools/sdk-utils/bstack/sdkHandler.js +10 -2
  28. package/dist/tools/sdk-utils/common/constants.d.ts +5 -4
  29. package/dist/tools/sdk-utils/common/constants.js +7 -6
  30. package/dist/tools/sdk-utils/common/device-validator.d.ts +25 -0
  31. package/dist/tools/sdk-utils/common/device-validator.js +368 -0
  32. package/dist/tools/sdk-utils/common/schema.d.ts +24 -4
  33. package/dist/tools/sdk-utils/common/schema.js +57 -3
  34. package/dist/tools/sdk-utils/common/utils.js +2 -1
  35. package/dist/tools/sdk-utils/handler.d.ts +1 -0
  36. package/dist/tools/sdk-utils/handler.js +40 -12
  37. package/dist/tools/sdk-utils/percy-bstack/handler.js +5 -1
  38. package/dist/tools/sdk-utils/percy-web/handler.js +3 -1
  39. package/package.json +2 -2
@@ -4,15 +4,31 @@ import { createStep, combineInstructions, createEnvStep, PLATFORM_UTILS, } from
4
4
  export const MAVEN_ARCHETYPE_GROUP_ID = "com.browserstack";
5
5
  export const MAVEN_ARCHETYPE_ARTIFACT_ID = "junit-archetype-integrate";
6
6
  export const MAVEN_ARCHETYPE_VERSION = "1.0";
7
+ // Version mapping for different frameworks
8
+ export const JAVA_APP_FRAMEWORK_VERSION_MAP = {
9
+ testng: "1.4",
10
+ selenide: "1.4",
11
+ junit5: "1.0",
12
+ junit4: "1.0",
13
+ jbehave: "1.0",
14
+ cucumberTestng: "1.0",
15
+ cucumberJunit4: "1.0",
16
+ cucumberJunit5: "1.0",
17
+ cucumber: "1.0",
18
+ serenity: "1.0",
19
+ };
7
20
  // Framework mapping for Java Maven archetype generation for App Automate
8
21
  export const JAVA_APP_FRAMEWORK_MAP = {
9
- testng: "browserstack-sdk-archetype-integrate",
22
+ testng: "testng-archetype-integrate",
10
23
  junit5: "browserstack-sdk-archetype-integrate",
11
24
  selenide: "selenide-archetype-integrate",
12
25
  jbehave: "browserstack-sdk-archetype-integrate",
26
+ junit4: "browserstack-sdk-archetype-integrate",
13
27
  cucumberTestng: "browserstack-sdk-archetype-integrate",
14
28
  cucumberJunit4: "browserstack-sdk-archetype-integrate",
15
29
  cucumberJunit5: "browserstack-sdk-archetype-integrate",
30
+ cucumber: "browserstack-sdk-archetype-integrate",
31
+ serenity: "browserstack-sdk-archetype-integrate",
16
32
  };
17
33
  // Common Gradle setup instructions for App Automate (platform-independent)
18
34
  export const GRADLE_APP_SETUP_INSTRUCTIONS = `
@@ -35,53 +51,71 @@ mvn test
35
51
  export function getJavaAppFrameworkForMaven(framework) {
36
52
  return JAVA_APP_FRAMEWORK_MAP[framework] || framework;
37
53
  }
38
- function getMavenCommandForWindows(framework, mavenFramework, username, accessKey) {
39
- return (`mvn archetype:generate -B ` +
54
+ export function getJavaAppFrameworkVersion(framework) {
55
+ return JAVA_APP_FRAMEWORK_VERSION_MAP[framework] || MAVEN_ARCHETYPE_VERSION;
56
+ }
57
+ function getMavenCommandForWindows(framework, mavenFramework, version, username, accessKey, appPath) {
58
+ let command = `mvn archetype:generate -B ` +
40
59
  `-DarchetypeGroupId="${MAVEN_ARCHETYPE_GROUP_ID}" ` +
41
60
  `-DarchetypeArtifactId="${mavenFramework}" ` +
42
- `-DarchetypeVersion="${MAVEN_ARCHETYPE_VERSION}" ` +
61
+ `-DarchetypeVersion="${version}" ` +
43
62
  `-DgroupId="${MAVEN_ARCHETYPE_GROUP_ID}" ` +
44
- `-DartifactId="${MAVEN_ARCHETYPE_ARTIFACT_ID}" ` +
45
- `-Dversion="${MAVEN_ARCHETYPE_VERSION}" ` +
63
+ `-DartifactId="${mavenFramework}" ` +
64
+ `-Dversion="${version}" ` +
46
65
  `-DBROWSERSTACK_USERNAME="${username}" ` +
47
- `-DBROWSERSTACK_ACCESS_KEY="${accessKey}" ` +
48
- `-DBROWSERSTACK_FRAMEWORK="${framework}"`);
66
+ `-DBROWSERSTACK_ACCESS_KEY="${accessKey}"`;
67
+ // Add framework parameter for browserstack-sdk-archetype-integrate
68
+ if (mavenFramework === "browserstack-sdk-archetype-integrate") {
69
+ command += ` -DBROWSERSTACK_FRAMEWORK="${framework}"`;
70
+ }
71
+ // Add app path if provided
72
+ if (appPath) {
73
+ command += ` -DBROWSERSTACK_APP="${appPath}"`;
74
+ }
75
+ return command;
49
76
  }
50
- function getMavenCommandForUnix(framework, mavenFramework, username, accessKey) {
51
- return (`mvn archetype:generate -B ` +
77
+ function getMavenCommandForUnix(framework, mavenFramework, version, username, accessKey, appPath) {
78
+ let command = `mvn archetype:generate -B ` +
52
79
  `-DarchetypeGroupId="${MAVEN_ARCHETYPE_GROUP_ID}" ` +
53
80
  `-DarchetypeArtifactId="${mavenFramework}" ` +
54
- `-DarchetypeVersion="${MAVEN_ARCHETYPE_VERSION}" ` +
81
+ `-DarchetypeVersion="${version}" ` +
55
82
  `-DgroupId="${MAVEN_ARCHETYPE_GROUP_ID}" ` +
56
- `-DartifactId="${MAVEN_ARCHETYPE_ARTIFACT_ID}" ` +
57
- `-Dversion="${MAVEN_ARCHETYPE_VERSION}" ` +
83
+ `-DartifactId="${mavenFramework}" ` +
84
+ `-Dversion="${version}" ` +
58
85
  `-DBROWSERSTACK_USERNAME="${username}" ` +
59
- `-DBROWSERSTACK_ACCESS_KEY="${accessKey}" ` +
60
- `-DBROWSERSTACK_FRAMEWORK="${framework}"`);
86
+ `-DBROWSERSTACK_ACCESS_KEY="${accessKey}"`;
87
+ // Add framework parameter for browserstack-sdk-archetype-integrate
88
+ if (mavenFramework === "browserstack-sdk-archetype-integrate") {
89
+ command += ` -DBROWSERSTACK_FRAMEWORK="${framework}"`;
90
+ }
91
+ // Add app path if provided
92
+ if (appPath) {
93
+ command += ` -DBROWSERSTACK_APP="${appPath}"`;
94
+ }
95
+ return command;
61
96
  }
62
97
  export function getJavaSDKCommand(framework, username, accessKey, appPath) {
63
98
  const { isWindows = false, getPlatformLabel } = PLATFORM_UTILS || {};
64
99
  const mavenFramework = getJavaAppFrameworkForMaven(framework);
100
+ const version = getJavaAppFrameworkVersion(framework);
65
101
  let mavenCommand;
66
102
  if (isWindows) {
67
- mavenCommand = getMavenCommandForWindows(framework, mavenFramework, username, accessKey);
68
- if (appPath) {
69
- mavenCommand += ` -DBROWSERSTACK_APP="${appPath}"`;
70
- }
103
+ mavenCommand = getMavenCommandForWindows(framework, mavenFramework, version, username, accessKey, appPath);
71
104
  }
72
105
  else {
73
- mavenCommand = getMavenCommandForUnix(framework, mavenFramework, username, accessKey);
74
- if (appPath) {
75
- mavenCommand += ` -DBROWSERSTACK_APP="${appPath}"`;
76
- }
106
+ mavenCommand = getMavenCommandForUnix(framework, mavenFramework, version, username, accessKey, appPath);
77
107
  }
78
108
  const envStep = createEnvStep(username, accessKey, isWindows, getPlatformLabel());
79
109
  const mavenStep = createStep("Install BrowserStack SDK using Maven Archetype for App Automate", `Maven command for ${framework} (${getPlatformLabel()}):
80
- \`\`\`bash
81
- ${mavenCommand}
82
- \`\`\`
110
+ \`\`\`bash
111
+ ${mavenCommand}
112
+ \`\`\`
83
113
 
84
- Alternative setup for Gradle users:
85
- ${GRADLE_APP_SETUP_INSTRUCTIONS}`);
86
- return combineInstructions(envStep, mavenStep);
114
+ Alternative setup for Gradle users:
115
+ ${GRADLE_APP_SETUP_INSTRUCTIONS}`);
116
+ const argsLineStep = createStep("Verifying dependency and argsLine", `Verify browserstack-java-sdk with LATEST is added as dependency and add this line in pom.xml if not added:
117
+ \`\`\`xml
118
+ <argLine>-javaagent:"\${com.browserstack:browserstack-java-sdk:jar}"</argLine>
119
+ \`\`\``);
120
+ return combineInstructions(envStep, mavenStep, argsLineStep);
87
121
  }
@@ -16,6 +16,7 @@ export declare enum AppSDKSupportedTestingFrameworkEnum {
16
16
  junit4 = "junit4",
17
17
  selenide = "selenide",
18
18
  jbehave = "jbehave",
19
+ serenity = "serenity",
19
20
  cucumberTestng = "cucumberTestng",
20
21
  cucumberJunit4 = "cucumberJunit4",
21
22
  cucumberJunit5 = "cucumberJunit5",
@@ -49,7 +50,7 @@ export interface AppSDKInstruction {
49
50
  export declare const SUPPORTED_CONFIGURATIONS: {
50
51
  appium: {
51
52
  ruby: string[];
52
- java: never[];
53
+ java: string[];
53
54
  csharp: never[];
54
55
  python: string[];
55
56
  nodejs: string[];
@@ -18,6 +18,7 @@ export var AppSDKSupportedTestingFrameworkEnum;
18
18
  AppSDKSupportedTestingFrameworkEnum["junit4"] = "junit4";
19
19
  AppSDKSupportedTestingFrameworkEnum["selenide"] = "selenide";
20
20
  AppSDKSupportedTestingFrameworkEnum["jbehave"] = "jbehave";
21
+ AppSDKSupportedTestingFrameworkEnum["serenity"] = "serenity";
21
22
  AppSDKSupportedTestingFrameworkEnum["cucumberTestng"] = "cucumberTestng";
22
23
  AppSDKSupportedTestingFrameworkEnum["cucumberJunit4"] = "cucumberJunit4";
23
24
  AppSDKSupportedTestingFrameworkEnum["cucumberJunit5"] = "cucumberJunit5";
@@ -46,7 +47,15 @@ export var AppSDKSupportedPlatformEnum;
46
47
  export const SUPPORTED_CONFIGURATIONS = {
47
48
  appium: {
48
49
  ruby: ["cucumberRuby"],
49
- java: [],
50
+ java: [
51
+ "testng",
52
+ "cucumber",
53
+ "junit4",
54
+ "junit5",
55
+ "jbehave",
56
+ "selenide",
57
+ "serenity",
58
+ ],
50
59
  csharp: [],
51
60
  python: ["pytest", "robot", "behave", "lettuce"],
52
61
  nodejs: ["jest", "mocha", "cucumberJs", "webdriverio", "nightwatch"],
@@ -1,10 +1,11 @@
1
1
  import { z } from "zod";
2
2
  import { AppTestPlatform } from "./types.js";
3
+ import { AppSDKSupportedPlatformEnum } from "../appium-sdk/types.js";
3
4
  export declare const RUN_APP_AUTOMATE_DESCRIPTION = "Execute pre-built native mobile test suites (Espresso for Android, XCUITest for iOS) by direct upload to BrowserStack. ONLY for compiled .apk/.ipa test files. This is NOT for SDK integration or Appium tests. For Appium-based testing with SDK setup, use 'setupBrowserStackAppAutomateTests' instead.";
4
5
  export declare const RUN_APP_AUTOMATE_SCHEMA: {
5
6
  appPath: z.ZodString;
6
7
  testSuitePath: z.ZodString;
7
- devices: z.ZodArray<z.ZodString, "many">;
8
+ devices: z.ZodDefault<z.ZodArray<z.ZodUnion<[z.ZodTuple<[z.ZodLiteral<AppSDKSupportedPlatformEnum.android>, z.ZodString, z.ZodString], null>, z.ZodTuple<[z.ZodLiteral<AppSDKSupportedPlatformEnum.ios>, z.ZodString, z.ZodString], null>]>, "many">>;
8
9
  project: z.ZodDefault<z.ZodOptional<z.ZodString>>;
9
10
  detectedAutomationFramework: z.ZodNativeEnum<typeof AppTestPlatform>;
10
11
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { AppTestPlatform } from "./types.js";
3
+ import { AppSDKSupportedPlatformEnum } from "../appium-sdk/types.js";
3
4
  export const RUN_APP_AUTOMATE_DESCRIPTION = `Execute pre-built native mobile test suites (Espresso for Android, XCUITest for iOS) by direct upload to BrowserStack. ONLY for compiled .apk/.ipa test files. This is NOT for SDK integration or Appium tests. For Appium-based testing with SDK setup, use 'setupBrowserStackAppAutomateTests' instead.`;
4
5
  export const RUN_APP_AUTOMATE_SCHEMA = {
5
6
  appPath: z
@@ -23,8 +24,29 @@ export const RUN_APP_AUTOMATE_SCHEMA = {
23
24
  " zip -r Tests.zip *.xctestrun *-Runner.app\n\n" +
24
25
  "If in other directory, provide existing test file path"),
25
26
  devices: z
26
- .array(z.string())
27
- .describe("List of devices to run the test on, e.g., ['Samsung Galaxy S20-10.0', 'iPhone 12 Pro-16.0']."),
27
+ .array(z.union([
28
+ // Android: [android, deviceName, osVersion]
29
+ z.tuple([
30
+ z
31
+ .literal(AppSDKSupportedPlatformEnum.android)
32
+ .describe("Platform identifier: 'android'"),
33
+ z
34
+ .string()
35
+ .describe("Device name, e.g. 'Samsung Galaxy S24', 'Google Pixel 8'"),
36
+ z.string().describe("Android version, e.g. '14', '16', 'latest'"),
37
+ ]),
38
+ // iOS: [ios, deviceName, osVersion]
39
+ z.tuple([
40
+ z
41
+ .literal(AppSDKSupportedPlatformEnum.ios)
42
+ .describe("Platform identifier: 'ios'"),
43
+ z.string().describe("Device name, e.g. 'iPhone 15', 'iPhone 14 Pro'"),
44
+ z.string().describe("iOS version, e.g. '17', '16', 'latest'"),
45
+ ]),
46
+ ]))
47
+ .max(3)
48
+ .default([])
49
+ .describe("Tuples describing target mobile devices. Add device only when user asks explicitly for it. Defaults to [] . Example: [['android', 'Samsung Galaxy S24', '14'], ['ios', 'iPhone 15', '17']]"),
28
50
  project: z
29
51
  .string()
30
52
  .optional()
@@ -6,6 +6,7 @@ import { maybeCompressBase64 } from "../lib/utils.js";
6
6
  import { remote } from "webdriverio";
7
7
  import { AppTestPlatform } from "./appautomate-utils/native-execution/types.js";
8
8
  import { setupAppAutomateHandler } from "./appautomate-utils/appium-sdk/handler.js";
9
+ import { validateAppAutomateDevices } from "./sdk-utils/common/device-validator.js";
9
10
  import { SETUP_APP_AUTOMATE_DESCRIPTION, SETUP_APP_AUTOMATE_SCHEMA, } from "./appautomate-utils/appium-sdk/constants.js";
10
11
  import { Platform, } from "./appautomate-utils/native-execution/types.js";
11
12
  import { getDevicesAndBrowsers, BrowserStackProducts, } from "../lib/device-cache.js";
@@ -105,6 +106,8 @@ async function runAppTestsOnBrowserStack(args, config) {
105
106
  if (!args.browserstackTestSuiteUrl && !args.testSuitePath) {
106
107
  throw new Error("testSuitePath is required when browserstackTestSuiteUrl is not provided");
107
108
  }
109
+ // Validate devices against real BrowserStack device data
110
+ await validateAppAutomateDevices(args.devices);
108
111
  switch (args.detectedAutomationFramework) {
109
112
  case AppTestPlatform.ESPRESSO: {
110
113
  try {
@@ -126,7 +129,12 @@ async function runAppTestsOnBrowserStack(args, config) {
126
129
  test_suite_url = await uploadEspressoTestSuite(args.testSuitePath, config);
127
130
  logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
128
131
  }
129
- const build_id = await triggerEspressoBuild(app_url, test_suite_url, args.devices, args.project);
132
+ // Convert array format to string format for Espresso
133
+ const deviceStrings = args.devices.map((device) => {
134
+ const [, deviceName, osVersion] = device;
135
+ return `${deviceName}-${osVersion}`;
136
+ });
137
+ const build_id = await triggerEspressoBuild(app_url, test_suite_url, deviceStrings, args.project);
130
138
  return {
131
139
  content: [
132
140
  {
@@ -161,7 +169,12 @@ async function runAppTestsOnBrowserStack(args, config) {
161
169
  test_suite_url = await uploadXcuiTestSuite(args.testSuitePath, config);
162
170
  logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
163
171
  }
164
- const build_id = await triggerXcuiBuild(app_url, test_suite_url, args.devices, args.project, config);
172
+ // Convert array format to string format for XCUITest
173
+ const deviceStrings = args.devices.map((device) => {
174
+ const [, deviceName, osVersion] = device;
175
+ return `${deviceName}-${osVersion}`;
176
+ });
177
+ const build_id = await triggerXcuiBuild(app_url, test_suite_url, deviceStrings, args.project, config);
165
178
  return {
166
179
  content: [
167
180
  {
@@ -5,14 +5,41 @@ import { runPercyScan } from "./run-percy-scan.js";
5
5
  import { SetUpPercyParamsShape } from "./sdk-utils/common/schema.js";
6
6
  import { updateTestsWithPercyCommands } from "./add-percy-snapshots.js";
7
7
  import { approveOrDeclinePercyBuild } from "./review-agent-utils/percy-approve-reject.js";
8
- import { setUpPercyHandler } from "./sdk-utils/handler.js";
9
- import { SETUP_PERCY_DESCRIPTION, LIST_TEST_FILES_DESCRIPTION, PERCY_SNAPSHOT_COMMANDS_DESCRIPTION, } from "./sdk-utils/common/constants.js";
8
+ import { setUpPercyHandler, simulatePercyChangeHandler, } from "./sdk-utils/handler.js";
9
+ import { z } from "zod";
10
+ import { SETUP_PERCY_DESCRIPTION, LIST_TEST_FILES_DESCRIPTION, PERCY_SNAPSHOT_COMMANDS_DESCRIPTION, SIMULATE_PERCY_CHANGE_DESCRIPTION, } from "./sdk-utils/common/constants.js";
10
11
  import { ListTestFilesParamsShape, UpdateTestFileWithInstructionsParams, } from "./percy-snapshot-utils/constants.js";
11
12
  import { RunPercyScanParamsShape, FetchPercyChangesParamsShape, ManagePercyBuildApprovalParamsShape, } from "./sdk-utils/common/schema.js";
12
13
  import { handleMCPError } from "../lib/utils.js";
13
14
  export function registerPercyTools(server, config) {
14
15
  const tools = {};
15
- tools.setupPercyVisualTesting = server.tool("setupPercyVisualTesting", SETUP_PERCY_DESCRIPTION, SetUpPercyParamsShape, async (args) => {
16
+ server.prompt("integrate-percy", {
17
+ project_name: z
18
+ .string()
19
+ .describe("The name of the project to integrate with Percy"),
20
+ }, async ({ project_name }) => {
21
+ return {
22
+ messages: [
23
+ {
24
+ role: "assistant",
25
+ content: {
26
+ type: "text",
27
+ text: `Integrate percy in this project ${project_name} using tool percyVisualTestIntegrationAgent.`,
28
+ },
29
+ },
30
+ ],
31
+ };
32
+ });
33
+ tools.percyVisualTestIntegrationAgent = server.tool("percyVisualTestIntegrationAgent", SIMULATE_PERCY_CHANGE_DESCRIPTION, SetUpPercyParamsShape, async (args) => {
34
+ try {
35
+ trackMCP("VisualTestIntegrationAgent", server.server.getClientVersion(), config);
36
+ return simulatePercyChangeHandler(args, config);
37
+ }
38
+ catch (error) {
39
+ return handleMCPError("VisualTestIntegrationAgent", server, config, error);
40
+ }
41
+ });
42
+ tools.setupPercyVisualTesting = server.tool("expandPercyVisualTesting", SETUP_PERCY_DESCRIPTION, SetUpPercyParamsShape, async (args) => {
16
43
  try {
17
44
  trackMCP("setupPercyVisualTesting", server.server.getClientVersion(), config);
18
45
  return setUpPercyHandler(args, config);
@@ -15,7 +15,7 @@ export async function runPercyScan(args, config) {
15
15
  - Python → pytest
16
16
  - Node.js → npm test or yarn test
17
17
  - Cypress → cypress run
18
- or from package.json scripts`, `Wrap the inferred command with Percy:\nnpx percy exec -- <test command>`, `If the test command cannot be inferred confidently, ask the user directly for the correct test command.`);
18
+ or from package.json scripts`, `Wrap the inferred command with Percy along with label: \nnpx percy exec --labels=mcp -- <test command>`, `If the test command cannot be inferred confidently, ask the user directly for the correct test command.`);
19
19
  const instructionContext = steps
20
20
  .map((step, index) => `${index + 1}. ${step}`)
21
21
  .join("\n\n");
@@ -1,4 +1,7 @@
1
- /**
2
- * Utilities for generating BrowserStack configuration files.
3
- */
4
- export declare function generateBrowserStackYMLInstructions(desiredPlatforms: string[], enablePercy: boolean | undefined, projectName: string): string;
1
+ import { ValidatedEnvironment } from "../common/device-validator.js";
2
+ export declare function generateBrowserStackYMLInstructions(config: {
3
+ validatedEnvironments?: ValidatedEnvironment[];
4
+ platforms?: string[];
5
+ enablePercy?: boolean;
6
+ projectName: string;
7
+ }): string;
@@ -1,37 +1,34 @@
1
- /**
2
- * Utilities for generating BrowserStack configuration files.
3
- */
4
- export function generateBrowserStackYMLInstructions(desiredPlatforms, enablePercy = false, projectName) {
1
+ export function generateBrowserStackYMLInstructions(config) {
2
+ const enablePercy = config.enablePercy || false;
3
+ const projectName = config.projectName || "BrowserStack Automate Build";
4
+ // Generate platform configurations using the utility function
5
+ const platformConfigs = generatePlatformConfigs(config);
6
+ const stepTitle = "Create a browserstack.yml file in the project root with your validated device configurations:";
7
+ const buildName = `${projectName}-Build`;
5
8
  let ymlContent = `
6
9
  # ======================
7
10
  # BrowserStack Reporting
8
11
  # ======================
9
- # A single name for your project to organize all your tests. This is required for Percy.
10
- projectName: ${projectName}
12
+
11
13
  # TODO: Replace these sample values with your actual project details
12
- buildName: Sample-Build
14
+ projectName: ${projectName}
15
+ buildName: ${buildName}
13
16
 
14
17
  # =======================================
15
18
  # Platforms (Browsers / Devices to test)
16
- # =======================================
19
+ # =======================================`;
20
+ ymlContent += `
17
21
  # Platforms object contains all the browser / device combinations you want to test on.
18
- # Generate this on the basis of the following platforms requested by the user:
19
- # Requested platforms: ${desiredPlatforms}
20
22
  platforms:
21
- - os: Windows
22
- osVersion: 11
23
- browserName: chrome
24
- browserVersion: latest
25
-
23
+ ${platformConfigs}`;
24
+ ymlContent += `
25
+
26
26
  # =======================
27
27
  # Parallels per Platform
28
28
  # =======================
29
29
  # The number of parallel threads to be used for each platform set.
30
30
  # BrowserStack's SDK runner will select the best strategy based on the configured value
31
- #
32
- # Example 1 - If you have configured 3 platforms and set \`parallelsPerPlatform\` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack
33
- #
34
- # Example 2 - If you have configured 1 platform and set \`parallelsPerPlatform\` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack
31
+ # The number of parallel threads to be used for each platform set.
35
32
  parallelsPerPlatform: 1
36
33
 
37
34
  # =================
@@ -58,9 +55,59 @@ percyCaptureMode: manual`;
58
55
  }
59
56
  return `
60
57
  ---STEP---
61
- Create a browserstack.yml file in the project root. The file should be in the following format:
58
+ ${stepTitle}
62
59
 
63
60
  \`\`\`yaml${ymlContent}
64
61
  \`\`\`
65
62
  \n`;
66
63
  }
64
+ function generatePlatformConfigs(config) {
65
+ if (config.validatedEnvironments && config.validatedEnvironments.length > 0) {
66
+ // Generate platforms array from validated environments
67
+ const platforms = config.validatedEnvironments.map((env) => {
68
+ if (env.platform === "windows" || env.platform === "macos") {
69
+ // Desktop configuration
70
+ return {
71
+ os: env.platform === "windows" ? "Windows" : "OS X",
72
+ osVersion: env.osVersion,
73
+ browserName: env.browser,
74
+ browserVersion: env.browserVersion || "latest",
75
+ };
76
+ }
77
+ else {
78
+ // Mobile configuration (android/ios)
79
+ return {
80
+ deviceName: env.deviceName,
81
+ osVersion: env.osVersion,
82
+ browserName: env.browser,
83
+ };
84
+ }
85
+ });
86
+ // Convert platforms to YAML format
87
+ return platforms
88
+ .map((platform) => {
89
+ if (platform.deviceName) {
90
+ // Mobile platform
91
+ return ` - deviceName: "${platform.deviceName}"
92
+ osVersion: "${platform.osVersion}"
93
+ browserName: ${platform.browserName}`;
94
+ }
95
+ else {
96
+ // Desktop platform
97
+ return ` - os: ${platform.os}
98
+ osVersion: "${platform.osVersion}"
99
+ browserName: ${platform.browserName}
100
+ browserVersion: ${platform.browserVersion}`;
101
+ }
102
+ })
103
+ .join("\n");
104
+ }
105
+ else if (config.platforms && config.platforms.length > 0) {
106
+ // Fallback to default platforms configuration
107
+ return ` - os: Windows
108
+ osVersion: 11
109
+ browserName: chrome
110
+ browserVersion: latest`;
111
+ }
112
+ return "";
113
+ }
@@ -1,4 +1,4 @@
1
1
  import { RunTestsInstructionResult } from "../common/types.js";
2
2
  import { RunTestsOnBrowserStackInput } from "../common/schema.js";
3
3
  import { BrowserStackConfig } from "../../../lib/types.js";
4
- export declare function runBstackSDKOnly(input: RunTestsOnBrowserStackInput, config: BrowserStackConfig, isPercyAutomate?: boolean): RunTestsInstructionResult;
4
+ export declare function runBstackSDKOnly(input: RunTestsOnBrowserStackInput, config: BrowserStackConfig, isPercyAutomate?: boolean): Promise<RunTestsInstructionResult>;
@@ -2,10 +2,14 @@ import { getBrowserStackAuth } from "../../../lib/get-auth.js";
2
2
  import { getSDKPrefixCommand } from "./commands.js";
3
3
  import { generateBrowserStackYMLInstructions } from "./configUtils.js";
4
4
  import { getInstructionsForProjectConfiguration } from "../common/instructionUtils.js";
5
- export function runBstackSDKOnly(input, config, isPercyAutomate = false) {
5
+ import { validateDevices } from "../common/device-validator.js";
6
+ export async function runBstackSDKOnly(input, config, isPercyAutomate = false) {
6
7
  const steps = [];
7
8
  const authString = getBrowserStackAuth(config);
8
9
  const [username, accessKey] = authString.split(":");
10
+ // Validate devices against real BrowserStack device data
11
+ const tupleTargets = input.devices;
12
+ const validatedEnvironments = await validateDevices(tupleTargets || [], input.detectedBrowserAutomationFramework);
9
13
  // Handle frameworks with unique setup instructions that don't use browserstack.yml
10
14
  if (input.detectedBrowserAutomationFramework === "cypress" ||
11
15
  input.detectedTestingFramework === "webdriverio") {
@@ -41,7 +45,11 @@ export function runBstackSDKOnly(input, config, isPercyAutomate = false) {
41
45
  content: sdkSetupCommand,
42
46
  });
43
47
  }
44
- const ymlInstructions = generateBrowserStackYMLInstructions(input.desiredPlatforms, false, input.projectName);
48
+ const ymlInstructions = generateBrowserStackYMLInstructions({
49
+ validatedEnvironments,
50
+ enablePercy: false,
51
+ projectName: input.projectName,
52
+ });
45
53
  if (ymlInstructions) {
46
54
  steps.push({
47
55
  type: "instruction",
@@ -1,10 +1,11 @@
1
1
  export declare const IMPORTANT_SETUP_WARNING = "IMPORTANT: DO NOT SKIP ANY STEP. All the setup steps described below MUST be executed regardless of any existing configuration or setup. This ensures proper BrowserStack SDK setup.";
2
- export declare const SETUP_PERCY_DESCRIPTION = "Set up Percy visual testing for your project. This supports both Percy Web Standalone and Percy Automate.";
2
+ export declare const SETUP_PERCY_DESCRIPTION = "Set up or expand Percy visual testing configuration with comprehensive coverage for existing projects that might have Percy integrated. This supports both Percy Web Standalone and Percy Automate. Example prompts: Expand percy coverage for this project {project_name}";
3
3
  export declare const LIST_TEST_FILES_DESCRIPTION = "Lists all test files for a given set of directories.";
4
4
  export declare const PERCY_SNAPSHOT_COMMANDS_DESCRIPTION = "Adds Percy snapshot commands to the specified test files.";
5
5
  export declare const RUN_ON_BROWSERSTACK_DESCRIPTION = "Set up and run automated web-based tests on BrowserStack using the BrowserStack SDK. Use this tool for functional or integration test setup on BrowserStack only. For any visual testing or Percy integration, use the dedicated Percy setup tool. Example prompts: run this test on browserstack; set up this project for browserstack.";
6
- export declare const SIMULATE_PERCY_CHANGE_DESCRIPTION = "This tool helps new users simulate a Percy visual change by guiding them to make a visible UI modification, rerun Percy, and verify that the change is detected. Only Percy web is supported";
6
+ export declare const SIMULATE_PERCY_CHANGE_DESCRIPTION = "Integrate Percy visual testing into new projects and demonstrate visual change detection through a step-by-step simulation. This tool handles initial Percy integration, setup, and creates a working demonstration for new users. Primary tool for prompts like: \"Integrate percy for this project\", \"Integrate percy in this project {project_name}\"";
7
7
  export declare const PERCY_REPLACE_REGEX: RegExp;
8
8
  export declare const PERCY_SNAPSHOT_INSTRUCTION = "\nInvoke listTestFiles() with the provided directories from user to gather all test files in memory and obtain the generated UUID ---STEP---\nProcess files in STRICT sequential order using tool addPercySnapshotCommands() with below instructions:\n- Start with index 0\n- Then index 1 \n- Then index 2\n- Continue sequentially until the last index\n- DO NOT skip any index numbers\n- DO NOT jump to random indices like 10, 20, 30\n- DO NOT worry about time consuming and any constraints.\n- DO NOT STOP until you add commands in all the files or you reach end of the files.\n";
9
- export declare const PERCY_SIMULATE_INSTRUCTION = "\nMANDATORY: Aggressive, Accurate Percy Snapshots Integration\n\nPrerequisite: Before making any code changes in the user's repo, switch to a branch named temp-percy (run git init first if the repo is not yet initialized).\n\nYou MUST follow these steps with NO EXCEPTIONS:\n1. Identify 4-5 key test files in the project that are critical for visual testing.\n2. For EACH identified test file, analyze the code to find EVERY location where a Percy snapshot should be called (e.g., after each navigation, UI change, or significant state in tests).\n3. Insert snapshot calls at ALL such points, not just the first or most obvious one.\n\nIteratively update every selected test file with percy snapshot calls at all identified points, ensuring comprehensive coverage of visual changes.";
10
- export declare const PERCY_SIMULATION_DRIVER_INSTRUCTION = "\nMANDATORY: Step-by-Step Percy Visual Change Simulation\nYou MUST follow these steps with NO EXCEPTIONS:\n\nSTEP 1: At this case we have added Percy snapshot commands to the test files at the appropriate locations (e.g., after navigation, UI change, or significant state). \n- Use ONLY existing snapshot names.\n- Do NOT add any popup injection or visual changes yet.\n- Do NOT run any Percy builds at this stage.\n\nSTEP 2: Run ONE comprehensive baseline Percy build that executes ALL tests containing Percy snapshots in a SINGLE build. This creates one baseline build with all snapshots for comparison. Use a command like: npx percy exec -- python -m pytest tests/ -k 'test_name1 or test_name2 or test_name3' -v to run multiple specific tests in one build.\"\n\nSTEP 3: Modify your test to inject a visible UI change (such as a popup) IMMEDIATELY BEFORE an EXISTING snapshot command (e.g., before percy_snapshot(self.driver, \"screenshot name\")).\n- Do NOT add a new snapshot name for the popup.\n- The popup must appear in an existing snapshot, not a new one.\n- Add this popup code in some test files before the percy_snapshot command you've added, to display the visual changes.\n\n```Javascript\npopup_script = `\nvar popup = document.createElement('div');\npopup.id = 'percy-test-popup';\npopup.style.cssText = popup.style.cssText = `\n /* TODO: Add styles to make the popup large, centered, and visually noticeable.\n Suggested properties: position: fixed; top/left; transform; background; color; font-size; padding; z-index; animation, etc. */\n`;\npopup.innerHTML = 'PERCY TEST<br>VISUAL CHANGE<br>DETECTED!';\ndocument.body.appendChild(popup);\n`;\n\n# Insert this just before the EXISTING snapshot command:\ndriver.execute_script(popup_script)\npercy_snapshot(self.driver, \"Before Adding to Cart\") # (Do NOT change the snapshot name, keep existing one)\n```\n\nSTEP 4: Run a second Percy build.\n- The snapshot names must remain the same as in the baseline.\n- The visual change should now appear in the same snapshot as before.\n- Use the same build command you ran for the baseline.\n\nSTEP 5: Compare the two Percy builds to see the detected visual difference.\n\nSTEP 6: Now ask user if they want to setup percy for full project coverage? If yes, call the \"setupPercyVisualTesting\" tool to enable complete coverage for the entire project.\n\nCONSTRAINTS:\n- Do NOT run any builds until explicitly instructed in the steps.\n- Do NOT add new snapshot names\u2014only use existing ones.\n- Do NOT add popup injection until the baseline is established.\n- Visual changes must appear in EXISTING snapshots, not new ones.\n\nVALIDATION CHECKPOINTS (before proceeding to the next step):\n- Are you adding only snapshot commands (not running builds)?\n- Are you reusing existing snapshot names (not creating new ones)?\n- Have you established the baseline first (before adding visual changes)\n\nCRITICAL: \nDo NOT run tests separately or create multiple builds during baseline establishment. The goal is to have exactly TWO builds total: (1) baseline build with all original snapshots, (2) modified build with the same tests but visual changes injected.\n";
9
+ export declare const PERCY_SIMULATE_INSTRUCTION = "\nMANDATORY: Aggressive, Accurate Percy Snapshots Integration\n\nPrerequisite: Before making any code changes in the user's repository, create and switch to a new branch named browserstack-simulation. Stage all changes but do not commit them. If the repository has not been initialized yet, run git init first.\n\nYou MUST follow these steps with NO EXCEPTIONS:\n1. Identify 4-5 key test files in the project that are critical for visual testing.\n2. For EACH identified test file, analyze the code to find EVERY location where a Percy snapshot should be called (e.g., after each navigation, UI change, or significant state in tests).\n3. Insert snapshot calls at ALL such points, not just the first or most obvious one.\n\nIteratively update every selected test file with percy snapshot calls at all identified points, ensuring comprehensive coverage of visual changes.";
10
+ export declare const PERCY_SIMULATION_DRIVER_INSTRUCTION = "\nMANDATORY: Step-by-Step Percy Visual Change Simulation\nYou MUST follow these steps with NO EXCEPTIONS:\n\nSTEP 1: At this case we have added Percy snapshot commands to the test files at the appropriate locations (e.g., after navigation, UI change, or significant state). \n- Use ONLY existing snapshot names.\n- Do NOT add any popup injection or visual changes yet.\n- Do NOT run any Percy builds at this stage.\n\nSTEP 2: Run ONE comprehensive baseline Percy build that executes ALL tests containing Percy snapshots in a SINGLE build. This creates one baseline build with all snapshots for comparison. Use a command like: npx percy exec --label=mcp -- python -m pytest tests/ -k 'test_name1 or test_name2 or test_name3' -v to run multiple specific tests in one build.\"\n\nSTEP 3: Modify your test to inject a visible UI change (such as a popup) IMMEDIATELY BEFORE an EXISTING snapshot command (e.g., before percy_snapshot(self.driver, \"screenshot name\")).\n- Do NOT add a new snapshot name for the popup.\n- The popup must appear in an existing snapshot, not a new one.\n- Add this popup code in some test files before the percy_snapshot command you've added, to display the visual changes.\n\n```Javascript\npopup_script = `\nvar popup = document.createElement('div');\npopup.id = 'percy-test-popup';\npopup.style.cssText = popup.style.cssText = `\n /* TODO: Add styles to make the popup large, centered, and visually noticeable.\n Suggested properties: position: fixed; top/left; transform; background; color; font-size; padding; z-index; animation, etc. */\n`;\npopup.innerHTML = 'PERCY TEST<br>VISUAL CHANGE<br>DETECTED!';\ndocument.body.appendChild(popup);\n`;\n\n# Insert this just before the EXISTING snapshot command:\ndriver.execute_script(popup_script)\npercy_snapshot(self.driver, \"Before Adding to Cart\") # (Do NOT change the snapshot name, keep existing one)\n```\n\nSTEP 4: Run a second Percy build with same label and same test command as the baseline.\n- The snapshot names must remain the same as in the baseline.\n- The visual change should now appear in the same snapshot as before.\n- Use the same build command you ran for the baseline.\n\nSTEP 5: Compare the two Percy builds to see the detected visual difference.\n\nSTEP 6: Now ask user if they want to expand percy for other testcases? If yes, call the \"expandPercyVisualTesting\" tool to enable complete coverage for the entire project.\n\nCONSTRAINTS:\n- Do NOT run any builds until explicitly instructed in the steps.\n- Do NOT add new snapshot names\u2014only use existing ones.\n- Do NOT add popup injection until the baseline is established.\n- Visual changes must appear in EXISTING snapshots, not new ones.\n\nVALIDATION CHECKPOINTS (before proceeding to the next step):\n- Are you adding only snapshot commands (not running builds)?\n- Are you reusing existing snapshot names (not creating new ones)?\n- Have you established the baseline first (before adding visual changes)\n\nCRITICAL: \nDo NOT run tests separately or create multiple builds during baseline establishment. The goal is to have exactly TWO builds total: (1) baseline build with all original snapshots, (2) modified build with the same tests but visual changes injected.\n";
11
+ export declare const PERCY_VERIFICATION_REGEX: RegExp;
@@ -1,9 +1,9 @@
1
1
  export const IMPORTANT_SETUP_WARNING = "IMPORTANT: DO NOT SKIP ANY STEP. All the setup steps described below MUST be executed regardless of any existing configuration or setup. This ensures proper BrowserStack SDK setup.";
2
- export const SETUP_PERCY_DESCRIPTION = "Set up Percy visual testing for your project. This supports both Percy Web Standalone and Percy Automate.";
2
+ export const SETUP_PERCY_DESCRIPTION = "Set up or expand Percy visual testing configuration with comprehensive coverage for existing projects that might have Percy integrated. This supports both Percy Web Standalone and Percy Automate. Example prompts: Expand percy coverage for this project {project_name}";
3
3
  export const LIST_TEST_FILES_DESCRIPTION = "Lists all test files for a given set of directories.";
4
4
  export const PERCY_SNAPSHOT_COMMANDS_DESCRIPTION = "Adds Percy snapshot commands to the specified test files.";
5
5
  export const RUN_ON_BROWSERSTACK_DESCRIPTION = "Set up and run automated web-based tests on BrowserStack using the BrowserStack SDK. Use this tool for functional or integration test setup on BrowserStack only. For any visual testing or Percy integration, use the dedicated Percy setup tool. Example prompts: run this test on browserstack; set up this project for browserstack.";
6
- export const SIMULATE_PERCY_CHANGE_DESCRIPTION = "This tool helps new users simulate a Percy visual change by guiding them to make a visible UI modification, rerun Percy, and verify that the change is detected. Only Percy web is supported";
6
+ export const SIMULATE_PERCY_CHANGE_DESCRIPTION = `Integrate Percy visual testing into new projects and demonstrate visual change detection through a step-by-step simulation. This tool handles initial Percy integration, setup, and creates a working demonstration for new users. Primary tool for prompts like: "Integrate percy for this project", "Integrate percy in this project {project_name}"`;
7
7
  export const PERCY_REPLACE_REGEX = /Invoke listTestFiles\(\) with the provided directories[\s\S]*?- DO NOT STOP until you add commands in all the files or you reach end of the files\./;
8
8
  export const PERCY_SNAPSHOT_INSTRUCTION = `
9
9
  Invoke listTestFiles() with the provided directories from user to gather all test files in memory and obtain the generated UUID ---STEP---
@@ -20,7 +20,7 @@ Process files in STRICT sequential order using tool addPercySnapshotCommands() w
20
20
  export const PERCY_SIMULATE_INSTRUCTION = `
21
21
  MANDATORY: Aggressive, Accurate Percy Snapshots Integration
22
22
 
23
- Prerequisite: Before making any code changes in the user's repo, switch to a branch named temp-percy (run git init first if the repo is not yet initialized).
23
+ Prerequisite: Before making any code changes in the user's repository, create and switch to a new branch named browserstack-simulation. Stage all changes but do not commit them. If the repository has not been initialized yet, run git init first.
24
24
 
25
25
  You MUST follow these steps with NO EXCEPTIONS:
26
26
  1. Identify 4-5 key test files in the project that are critical for visual testing.
@@ -37,7 +37,7 @@ STEP 1: At this case we have added Percy snapshot commands to the test files at
37
37
  - Do NOT add any popup injection or visual changes yet.
38
38
  - Do NOT run any Percy builds at this stage.
39
39
 
40
- STEP 2: Run ONE comprehensive baseline Percy build that executes ALL tests containing Percy snapshots in a SINGLE build. This creates one baseline build with all snapshots for comparison. Use a command like: npx percy exec -- python -m pytest tests/ -k 'test_name1 or test_name2 or test_name3' -v to run multiple specific tests in one build."
40
+ STEP 2: Run ONE comprehensive baseline Percy build that executes ALL tests containing Percy snapshots in a SINGLE build. This creates one baseline build with all snapshots for comparison. Use a command like: npx percy exec --label=mcp -- python -m pytest tests/ -k 'test_name1 or test_name2 or test_name3' -v to run multiple specific tests in one build."
41
41
 
42
42
  STEP 3: Modify your test to inject a visible UI change (such as a popup) IMMEDIATELY BEFORE an EXISTING snapshot command (e.g., before percy_snapshot(self.driver, "screenshot name")).
43
43
  - Do NOT add a new snapshot name for the popup.
@@ -61,14 +61,14 @@ driver.execute_script(popup_script)
61
61
  percy_snapshot(self.driver, "Before Adding to Cart") # (Do NOT change the snapshot name, keep existing one)
62
62
  \`\`\`
63
63
 
64
- STEP 4: Run a second Percy build.
64
+ STEP 4: Run a second Percy build with same label and same test command as the baseline.
65
65
  - The snapshot names must remain the same as in the baseline.
66
66
  - The visual change should now appear in the same snapshot as before.
67
67
  - Use the same build command you ran for the baseline.
68
68
 
69
69
  STEP 5: Compare the two Percy builds to see the detected visual difference.
70
70
 
71
- STEP 6: Now ask user if they want to setup percy for full project coverage? If yes, call the "setupPercyVisualTesting" tool to enable complete coverage for the entire project.
71
+ STEP 6: Now ask user if they want to expand percy for other testcases? If yes, call the "expandPercyVisualTesting" tool to enable complete coverage for the entire project.
72
72
 
73
73
  CONSTRAINTS:
74
74
  - Do NOT run any builds until explicitly instructed in the steps.
@@ -84,3 +84,4 @@ VALIDATION CHECKPOINTS (before proceeding to the next step):
84
84
  CRITICAL:
85
85
  Do NOT run tests separately or create multiple builds during baseline establishment. The goal is to have exactly TWO builds total: (1) baseline build with all original snapshots, (2) modified build with the same tests but visual changes injected.
86
86
  `;
87
+ export const PERCY_VERIFICATION_REGEX = /\*\*✅ Verification:\*\*\nPlease verify that you have completed all[\s\S]*?double-check each step and ensure all commands executed successfully\./s;
@@ -0,0 +1,25 @@
1
+ export interface DesktopBrowserEntry {
2
+ os: string;
3
+ os_version: string;
4
+ browser: string;
5
+ browser_version: string;
6
+ }
7
+ export interface MobileDeviceEntry {
8
+ os: "android" | "ios";
9
+ os_version: string;
10
+ display_name: string;
11
+ browsers?: Array<{
12
+ browser: string;
13
+ display_name?: string;
14
+ }>;
15
+ }
16
+ export interface ValidatedEnvironment {
17
+ platform: string;
18
+ osVersion: string;
19
+ browser?: string;
20
+ browserVersion?: string;
21
+ deviceName?: string;
22
+ notes?: string;
23
+ }
24
+ export declare function validateDevices(devices: Array<Array<string>>, framework?: string): Promise<ValidatedEnvironment[]>;
25
+ export declare function validateAppAutomateDevices(devices: Array<Array<string>>): Promise<ValidatedEnvironment[]>;