@browserstack/mcp-server 2.2.5-beta.2 → 2.2.5-beta.3
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/dist/lib/accessibility-routing.d.ts +5 -0
- package/dist/lib/accessibility-routing.js +5 -0
- package/dist/server-factory.js +6 -1
- package/dist/tools/accessibility.js +588 -67
- package/dist/tools/accessiblity-utils/app-report-fetcher.d.ts +341 -0
- package/dist/tools/accessiblity-utils/app-report-fetcher.js +731 -0
- package/dist/tools/accessiblity-utils/paginate.d.ts +21 -0
- package/dist/tools/accessiblity-utils/paginate.js +44 -0
- package/dist/tools/accessiblity-utils/report-fetcher.d.ts +116 -7
- package/dist/tools/accessiblity-utils/report-fetcher.js +273 -30
- package/dist/tools/accessiblity-utils/report-parser.d.ts +41 -1
- package/dist/tools/accessiblity-utils/report-parser.js +142 -43
- package/dist/tools/appautomate-utils/appium-sdk/config-generator.d.ts +2 -0
- package/dist/tools/appautomate-utils/appium-sdk/config-generator.js +15 -5
- package/dist/tools/appautomate-utils/appium-sdk/constants.d.ts +2 -1
- package/dist/tools/appautomate-utils/appium-sdk/constants.js +6 -1
- package/dist/tools/appautomate-utils/appium-sdk/handler.js +11 -1
- package/dist/tools/appautomate-utils/appium-sdk/instructions.d.ts +3 -1
- package/dist/tools/appautomate-utils/appium-sdk/instructions.js +2 -2
- package/dist/tools/appautomate-utils/appium-sdk/languages/accessibility.d.ts +2 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/accessibility.js +32 -0
- package/dist/tools/appautomate-utils/appium-sdk/languages/nodejs.d.ts +3 -1
- package/dist/tools/appautomate-utils/appium-sdk/languages/nodejs.js +19 -6
- package/dist/tools/appautomate-utils/native-execution/appautomate.d.ts +7 -2
- package/dist/tools/appautomate-utils/native-execution/appautomate.js +15 -12
- package/dist/tools/appautomate-utils/native-execution/constants.d.ts +2 -1
- package/dist/tools/appautomate-utils/native-execution/constants.js +6 -1
- package/dist/tools/appautomate.js +29 -16
- package/dist/tools/failurelogs-utils/app-automate.js +1 -1
- package/dist/tools/testmanagement-utils/create-testrun.d.ts +1 -1
- package/dist/tools/testmanagement-utils/update-testrun.d.ts +1 -1
- package/dist/tools/tool-handoff.d.ts +5 -4
- package/dist/tools/tool-handoff.js +5 -10
- package/package.json +1 -1
- package/dist/tools/accessiblity-utils/accessibility-rag.d.ts +0 -12
- package/dist/tools/accessiblity-utils/accessibility-rag.js +0 -59
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AppTestPlatform } from "./types.js";
|
|
3
|
-
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.";
|
|
3
|
+
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. Set accessibility: true to also run App Accessibility (WCAG) checks during the tests; the issues are then read with fetchAccessibilityIssues.";
|
|
4
4
|
export declare const MobileDeviceSchema: z.ZodObject<{
|
|
5
5
|
platform: z.ZodEnum<{
|
|
6
6
|
android: "android";
|
|
@@ -22,4 +22,5 @@ export declare const RUN_APP_AUTOMATE_SCHEMA: {
|
|
|
22
22
|
}, z.core.$strip>>>;
|
|
23
23
|
project: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
24
24
|
detectedAutomationFramework: z.ZodEnum<typeof AppTestPlatform>;
|
|
25
|
+
accessibility: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
25
26
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AppTestPlatform } from "./types.js";
|
|
3
|
-
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.`;
|
|
3
|
+
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. Set accessibility: true to also run App Accessibility (WCAG) checks during the tests; the issues are then read with fetchAccessibilityIssues.`;
|
|
4
4
|
export const MobileDeviceSchema = z.object({
|
|
5
5
|
platform: z
|
|
6
6
|
.enum(["android", "ios"])
|
|
@@ -46,4 +46,9 @@ export const RUN_APP_AUTOMATE_SCHEMA = {
|
|
|
46
46
|
detectedAutomationFramework: z
|
|
47
47
|
.nativeEnum(AppTestPlatform)
|
|
48
48
|
.describe("The automation framework used in the project, such as 'espresso' (Android) or 'xcuitest' (iOS)."),
|
|
49
|
+
accessibility: z
|
|
50
|
+
.boolean()
|
|
51
|
+
.optional()
|
|
52
|
+
.default(false)
|
|
53
|
+
.describe("Run App Accessibility (WCAG) checks on the screens the tests touch; Android 11+ or iOS 14+."),
|
|
49
54
|
};
|
|
@@ -96,8 +96,26 @@ async function takeAppScreenshot(args) {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* What a native suite needs for App Accessibility to scan it. The build request's flag
|
|
101
|
+
* turns the checks on, but only a suite built with BrowserStack's instrumentation records
|
|
102
|
+
* scans; without it the run finishes with none, and nothing says why.
|
|
103
|
+
*/
|
|
104
|
+
const NATIVE_ACCESSIBILITY_SETUP = {
|
|
105
|
+
Espresso: "The test suite must be built with BrowserStack's Gradle plugin and SDK, or the run records no scans: classpath 'com.browserstack:gradle-tool:latest.release' in the root build.gradle, id 'com.browserstack.gradle-tool' in the app's plugins, and androidTestImplementation 'com.browserstack:browserstack-java-sdk:latest.release' (1.31.0 or newer). The app also needs internet permission.",
|
|
106
|
+
XCUITest: "The UI test target must embed and initialize the BrowserStack Swift SDK (BrowserstackSwiftSDK.xcframework, or the Swift package), or the run records no scans: set NSPrincipalClass to BrowserstackSwiftSDK.BrowserstackSwiftSDK in the test target's Info.plist, or call BrowserstackSwiftSDK.init() in the test class's init(). Swift 5.4+ and iOS 14+.",
|
|
107
|
+
};
|
|
108
|
+
/** The run's build and, with accessibility on, where its issues are read once it finishes. */
|
|
109
|
+
function runStartedMessage(framework, buildId, accessibility) {
|
|
110
|
+
const link = `https://app-automate.browserstack.com/builds/${buildId}`;
|
|
111
|
+
const a11y = accessibility
|
|
112
|
+
? `\n\n♿ App Accessibility checks are on. ${NATIVE_ACCESSIBILITY_SETUP[framework] ?? ""}\nWhen the build finishes (typically 10–20 minutes), read the issues with fetchAccessibilityIssues, reportUrl: ${link} — it maps the build to its accessibility report and waits while the report is still processing.`
|
|
113
|
+
: "";
|
|
114
|
+
return `✅ ${framework} run started successfully!\n\n🔧 Build ID: ${buildId}\n🔗 View your build: ${link}${a11y}`;
|
|
115
|
+
}
|
|
99
116
|
//Runs AppAutomate tests on BrowserStack by uploading app and test suite, then triggering a test run.
|
|
100
117
|
async function runAppTestsOnBrowserStack(args, config) {
|
|
118
|
+
const accessibility = args.accessibility === true;
|
|
101
119
|
// Validate that either paths or URLs are provided for both app and test suite
|
|
102
120
|
if (!args.browserstackAppUrl && !args.appPath) {
|
|
103
121
|
throw new Error("appPath is required when browserstackAppUrl is not provided");
|
|
@@ -105,8 +123,13 @@ async function runAppTestsOnBrowserStack(args, config) {
|
|
|
105
123
|
if (!args.browserstackTestSuiteUrl && !args.testSuitePath) {
|
|
106
124
|
throw new Error("testSuitePath is required when browserstackTestSuiteUrl is not provided");
|
|
107
125
|
}
|
|
108
|
-
// Validate devices against real BrowserStack device data
|
|
109
|
-
|
|
126
|
+
// Validate devices against real BrowserStack device data, and ask for them as App Automate
|
|
127
|
+
// lists them: a version given as "14" is "14.0" there, and a run asking for "-14" waits for
|
|
128
|
+
// a device that never matches.
|
|
129
|
+
const validated = await validateAppAutomateDevices(args.devices);
|
|
130
|
+
const deviceStrings = args.devices.length > 0
|
|
131
|
+
? validated.map((device) => `${device.deviceName}-${device.osVersion}`)
|
|
132
|
+
: [];
|
|
110
133
|
switch (args.detectedAutomationFramework) {
|
|
111
134
|
case AppTestPlatform.ESPRESSO: {
|
|
112
135
|
try {
|
|
@@ -128,17 +151,12 @@ async function runAppTestsOnBrowserStack(args, config) {
|
|
|
128
151
|
test_suite_url = await uploadEspressoTestSuite(args.testSuitePath, config);
|
|
129
152
|
logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
|
|
130
153
|
}
|
|
131
|
-
|
|
132
|
-
const deviceStrings = args.devices.map((device) => {
|
|
133
|
-
const [, deviceName, osVersion] = device;
|
|
134
|
-
return `${deviceName}-${osVersion}`;
|
|
135
|
-
});
|
|
136
|
-
const build_id = await triggerEspressoBuild(app_url, test_suite_url, deviceStrings, args.project, config);
|
|
154
|
+
const build_id = await triggerEspressoBuild(app_url, test_suite_url, deviceStrings, args.project, config, { accessibility });
|
|
137
155
|
return {
|
|
138
156
|
content: [
|
|
139
157
|
{
|
|
140
158
|
type: "text",
|
|
141
|
-
text:
|
|
159
|
+
text: runStartedMessage("Espresso", build_id, accessibility),
|
|
142
160
|
},
|
|
143
161
|
],
|
|
144
162
|
};
|
|
@@ -168,17 +186,12 @@ async function runAppTestsOnBrowserStack(args, config) {
|
|
|
168
186
|
test_suite_url = await uploadXcuiTestSuite(args.testSuitePath, config);
|
|
169
187
|
logger.info(`Test suite uploaded. URL: ${test_suite_url}`);
|
|
170
188
|
}
|
|
171
|
-
|
|
172
|
-
const deviceStrings = args.devices.map((device) => {
|
|
173
|
-
const [, deviceName, osVersion] = device;
|
|
174
|
-
return `${deviceName}-${osVersion}`;
|
|
175
|
-
});
|
|
176
|
-
const build_id = await triggerXcuiBuild(app_url, test_suite_url, deviceStrings, args.project, config);
|
|
189
|
+
const build_id = await triggerXcuiBuild(app_url, test_suite_url, deviceStrings, args.project, config, { accessibility });
|
|
177
190
|
return {
|
|
178
191
|
content: [
|
|
179
192
|
{
|
|
180
193
|
type: "text",
|
|
181
|
-
text:
|
|
194
|
+
text: runStartedMessage("XCUITest", build_id, accessibility),
|
|
182
195
|
},
|
|
183
196
|
],
|
|
184
197
|
};
|
|
@@ -4,7 +4,7 @@ import { apiClient } from "../../lib/apiClient.js";
|
|
|
4
4
|
import { wrapUntrusted } from "../../lib/untrusted-content.js";
|
|
5
5
|
// DEVICE LOGS
|
|
6
6
|
export async function retrieveDeviceLogs(sessionId, buildId, config) {
|
|
7
|
-
const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/
|
|
7
|
+
const url = `https://api.browserstack.com/app-automate/builds/${buildId}/sessions/${sessionId}/devicelogs`;
|
|
8
8
|
const authString = getBrowserStackAuth(config);
|
|
9
9
|
const auth = Buffer.from(authString).toString("base64");
|
|
10
10
|
const response = await apiClient.get({
|
|
@@ -11,8 +11,8 @@ export declare const CreateTestRunSchema: z.ZodObject<{
|
|
|
11
11
|
description: z.ZodOptional<z.ZodString>;
|
|
12
12
|
run_state: z.ZodOptional<z.ZodEnum<{
|
|
13
13
|
done: "done";
|
|
14
|
-
new_run: "new_run";
|
|
15
14
|
in_progress: "in_progress";
|
|
15
|
+
new_run: "new_run";
|
|
16
16
|
under_review: "under_review";
|
|
17
17
|
rejected: "rejected";
|
|
18
18
|
closed: "closed";
|
|
@@ -11,8 +11,8 @@ export declare const UpdateTestRunSchema: z.ZodObject<{
|
|
|
11
11
|
name: z.ZodOptional<z.ZodString>;
|
|
12
12
|
run_state: z.ZodOptional<z.ZodEnum<{
|
|
13
13
|
done: "done";
|
|
14
|
-
new_run: "new_run";
|
|
15
14
|
in_progress: "in_progress";
|
|
15
|
+
new_run: "new_run";
|
|
16
16
|
under_review: "under_review";
|
|
17
17
|
rejected: "rejected";
|
|
18
18
|
closed: "closed";
|
|
@@ -62,11 +62,12 @@ export declare const NEEDS_BUILD_ID: string;
|
|
|
62
62
|
*/
|
|
63
63
|
export declare const NEEDS_SESSION_ID: string;
|
|
64
64
|
/**
|
|
65
|
-
* A scan run's ids come from startAccessibilityScan
|
|
66
|
-
*
|
|
65
|
+
* A scan run's ids come from startAccessibilityScan; a build's id is printed by the SDK at
|
|
66
|
+
* the end of its run. Nothing here lists builds or reports, but the tool takes a report's
|
|
67
|
+
* dashboard link, or an Automate link, and looks the ids up itself.
|
|
67
68
|
*/
|
|
68
|
-
export declare const NEEDS_A11Y_SCAN_ID:
|
|
69
|
+
export declare const NEEDS_A11Y_SCAN_ID = " Ids: startAccessibilityScan or a dashboard link; nothing here lists reports.";
|
|
69
70
|
/** Auth-config ids are not listable by any tool here. */
|
|
70
|
-
export declare const NEEDS_A11Y_CONFIG_ID
|
|
71
|
+
export declare const NEEDS_A11Y_CONFIG_ID = " Requires the id returned by createAccessibilityAuthConfig; nothing here lists configs.";
|
|
71
72
|
/** Test ids come from listTestIds, which itself needs a build id. */
|
|
72
73
|
export declare const NEEDS_TEST_IDS: string;
|
|
@@ -71,17 +71,12 @@ export const NEEDS_BUILD_ID = needsIdFrom("a BrowserStack build id", "getBuildId
|
|
|
71
71
|
export const NEEDS_SESSION_ID = " Requires a session id. listSessions lists them for a build; if you do not have the " +
|
|
72
72
|
"build either, getBuildId or listBuildId resolves one from a project and build name.";
|
|
73
73
|
/**
|
|
74
|
-
* A scan run's ids come from startAccessibilityScan
|
|
75
|
-
*
|
|
74
|
+
* A scan run's ids come from startAccessibilityScan; a build's id is printed by the SDK at
|
|
75
|
+
* the end of its run. Nothing here lists builds or reports, but the tool takes a report's
|
|
76
|
+
* dashboard link, or an Automate link, and looks the ids up itself.
|
|
76
77
|
*/
|
|
77
|
-
export const NEEDS_A11Y_SCAN_ID = "
|
|
78
|
-
"startAccessibilityScan; an Automated Tests build's id, the thBuildId in its accessibility " +
|
|
79
|
-
"report link; a Workflow Analyzer report's numeric id; or a Website Scanner or Automated " +
|
|
80
|
-
"Tests report's dashboard link, passed as reportUrl. No tool here lists past scans, builds " +
|
|
81
|
-
"or reports, so if you have none of these, ask the user for the report link, or start a " +
|
|
82
|
-
"new scan, rather than guessing.";
|
|
78
|
+
export const NEEDS_A11Y_SCAN_ID = " Ids: startAccessibilityScan or a dashboard link; nothing here lists reports.";
|
|
83
79
|
/** Auth-config ids are not listable by any tool here. */
|
|
84
|
-
export const NEEDS_A11Y_CONFIG_ID = " Requires the
|
|
85
|
-
"existing configurations, so if you do not have the id, create one rather than guessing.";
|
|
80
|
+
export const NEEDS_A11Y_CONFIG_ID = " Requires the id returned by createAccessibilityAuthConfig; nothing here lists configs.";
|
|
86
81
|
/** Test ids come from listTestIds, which itself needs a build id. */
|
|
87
82
|
export const NEEDS_TEST_IDS = needsIdFrom("test ids", "listTestIds");
|
package/package.json
CHANGED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export interface RAGChunk {
|
|
2
|
-
url: string;
|
|
3
|
-
content: string;
|
|
4
|
-
}
|
|
5
|
-
import { BrowserStackConfig } from "../../lib/types.js";
|
|
6
|
-
export interface AccessibilityRAGResponse {
|
|
7
|
-
content: Array<{
|
|
8
|
-
type: "text";
|
|
9
|
-
text: string;
|
|
10
|
-
}>;
|
|
11
|
-
}
|
|
12
|
-
export declare function queryAccessibilityRAG(userQuery: string, config: BrowserStackConfig): Promise<any>;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { apiClient } from "../../lib/apiClient.js";
|
|
2
|
-
import { wrapUntrusted } from "../../lib/untrusted-content.js";
|
|
3
|
-
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
4
|
-
export async function queryAccessibilityRAG(userQuery, config) {
|
|
5
|
-
const url = "https://accessibility.browserstack.com/api/tcg-proxy/search";
|
|
6
|
-
const authString = getBrowserStackAuth(config);
|
|
7
|
-
const auth = Buffer.from(authString).toString("base64");
|
|
8
|
-
const response = await apiClient.post({
|
|
9
|
-
url,
|
|
10
|
-
headers: {
|
|
11
|
-
"Content-Type": "application/json",
|
|
12
|
-
Authorization: `Basic ${auth}`,
|
|
13
|
-
},
|
|
14
|
-
body: {
|
|
15
|
-
query: userQuery,
|
|
16
|
-
},
|
|
17
|
-
raise_error: false,
|
|
18
|
-
});
|
|
19
|
-
if (!response.ok) {
|
|
20
|
-
const errorText = typeof response.data === "string"
|
|
21
|
-
? response.data
|
|
22
|
-
: JSON.stringify(response.data);
|
|
23
|
-
throw new Error(`RAG endpoint error: ${response.status} ${errorText}`);
|
|
24
|
-
}
|
|
25
|
-
const responseData = response.data;
|
|
26
|
-
if (!responseData.success) {
|
|
27
|
-
throw new Error("Something went wrong: " + responseData.message);
|
|
28
|
-
}
|
|
29
|
-
// Parse the stringified JSON data
|
|
30
|
-
let parsedData;
|
|
31
|
-
try {
|
|
32
|
-
parsedData = JSON.parse(responseData.data);
|
|
33
|
-
}
|
|
34
|
-
catch (err) {
|
|
35
|
-
throw new Error("Failed to parse RAG response data as JSON: " +
|
|
36
|
-
(err instanceof Error ? err.message : String(err)));
|
|
37
|
-
}
|
|
38
|
-
if (!parsedData ||
|
|
39
|
-
!parsedData.data ||
|
|
40
|
-
!Array.isArray(parsedData.data.chunks)) {
|
|
41
|
-
throw new Error("RAG response data is missing expected 'data.chunks' array");
|
|
42
|
-
}
|
|
43
|
-
const chunks = parsedData.data.chunks;
|
|
44
|
-
// Format the response properly
|
|
45
|
-
const instruction = "IMPORTANT: Use ONLY the data provided below to answer the user's accessibility question. Do not use any external knowledge. When answering, you MUST include the relevant BrowserStack documentation links provided in the sources for personalization and further reference.\n\n";
|
|
46
|
-
const formattedChunks = chunks
|
|
47
|
-
.map((chunk, index) => `${index + 1}: Source: ${chunk.url}\n\n${chunk.content}`)
|
|
48
|
-
.join("\n\n---\n\n");
|
|
49
|
-
const formattedResponse = instruction +
|
|
50
|
-
wrapUntrusted("BrowserStack accessibility documentation", formattedChunks);
|
|
51
|
-
return {
|
|
52
|
-
content: [
|
|
53
|
-
{
|
|
54
|
-
type: "text",
|
|
55
|
-
text: formattedResponse,
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
};
|
|
59
|
-
}
|