@browserstack/mcp-server 1.2.2 → 1.2.3-beta.1
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/README.md +225 -0
- package/dist/lib/instrumentation.js +2 -0
- package/dist/server-factory.js +1 -1
- package/dist/tools/accessibility.js +238 -78
- package/dist/tools/accessiblity-utils/auth-config.d.ts +39 -0
- package/dist/tools/accessiblity-utils/auth-config.js +125 -0
- package/dist/tools/accessiblity-utils/scanner.d.ts +1 -1
- package/dist/tools/accessiblity-utils/scanner.js +2 -1
- package/dist/tools/appautomate-utils/appium-sdk/config-generator.d.ts +1 -0
- package/dist/tools/appautomate-utils/appium-sdk/config-generator.js +50 -0
- package/dist/tools/appautomate-utils/appium-sdk/constants.d.ts +23 -0
- package/dist/tools/appautomate-utils/appium-sdk/constants.js +43 -0
- package/dist/tools/appautomate-utils/appium-sdk/formatter.d.ts +8 -0
- package/dist/tools/appautomate-utils/appium-sdk/formatter.js +59 -0
- package/dist/tools/appautomate-utils/appium-sdk/handler.d.ts +3 -0
- package/dist/tools/appautomate-utils/appium-sdk/handler.js +52 -0
- package/dist/tools/appautomate-utils/appium-sdk/index.d.ts +7 -0
- package/dist/tools/appautomate-utils/appium-sdk/index.js +8 -0
- package/dist/tools/appautomate-utils/appium-sdk/instructions.d.ts +3 -0
- package/dist/tools/appautomate-utils/appium-sdk/instructions.js +47 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/csharp.d.ts +2 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/csharp.js +78 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/java.d.ts +8 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/java.js +87 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/nodejs.d.ts +3 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/nodejs.js +194 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/python.d.ts +3 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/python.js +76 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/ruby.d.ts +2 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/ruby.js +85 -0
- package/dist/tools/appautomate-utils/appium-sdk/types.d.ts +57 -0
- package/dist/tools/appautomate-utils/appium-sdk/types.js +61 -0
- package/dist/tools/appautomate-utils/appium-sdk/utils.d.ts +17 -0
- package/dist/tools/appautomate-utils/appium-sdk/utils.js +61 -0
- package/dist/tools/appautomate-utils/{appautomate.d.ts → native-execution/appautomate.d.ts} +1 -1
- package/dist/tools/appautomate-utils/{appautomate.js → native-execution/appautomate.js} +2 -2
- package/dist/tools/appautomate-utils/native-execution/constants.d.ts +10 -0
- package/dist/tools/appautomate-utils/native-execution/constants.js +36 -0
- package/dist/tools/appautomate-utils/native-execution/types.d.ts +19 -0
- package/dist/tools/appautomate-utils/{types.js → native-execution/types.js} +5 -1
- package/dist/tools/appautomate.js +25 -40
- package/dist/tools/sdk-utils/constants.js +10 -0
- package/package.json +1 -1
- package/dist/tools/appautomate-utils/types.d.ts +0 -5
- /package/dist/tools/{getFailureLogs.d.ts → get-failure-logs.d.ts} +0 -0
- /package/dist/tools/{getFailureLogs.js → get-failure-logs.js} +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { apiClient } from "../../lib/apiClient.js";
|
|
2
|
+
import logger from "../../logger.js";
|
|
3
|
+
export class AccessibilityAuthConfig {
|
|
4
|
+
auth;
|
|
5
|
+
setAuth(auth) {
|
|
6
|
+
this.auth = auth;
|
|
7
|
+
}
|
|
8
|
+
transformLocalUrl(url) {
|
|
9
|
+
try {
|
|
10
|
+
const parsed = new URL(url);
|
|
11
|
+
const localHosts = new Set(["127.0.0.1", "localhost", "0.0.0.0"]);
|
|
12
|
+
const BS_LOCAL_DOMAIN = "bs-local.com";
|
|
13
|
+
if (localHosts.has(parsed.hostname)) {
|
|
14
|
+
parsed.hostname = BS_LOCAL_DOMAIN;
|
|
15
|
+
return parsed.toString();
|
|
16
|
+
}
|
|
17
|
+
return url;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return url;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async createFormAuthConfig(name, authData) {
|
|
24
|
+
if (!this.auth?.username || !this.auth?.password) {
|
|
25
|
+
throw new Error("BrowserStack credentials are not set for AccessibilityAuthConfig.");
|
|
26
|
+
}
|
|
27
|
+
const transformedAuthData = {
|
|
28
|
+
...authData,
|
|
29
|
+
url: this.transformLocalUrl(authData.url),
|
|
30
|
+
};
|
|
31
|
+
const requestBody = {
|
|
32
|
+
name,
|
|
33
|
+
type: "form",
|
|
34
|
+
authData: transformedAuthData,
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
const response = await apiClient.post({
|
|
38
|
+
url: "https://api-accessibility.browserstack.com/api/website-scanner/v1/auth_configs",
|
|
39
|
+
headers: {
|
|
40
|
+
Authorization: "Basic " +
|
|
41
|
+
Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
},
|
|
44
|
+
body: requestBody,
|
|
45
|
+
});
|
|
46
|
+
const data = response.data;
|
|
47
|
+
logger.info(`The data returned from the API is: ${JSON.stringify(data)}`);
|
|
48
|
+
if (!data.success) {
|
|
49
|
+
throw new Error(`Unable to create auth config: ${data.errors?.join(", ")}`);
|
|
50
|
+
}
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
logger.error(`Error creating form auth config: ${JSON.stringify(err?.response?.data)}`);
|
|
55
|
+
const msg = err?.response?.data?.error ||
|
|
56
|
+
err?.response?.data?.message ||
|
|
57
|
+
err?.message ||
|
|
58
|
+
String(err);
|
|
59
|
+
throw new Error(`Failed to create form auth config: ${msg}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async createBasicAuthConfig(name, authData) {
|
|
63
|
+
if (!this.auth?.username || !this.auth?.password) {
|
|
64
|
+
throw new Error("BrowserStack credentials are not set for AccessibilityAuthConfig.");
|
|
65
|
+
}
|
|
66
|
+
const transformedAuthData = {
|
|
67
|
+
...authData,
|
|
68
|
+
url: this.transformLocalUrl(authData.url),
|
|
69
|
+
};
|
|
70
|
+
const requestBody = {
|
|
71
|
+
name,
|
|
72
|
+
type: "basic",
|
|
73
|
+
authData: transformedAuthData,
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
const response = await apiClient.post({
|
|
77
|
+
url: "https://api-accessibility.browserstack.com/api/website-scanner/v1/auth_configs",
|
|
78
|
+
headers: {
|
|
79
|
+
Authorization: "Basic " +
|
|
80
|
+
Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
|
|
81
|
+
"Content-Type": "application/json",
|
|
82
|
+
},
|
|
83
|
+
body: requestBody,
|
|
84
|
+
});
|
|
85
|
+
const data = response.data;
|
|
86
|
+
if (!data.success) {
|
|
87
|
+
throw new Error(`Unable to create auth config: ${data.errors?.join(", ")}`);
|
|
88
|
+
}
|
|
89
|
+
return data;
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
const msg = err?.response?.data?.error ||
|
|
93
|
+
err?.response?.data?.message ||
|
|
94
|
+
err?.message ||
|
|
95
|
+
String(err);
|
|
96
|
+
throw new Error(`Failed to create basic auth config: ${msg}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async getAuthConfig(configId) {
|
|
100
|
+
if (!this.auth?.username || !this.auth?.password) {
|
|
101
|
+
throw new Error("BrowserStack credentials are not set for AccessibilityAuthConfig.");
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const response = await apiClient.get({
|
|
105
|
+
url: `https://api-accessibility.browserstack.com/api/website-scanner/v1/auth_configs/${configId}`,
|
|
106
|
+
headers: {
|
|
107
|
+
Authorization: "Basic " +
|
|
108
|
+
Buffer.from(`${this.auth.username}:${this.auth.password}`).toString("base64"),
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
const data = response.data;
|
|
112
|
+
if (!data.success) {
|
|
113
|
+
throw new Error(`Unable to get auth config: ${data.errors?.join(", ")}`);
|
|
114
|
+
}
|
|
115
|
+
return data;
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const msg = err?.response?.data?.error ||
|
|
119
|
+
err?.response?.data?.message ||
|
|
120
|
+
err?.message ||
|
|
121
|
+
String(err);
|
|
122
|
+
throw new Error(`Failed to get auth config: ${msg}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -19,7 +19,7 @@ export declare class AccessibilityScanner {
|
|
|
19
19
|
username: string;
|
|
20
20
|
password: string;
|
|
21
21
|
}): void;
|
|
22
|
-
startScan(name: string, urlList: string[]): Promise<AccessibilityScanResponse>;
|
|
22
|
+
startScan(name: string, urlList: string[], authConfigId?: number): Promise<AccessibilityScanResponse>;
|
|
23
23
|
pollStatus(scanId: string, scanRunId: string): Promise<AccessibilityScanStatus>;
|
|
24
24
|
waitUntilComplete(scanId: string, scanRunId: string, context: any): Promise<string>;
|
|
25
25
|
}
|
|
@@ -8,7 +8,7 @@ export class AccessibilityScanner {
|
|
|
8
8
|
setAuth(auth) {
|
|
9
9
|
this.auth = auth;
|
|
10
10
|
}
|
|
11
|
-
async startScan(name, urlList) {
|
|
11
|
+
async startScan(name, urlList, authConfigId) {
|
|
12
12
|
if (!this.auth?.username || !this.auth?.password) {
|
|
13
13
|
throw new Error("BrowserStack credentials are not set for AccessibilityScanner.");
|
|
14
14
|
}
|
|
@@ -47,6 +47,7 @@ export class AccessibilityScanner {
|
|
|
47
47
|
name,
|
|
48
48
|
urlList: transformedUrlList,
|
|
49
49
|
recurring: false,
|
|
50
|
+
...(authConfigId && { authConfigId }),
|
|
50
51
|
};
|
|
51
52
|
let requestBody = baseRequestBody;
|
|
52
53
|
if (hasLocal) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function generateAppBrowserStackYMLInstructions(platforms: string[], username: string, accessKey: string, appPath: string | undefined, testingFramework: string): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Configuration utilities for BrowserStack App SDK
|
|
2
|
+
import { APP_DEVICE_CONFIGS, AppSDKSupportedTestingFrameworkEnum, DEFAULT_APP_PATH, createStep, } from "./index.js";
|
|
3
|
+
export function generateAppBrowserStackYMLInstructions(platforms, username, accessKey, appPath = DEFAULT_APP_PATH, testingFramework) {
|
|
4
|
+
if (testingFramework === AppSDKSupportedTestingFrameworkEnum.nightwatch ||
|
|
5
|
+
testingFramework === AppSDKSupportedTestingFrameworkEnum.webdriverio ||
|
|
6
|
+
testingFramework === AppSDKSupportedTestingFrameworkEnum.cucumberRuby) {
|
|
7
|
+
return "";
|
|
8
|
+
}
|
|
9
|
+
// Generate platform and device configurations
|
|
10
|
+
const platformConfigs = platforms
|
|
11
|
+
.map((platform) => {
|
|
12
|
+
const devices = APP_DEVICE_CONFIGS[platform];
|
|
13
|
+
if (!devices)
|
|
14
|
+
return "";
|
|
15
|
+
return devices
|
|
16
|
+
.map((device) => ` - platformName: ${platform}
|
|
17
|
+
deviceName: ${device.deviceName}
|
|
18
|
+
platformVersion: "${device.platformVersion}"`)
|
|
19
|
+
.join("\n");
|
|
20
|
+
})
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.join("\n");
|
|
23
|
+
// Construct YAML content
|
|
24
|
+
const configContent = `\`\`\`yaml
|
|
25
|
+
userName: ${username}
|
|
26
|
+
accessKey: ${accessKey}
|
|
27
|
+
app: ${appPath}
|
|
28
|
+
platforms:
|
|
29
|
+
${platformConfigs}
|
|
30
|
+
parallelsPerPlatform: 1
|
|
31
|
+
browserstackLocal: true
|
|
32
|
+
buildName: bstack-demo
|
|
33
|
+
projectName: BrowserStack Sample
|
|
34
|
+
debug: true
|
|
35
|
+
networkLogs: true
|
|
36
|
+
percy: false
|
|
37
|
+
percyCaptureMode: auto
|
|
38
|
+
accessibility: false
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
**Important notes:**
|
|
42
|
+
- Replace \`app: ${appPath}\` with the path to your actual app file (e.g., \`./SampleApp.apk\` for Android or \`./SampleApp.ipa\` for iOS)
|
|
43
|
+
- You can upload your app using BrowserStack's App Upload API or manually through the dashboard
|
|
44
|
+
- Set \`browserstackLocal: true\` if you need to test with local/staging servers
|
|
45
|
+
- Adjust \`parallelsPerPlatform\` based on your subscription limits`;
|
|
46
|
+
// Return formatted step for instructions
|
|
47
|
+
return createStep("Update browserstack.yml file with App Automate configuration:", `Create or update the browserstack.yml file in your project root with the following content:
|
|
48
|
+
|
|
49
|
+
${configContent}`);
|
|
50
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { AppSDKSupportedFrameworkEnum, AppSDKSupportedTestingFrameworkEnum, AppSDKSupportedLanguageEnum, AppSDKSupportedPlatformEnum } from "./index.js";
|
|
3
|
+
export declare const APP_DEVICE_CONFIGS: {
|
|
4
|
+
android: {
|
|
5
|
+
deviceName: string;
|
|
6
|
+
platformVersion: string;
|
|
7
|
+
}[];
|
|
8
|
+
ios: {
|
|
9
|
+
deviceName: string;
|
|
10
|
+
platformVersion: string;
|
|
11
|
+
}[];
|
|
12
|
+
};
|
|
13
|
+
export declare const STEP_DELIMITER = "---STEP---";
|
|
14
|
+
export declare const DEFAULT_APP_PATH = "bs://sample.app";
|
|
15
|
+
export declare const SETUP_APP_AUTOMATE_DESCRIPTION = "Set up BrowserStack App Automate SDK integration for Appium-based mobile app testing. ONLY for Appium based framework . This tool configures SDK for various languages with appium. For pre-built Espresso or XCUITest test suites, use 'runAppTestsOnBrowserStack' instead.";
|
|
16
|
+
export declare const SETUP_APP_AUTOMATE_SCHEMA: {
|
|
17
|
+
detectedFramework: z.ZodNativeEnum<typeof AppSDKSupportedFrameworkEnum>;
|
|
18
|
+
detectedTestingFramework: z.ZodNativeEnum<typeof AppSDKSupportedTestingFrameworkEnum>;
|
|
19
|
+
detectedLanguage: z.ZodNativeEnum<typeof AppSDKSupportedLanguageEnum>;
|
|
20
|
+
desiredPlatforms: z.ZodArray<z.ZodNativeEnum<typeof AppSDKSupportedPlatformEnum>, "many">;
|
|
21
|
+
appPath: z.ZodString;
|
|
22
|
+
project: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
23
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { AppSDKSupportedFrameworkEnum, AppSDKSupportedTestingFrameworkEnum, AppSDKSupportedLanguageEnum, AppSDKSupportedPlatformEnum, } from "./index.js";
|
|
3
|
+
// App Automate specific device configurations
|
|
4
|
+
export const APP_DEVICE_CONFIGS = {
|
|
5
|
+
android: [
|
|
6
|
+
{ deviceName: "Samsung Galaxy S22 Ultra", platformVersion: "12.0" },
|
|
7
|
+
{ deviceName: "Google Pixel 7 Pro", platformVersion: "13.0" },
|
|
8
|
+
{ deviceName: "OnePlus 9", platformVersion: "11.0" },
|
|
9
|
+
],
|
|
10
|
+
ios: [
|
|
11
|
+
{ deviceName: "iPhone 14", platformVersion: "16" },
|
|
12
|
+
{ deviceName: "iPhone 13", platformVersion: "15" },
|
|
13
|
+
{ deviceName: "iPad Air 4", platformVersion: "14" },
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
// Step delimiter for parsing instructions
|
|
17
|
+
export const STEP_DELIMITER = "---STEP---";
|
|
18
|
+
// Default app path for examples
|
|
19
|
+
export const DEFAULT_APP_PATH = "bs://sample.app";
|
|
20
|
+
// Tool description and schema for setupBrowserStackAppAutomateTests
|
|
21
|
+
export const SETUP_APP_AUTOMATE_DESCRIPTION = "Set up BrowserStack App Automate SDK integration for Appium-based mobile app testing. ONLY for Appium based framework . This tool configures SDK for various languages with appium. For pre-built Espresso or XCUITest test suites, use 'runAppTestsOnBrowserStack' instead.";
|
|
22
|
+
export const SETUP_APP_AUTOMATE_SCHEMA = {
|
|
23
|
+
detectedFramework: z
|
|
24
|
+
.nativeEnum(AppSDKSupportedFrameworkEnum)
|
|
25
|
+
.describe("The mobile automation framework configured in the project. Example: 'appium'"),
|
|
26
|
+
detectedTestingFramework: z
|
|
27
|
+
.nativeEnum(AppSDKSupportedTestingFrameworkEnum)
|
|
28
|
+
.describe("The testing framework used in the project. Be precise with framework selection Example: 'testng', 'behave', 'pytest', 'robot'"),
|
|
29
|
+
detectedLanguage: z
|
|
30
|
+
.nativeEnum(AppSDKSupportedLanguageEnum)
|
|
31
|
+
.describe("The programming language used in the project. Supports Java and C#. Example: 'java', 'csharp'"),
|
|
32
|
+
desiredPlatforms: z
|
|
33
|
+
.array(z.nativeEnum(AppSDKSupportedPlatformEnum))
|
|
34
|
+
.describe("The mobile platforms the user wants to test on. Always ask this to the user, do not try to infer this. Example: ['android', 'ios']"),
|
|
35
|
+
appPath: z
|
|
36
|
+
.string()
|
|
37
|
+
.describe("Path to the mobile app file (.apk for Android, .ipa for iOS). Can be a local file path or a BrowserStack app URL (bs://)"),
|
|
38
|
+
project: z
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.default("BStack-AppAutomate-Suite")
|
|
42
|
+
.describe("Project name for organizing test runs on BrowserStack."),
|
|
43
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export declare function formatFinalAppInstructions(formattedInstructions: string): CallToolResult;
|
|
3
|
+
export declare function createStep(title: string, content: string): string;
|
|
4
|
+
export declare function combineInstructions(...instructionParts: string[]): string;
|
|
5
|
+
export declare function formatEnvCommands(username: string, accessKey: string, isWindows: boolean): string;
|
|
6
|
+
export declare function createEnvStep(username: string, accessKey: string, isWindows: boolean, platformLabel: string, title?: string): string;
|
|
7
|
+
export declare function formatMultiLineCommand(command: string, isWindows?: boolean): string;
|
|
8
|
+
export declare function formatAppInstructionsWithNumbers(instructions: string): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { STEP_DELIMITER } from "./constants.js";
|
|
2
|
+
export function formatFinalAppInstructions(formattedInstructions) {
|
|
3
|
+
const fullInstructions = `
|
|
4
|
+
⚠️ IMPORTANT: DO NOT SKIP ANY STEP
|
|
5
|
+
All the setup steps described in this file MUST be executed regardless of any existing configuration or setup.
|
|
6
|
+
This ensures proper BrowserStack App Automate SDK setup.
|
|
7
|
+
Each step is compulsory and sequence needs to be maintained.
|
|
8
|
+
|
|
9
|
+
${formattedInstructions}`;
|
|
10
|
+
return {
|
|
11
|
+
content: [
|
|
12
|
+
{
|
|
13
|
+
type: "text",
|
|
14
|
+
text: fullInstructions,
|
|
15
|
+
isError: false,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function createStep(title, content) {
|
|
21
|
+
return `${STEP_DELIMITER}
|
|
22
|
+
${title}
|
|
23
|
+
|
|
24
|
+
${content}`;
|
|
25
|
+
}
|
|
26
|
+
export function combineInstructions(...instructionParts) {
|
|
27
|
+
return instructionParts.filter(Boolean).join("\n\n");
|
|
28
|
+
}
|
|
29
|
+
export function formatEnvCommands(username, accessKey, isWindows) {
|
|
30
|
+
if (isWindows) {
|
|
31
|
+
return `\`\`\`cmd
|
|
32
|
+
setx BROWSERSTACK_USERNAME "${username}"
|
|
33
|
+
setx BROWSERSTACK_ACCESS_KEY "${accessKey}"
|
|
34
|
+
\`\`\``;
|
|
35
|
+
}
|
|
36
|
+
return `\`\`\`bash
|
|
37
|
+
export BROWSERSTACK_USERNAME=${username}
|
|
38
|
+
export BROWSERSTACK_ACCESS_KEY=${accessKey}
|
|
39
|
+
\`\`\``;
|
|
40
|
+
}
|
|
41
|
+
export function createEnvStep(username, accessKey, isWindows, platformLabel, title = "Set BrowserStack credentials as environment variables:") {
|
|
42
|
+
return createStep(title, `**${platformLabel}:**
|
|
43
|
+
${formatEnvCommands(username, accessKey, isWindows)}`);
|
|
44
|
+
}
|
|
45
|
+
export function formatMultiLineCommand(command, isWindows = process.platform === "win32") {
|
|
46
|
+
if (isWindows) {
|
|
47
|
+
// For Windows, keep commands on single line
|
|
48
|
+
return command.replace(/\s*\\\s*\n\s*/g, " ");
|
|
49
|
+
}
|
|
50
|
+
return command;
|
|
51
|
+
}
|
|
52
|
+
export function formatAppInstructionsWithNumbers(instructions) {
|
|
53
|
+
const steps = instructions
|
|
54
|
+
.split(STEP_DELIMITER)
|
|
55
|
+
.filter((step) => step.trim());
|
|
56
|
+
return steps
|
|
57
|
+
.map((step, index) => `**Step ${index + 1}:**\n${step.trim()}`)
|
|
58
|
+
.join("\n\n");
|
|
59
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getBrowserStackAuth } from "../../../lib/get-auth.js";
|
|
3
|
+
import { getAppUploadInstruction, validateSupportforAppAutomate, } from "./utils.js";
|
|
4
|
+
import { getAppSDKPrefixCommand, generateAppBrowserStackYMLInstructions, } from "./index.js";
|
|
5
|
+
import { formatAppInstructionsWithNumbers, getAppInstructionsForProjectConfiguration, SETUP_APP_AUTOMATE_SCHEMA, } from "./index.js";
|
|
6
|
+
export async function setupAppAutomateHandler(rawInput, config) {
|
|
7
|
+
const input = z.object(SETUP_APP_AUTOMATE_SCHEMA).parse(rawInput);
|
|
8
|
+
const auth = getBrowserStackAuth(config);
|
|
9
|
+
const [username, accessKey] = auth.split(":");
|
|
10
|
+
const instructions = [];
|
|
11
|
+
// Use variables for all major input properties
|
|
12
|
+
const testingFramework = input.detectedTestingFramework;
|
|
13
|
+
const language = input.detectedLanguage;
|
|
14
|
+
const platforms = input.desiredPlatforms ?? ["android"];
|
|
15
|
+
const appPath = input.appPath;
|
|
16
|
+
const framework = input.detectedFramework;
|
|
17
|
+
//Validating if supported framework or not
|
|
18
|
+
validateSupportforAppAutomate(framework, language, testingFramework);
|
|
19
|
+
// Step 1: Generate SDK setup command
|
|
20
|
+
const sdkCommand = getAppSDKPrefixCommand(language, testingFramework, username, accessKey, appPath);
|
|
21
|
+
if (sdkCommand) {
|
|
22
|
+
instructions.push({ content: sdkCommand, type: "setup" });
|
|
23
|
+
}
|
|
24
|
+
// Step 2: Generate browserstack.yml configuration
|
|
25
|
+
const configInstructions = generateAppBrowserStackYMLInstructions(platforms, username, accessKey, appPath, testingFramework);
|
|
26
|
+
if (configInstructions) {
|
|
27
|
+
instructions.push({ content: configInstructions, type: "config" });
|
|
28
|
+
}
|
|
29
|
+
// Step 3: Generate app upload instruction
|
|
30
|
+
const appUploadInstruction = await getAppUploadInstruction(appPath, username, accessKey, testingFramework);
|
|
31
|
+
if (appUploadInstruction) {
|
|
32
|
+
instructions.push({ content: appUploadInstruction, type: "setup" });
|
|
33
|
+
}
|
|
34
|
+
// Step 4: Generate project configuration and run instructions
|
|
35
|
+
const projectInstructions = getAppInstructionsForProjectConfiguration(framework, testingFramework, language);
|
|
36
|
+
if (projectInstructions) {
|
|
37
|
+
instructions.push({ content: projectInstructions, type: "run" });
|
|
38
|
+
}
|
|
39
|
+
const combinedInstructions = instructions
|
|
40
|
+
.map((instruction) => instruction.content)
|
|
41
|
+
.join("\n\n");
|
|
42
|
+
return {
|
|
43
|
+
content: [
|
|
44
|
+
{
|
|
45
|
+
type: "text",
|
|
46
|
+
text: formatAppInstructionsWithNumbers(combinedInstructions),
|
|
47
|
+
isError: false,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
isError: false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { getAppSDKPrefixCommand, getAppInstructionsForProjectConfiguration, } from "./instructions.js";
|
|
2
|
+
export { generateAppBrowserStackYMLInstructions } from "./config-generator.js";
|
|
3
|
+
export * from "./types.js";
|
|
4
|
+
export * from "./constants.js";
|
|
5
|
+
export * from "./utils.js";
|
|
6
|
+
export * from "./instructions.js";
|
|
7
|
+
export * from "./formatter.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Barrel exports for App BrowserStack module
|
|
2
|
+
export { getAppSDKPrefixCommand, getAppInstructionsForProjectConfiguration, } from "./instructions.js";
|
|
3
|
+
export { generateAppBrowserStackYMLInstructions } from "./config-generator.js";
|
|
4
|
+
export * from "./types.js";
|
|
5
|
+
export * from "./constants.js";
|
|
6
|
+
export * from "./utils.js";
|
|
7
|
+
export * from "./instructions.js";
|
|
8
|
+
export * from "./formatter.js";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { AppSDKSupportedLanguage, AppSDKSupportedTestingFramework } from "./index.js";
|
|
2
|
+
export declare function getAppInstructionsForProjectConfiguration(framework: string, testingFramework: AppSDKSupportedTestingFramework, language: AppSDKSupportedLanguage): string;
|
|
3
|
+
export declare function getAppSDKPrefixCommand(language: AppSDKSupportedLanguage, testingFramework: string, username: string, accessKey: string, appPath?: string): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Language-specific instruction imports
|
|
2
|
+
import { getJavaAppInstructions } from "./languages/java.js";
|
|
3
|
+
import { getCSharpAppInstructions } from "./languages/csharp.js";
|
|
4
|
+
import { getNodejsAppInstructions } from "./languages/nodejs.js";
|
|
5
|
+
import { getPythonAppInstructions } from "./languages/python.js";
|
|
6
|
+
import { getRubyAppInstructions } from "./languages/ruby.js";
|
|
7
|
+
// Language-specific command imports
|
|
8
|
+
import { getCSharpSDKCommand } from "./languages/csharp.js";
|
|
9
|
+
import { getJavaSDKCommand } from "./languages/java.js";
|
|
10
|
+
import { getNodejsSDKCommand } from "./languages/nodejs.js";
|
|
11
|
+
import { getPythonSDKCommand } from "./languages/python.js";
|
|
12
|
+
import { getRubySDKCommand } from "./languages/ruby.js";
|
|
13
|
+
export function getAppInstructionsForProjectConfiguration(framework, testingFramework, language) {
|
|
14
|
+
if (!framework || !testingFramework || !language) {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
switch (language) {
|
|
18
|
+
case "java":
|
|
19
|
+
return getJavaAppInstructions();
|
|
20
|
+
case "nodejs":
|
|
21
|
+
return getNodejsAppInstructions(testingFramework);
|
|
22
|
+
case "python":
|
|
23
|
+
return getPythonAppInstructions(testingFramework);
|
|
24
|
+
case "ruby":
|
|
25
|
+
return getRubyAppInstructions();
|
|
26
|
+
case "csharp":
|
|
27
|
+
return getCSharpAppInstructions();
|
|
28
|
+
default:
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function getAppSDKPrefixCommand(language, testingFramework, username, accessKey, appPath) {
|
|
33
|
+
switch (language) {
|
|
34
|
+
case "csharp":
|
|
35
|
+
return getCSharpSDKCommand(username, accessKey);
|
|
36
|
+
case "java":
|
|
37
|
+
return getJavaSDKCommand(testingFramework, username, accessKey, appPath);
|
|
38
|
+
case "nodejs":
|
|
39
|
+
return getNodejsSDKCommand(testingFramework, username, accessKey);
|
|
40
|
+
case "python":
|
|
41
|
+
return getPythonSDKCommand(testingFramework, username, accessKey);
|
|
42
|
+
case "ruby":
|
|
43
|
+
return getRubySDKCommand(testingFramework, username, accessKey);
|
|
44
|
+
default:
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// C# instructions and commands for App SDK utilities
|
|
2
|
+
import { PLATFORM_UTILS, createStep, createEnvStep, combineInstructions, } from "../index.js";
|
|
3
|
+
export function getCSharpAppInstructions() {
|
|
4
|
+
const { isWindows, isAppleSilicon, getPlatformLabel } = PLATFORM_UTILS;
|
|
5
|
+
let runCommand = "";
|
|
6
|
+
if (isWindows) {
|
|
7
|
+
runCommand = `\`\`\`cmd
|
|
8
|
+
dotnet build
|
|
9
|
+
dotnet test --filter <EXPRESSION> [other_args]
|
|
10
|
+
\`\`\``;
|
|
11
|
+
}
|
|
12
|
+
else if (isAppleSilicon) {
|
|
13
|
+
runCommand = `\`\`\`bash
|
|
14
|
+
dotnet build
|
|
15
|
+
dotnet test --filter <EXPRESSION> [other_args]
|
|
16
|
+
\`\`\`
|
|
17
|
+
|
|
18
|
+
**Did not set the alias?**
|
|
19
|
+
Use the absolute path to the dotnet installation to run your tests on Mac computers with Apple silicon chips:
|
|
20
|
+
\`\`\`bash
|
|
21
|
+
</absolute/path/to/location/of/dotnet/>/dotnet test
|
|
22
|
+
\`\`\``;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
runCommand = `\`\`\`bash
|
|
26
|
+
dotnet build
|
|
27
|
+
dotnet test --filter <EXPRESSION> [other_args]
|
|
28
|
+
\`\`\``;
|
|
29
|
+
}
|
|
30
|
+
const runStep = createStep("Run your C# test suite:", `**${getPlatformLabel()}:**
|
|
31
|
+
${runCommand}
|
|
32
|
+
|
|
33
|
+
**Debug Guidelines:**
|
|
34
|
+
If you encounter the error: java.lang.IllegalArgumentException: Multiple entries with the same key,
|
|
35
|
+
__Resolution:__
|
|
36
|
+
- The app capability should only be set in one place: browserstack.yml.
|
|
37
|
+
- Remove or comment out any code or configuration in your test setup (e.g., step definitions, runners, or capabilities setup) that sets the app path directly.`);
|
|
38
|
+
return runStep;
|
|
39
|
+
}
|
|
40
|
+
export function getCSharpSDKCommand(username, accessKey) {
|
|
41
|
+
const { isWindows = false, isAppleSilicon = false, getPlatformLabel = () => "Unknown", } = PLATFORM_UTILS || {};
|
|
42
|
+
if (!PLATFORM_UTILS) {
|
|
43
|
+
console.warn("PLATFORM_UTILS is undefined. Defaulting platform values.");
|
|
44
|
+
}
|
|
45
|
+
const envStep = createEnvStep(username, accessKey, isWindows, getPlatformLabel());
|
|
46
|
+
const installCommands = isWindows
|
|
47
|
+
? `\`\`\`cmd
|
|
48
|
+
dotnet add package BrowserStack.TestAdapter
|
|
49
|
+
dotnet build
|
|
50
|
+
dotnet browserstack-sdk setup --userName "${username}" --accessKey "${accessKey}"
|
|
51
|
+
\`\`\``
|
|
52
|
+
: `\`\`\`bash
|
|
53
|
+
dotnet add package BrowserStack.TestAdapter
|
|
54
|
+
dotnet build
|
|
55
|
+
dotnet browserstack-sdk setup --userName "${username}" --accessKey "${accessKey}"
|
|
56
|
+
\`\`\``;
|
|
57
|
+
const installStep = createStep("Install BrowserStack SDK", `Run the following command to install the BrowserStack SDK and create a browserstack.yml file in the root directory of your project:
|
|
58
|
+
|
|
59
|
+
**${getPlatformLabel()}:**
|
|
60
|
+
${installCommands}`);
|
|
61
|
+
const appleSiliconNote = isAppleSilicon
|
|
62
|
+
? createStep("[Only for Macs with Apple silicon] Install dotnet x64 on MacOS", `If you are using a Mac computer with Apple silicon chip (M1 or M2) architecture, use the given command:
|
|
63
|
+
|
|
64
|
+
\`\`\`bash
|
|
65
|
+
cd #(project folder Android or iOS)
|
|
66
|
+
dotnet browserstack-sdk setup-dotnet --dotnet-path "<path>" --dotnet-version "<version>"
|
|
67
|
+
\`\`\`
|
|
68
|
+
|
|
69
|
+
- \`<path>\` - Mention the absolute path to the directory where you want to save dotnet x64
|
|
70
|
+
- \`<version>\` - Mention the dotnet version which you want to use to run tests
|
|
71
|
+
|
|
72
|
+
This command performs the following functions:
|
|
73
|
+
- Installs dotnet x64
|
|
74
|
+
- Installs the required version of dotnet x64 at an appropriate path
|
|
75
|
+
- Sets alias for the dotnet installation location on confirmation (enter y option)`)
|
|
76
|
+
: "";
|
|
77
|
+
return combineInstructions(envStep, installStep, appleSiliconNote);
|
|
78
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const MAVEN_ARCHETYPE_GROUP_ID = "com.browserstack";
|
|
2
|
+
export declare const MAVEN_ARCHETYPE_ARTIFACT_ID = "junit-archetype-integrate";
|
|
3
|
+
export declare const MAVEN_ARCHETYPE_VERSION = "1.0";
|
|
4
|
+
export declare const JAVA_APP_FRAMEWORK_MAP: Record<string, string>;
|
|
5
|
+
export declare const GRADLE_APP_SETUP_INSTRUCTIONS = "\n**For Gradle setup:**\n1. Add browserstack-java-sdk to dependencies:\n compileOnly 'com.browserstack:browserstack-java-sdk:latest.release'\n\n2. Add browserstackSDK path variable:\n def browserstackSDKArtifact = configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.find { it.name == 'browserstack-java-sdk' }\n\n3. Add javaagent to gradle tasks:\n jvmArgs \"-javaagent:${browserstackSDKArtifact.file}\"\n";
|
|
6
|
+
export declare function getJavaAppInstructions(): string;
|
|
7
|
+
export declare function getJavaAppFrameworkForMaven(framework: string): string;
|
|
8
|
+
export declare function getJavaSDKCommand(framework: string, username: string, accessKey: string, appPath?: string): string;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Java instructions and commands for App SDK utilities
|
|
2
|
+
import { createStep, combineInstructions, createEnvStep, PLATFORM_UTILS, } from "../index.js";
|
|
3
|
+
// Java-specific constants and mappings
|
|
4
|
+
export const MAVEN_ARCHETYPE_GROUP_ID = "com.browserstack";
|
|
5
|
+
export const MAVEN_ARCHETYPE_ARTIFACT_ID = "junit-archetype-integrate";
|
|
6
|
+
export const MAVEN_ARCHETYPE_VERSION = "1.0";
|
|
7
|
+
// Framework mapping for Java Maven archetype generation for App Automate
|
|
8
|
+
export const JAVA_APP_FRAMEWORK_MAP = {
|
|
9
|
+
testng: "browserstack-sdk-archetype-integrate",
|
|
10
|
+
junit5: "browserstack-sdk-archetype-integrate",
|
|
11
|
+
selenide: "selenide-archetype-integrate",
|
|
12
|
+
jbehave: "browserstack-sdk-archetype-integrate",
|
|
13
|
+
cucumberTestng: "browserstack-sdk-archetype-integrate",
|
|
14
|
+
cucumberJunit4: "browserstack-sdk-archetype-integrate",
|
|
15
|
+
cucumberJunit5: "browserstack-sdk-archetype-integrate",
|
|
16
|
+
};
|
|
17
|
+
// Common Gradle setup instructions for App Automate (platform-independent)
|
|
18
|
+
export const GRADLE_APP_SETUP_INSTRUCTIONS = `
|
|
19
|
+
**For Gradle setup:**
|
|
20
|
+
1. Add browserstack-java-sdk to dependencies:
|
|
21
|
+
compileOnly 'com.browserstack:browserstack-java-sdk:latest.release'
|
|
22
|
+
|
|
23
|
+
2. Add browserstackSDK path variable:
|
|
24
|
+
def browserstackSDKArtifact = configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.find { it.name == 'browserstack-java-sdk' }
|
|
25
|
+
|
|
26
|
+
3. Add javaagent to gradle tasks:
|
|
27
|
+
jvmArgs "-javaagent:\${browserstackSDKArtifact.file}"
|
|
28
|
+
`;
|
|
29
|
+
export function getJavaAppInstructions() {
|
|
30
|
+
const baseRunStep = createStep("Run your App Automate test suite:", `\`\`\`bash
|
|
31
|
+
mvn test
|
|
32
|
+
\`\`\``);
|
|
33
|
+
return baseRunStep;
|
|
34
|
+
}
|
|
35
|
+
export function getJavaAppFrameworkForMaven(framework) {
|
|
36
|
+
return JAVA_APP_FRAMEWORK_MAP[framework] || framework;
|
|
37
|
+
}
|
|
38
|
+
function getMavenCommandForWindows(framework, mavenFramework, username, accessKey) {
|
|
39
|
+
return (`mvn archetype:generate -B ` +
|
|
40
|
+
`-DarchetypeGroupId="${MAVEN_ARCHETYPE_GROUP_ID}" ` +
|
|
41
|
+
`-DarchetypeArtifactId="${mavenFramework}" ` +
|
|
42
|
+
`-DarchetypeVersion="${MAVEN_ARCHETYPE_VERSION}" ` +
|
|
43
|
+
`-DgroupId="${MAVEN_ARCHETYPE_GROUP_ID}" ` +
|
|
44
|
+
`-DartifactId="${MAVEN_ARCHETYPE_ARTIFACT_ID}" ` +
|
|
45
|
+
`-Dversion="${MAVEN_ARCHETYPE_VERSION}" ` +
|
|
46
|
+
`-DBROWSERSTACK_USERNAME="${username}" ` +
|
|
47
|
+
`-DBROWSERSTACK_ACCESS_KEY="${accessKey}" ` +
|
|
48
|
+
`-DBROWSERSTACK_FRAMEWORK="${framework}"`);
|
|
49
|
+
}
|
|
50
|
+
function getMavenCommandForUnix(framework, mavenFramework, username, accessKey) {
|
|
51
|
+
return (`mvn archetype:generate -B ` +
|
|
52
|
+
`-DarchetypeGroupId="${MAVEN_ARCHETYPE_GROUP_ID}" ` +
|
|
53
|
+
`-DarchetypeArtifactId="${mavenFramework}" ` +
|
|
54
|
+
`-DarchetypeVersion="${MAVEN_ARCHETYPE_VERSION}" ` +
|
|
55
|
+
`-DgroupId="${MAVEN_ARCHETYPE_GROUP_ID}" ` +
|
|
56
|
+
`-DartifactId="${MAVEN_ARCHETYPE_ARTIFACT_ID}" ` +
|
|
57
|
+
`-Dversion="${MAVEN_ARCHETYPE_VERSION}" ` +
|
|
58
|
+
`-DBROWSERSTACK_USERNAME="${username}" ` +
|
|
59
|
+
`-DBROWSERSTACK_ACCESS_KEY="${accessKey}" ` +
|
|
60
|
+
`-DBROWSERSTACK_FRAMEWORK="${framework}"`);
|
|
61
|
+
}
|
|
62
|
+
export function getJavaSDKCommand(framework, username, accessKey, appPath) {
|
|
63
|
+
const { isWindows = false, getPlatformLabel } = PLATFORM_UTILS || {};
|
|
64
|
+
const mavenFramework = getJavaAppFrameworkForMaven(framework);
|
|
65
|
+
let mavenCommand;
|
|
66
|
+
if (isWindows) {
|
|
67
|
+
mavenCommand = getMavenCommandForWindows(framework, mavenFramework, username, accessKey);
|
|
68
|
+
if (appPath) {
|
|
69
|
+
mavenCommand += ` -DBROWSERSTACK_APP="${appPath}"`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
mavenCommand = getMavenCommandForUnix(framework, mavenFramework, username, accessKey);
|
|
74
|
+
if (appPath) {
|
|
75
|
+
mavenCommand += ` -DBROWSERSTACK_APP="${appPath}"`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const envStep = createEnvStep(username, accessKey, isWindows, getPlatformLabel());
|
|
79
|
+
const mavenStep = createStep("Install BrowserStack SDK using Maven Archetype for App Automate", `Maven command for ${framework} (${getPlatformLabel()}):
|
|
80
|
+
\`\`\`bash
|
|
81
|
+
${mavenCommand}
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
Alternative setup for Gradle users:
|
|
85
|
+
${GRADLE_APP_SETUP_INSTRUCTIONS}`);
|
|
86
|
+
return combineInstructions(envStep, mavenStep);
|
|
87
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { AppSDKSupportedTestingFramework } from "../index.js";
|
|
2
|
+
export declare function getNodejsSDKCommand(testingFramework: string, username: string, accessKey: string): string;
|
|
3
|
+
export declare function getNodejsAppInstructions(testingFramework: AppSDKSupportedTestingFramework): string;
|