Skip to content

Commit 5571de9

Browse files
Merge pull request #149 from tech-sushant/simulate-tool
feat : Adding percy simulate tool
2 parents 3fb77f9 + 167ea43 commit 5571de9

File tree

5 files changed

+111
-12
lines changed

5 files changed

+111
-12
lines changed

src/tools/percy-sdk.ts

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
77
import { SetUpPercyParamsShape } from "./sdk-utils/common/schema.js";
88
import { updateTestsWithPercyCommands } from "./add-percy-snapshots.js";
99
import { approveOrDeclinePercyBuild } from "./review-agent-utils/percy-approve-reject.js";
10-
import { setUpPercyHandler } from "./sdk-utils/handler.js";
11-
10+
import {
11+
setUpPercyHandler,
12+
simulatePercyChangeHandler,
13+
} from "./sdk-utils/handler.js";
14+
import { z } from "zod";
1215
import {
1316
SETUP_PERCY_DESCRIPTION,
1417
LIST_TEST_FILES_DESCRIPTION,
1518
PERCY_SNAPSHOT_COMMANDS_DESCRIPTION,
19+
SIMULATE_PERCY_CHANGE_DESCRIPTION,
1620
} from "./sdk-utils/common/constants.js";
1721

1822
import {
@@ -33,8 +37,53 @@ export function registerPercyTools(
3337
) {
3438
const tools: Record<string, any> = {};
3539

40+
server.prompt(
41+
"integrate-percy",
42+
{
43+
project_name: z
44+
.string()
45+
.describe("The name of the project to integrate with Percy"),
46+
},
47+
async ({ project_name }) => {
48+
return {
49+
messages: [
50+
{
51+
role: "assistant",
52+
content: {
53+
type: "text",
54+
text: `Integrate percy in this project ${project_name} using tool percyVisualTestIntegrationAgent.`,
55+
},
56+
},
57+
],
58+
};
59+
},
60+
);
61+
62+
tools.percyVisualTestIntegrationAgent = server.tool(
63+
"percyVisualTestIntegrationAgent",
64+
SIMULATE_PERCY_CHANGE_DESCRIPTION,
65+
SetUpPercyParamsShape,
66+
async (args) => {
67+
try {
68+
trackMCP(
69+
"VisualTestIntegrationAgent",
70+
server.server.getClientVersion()!,
71+
config,
72+
);
73+
return simulatePercyChangeHandler(args, config);
74+
} catch (error) {
75+
return handleMCPError(
76+
"VisualTestIntegrationAgent",
77+
server,
78+
config,
79+
error,
80+
);
81+
}
82+
},
83+
);
84+
3685
tools.setupPercyVisualTesting = server.tool(
37-
"setupPercyVisualTesting",
86+
"expandPercyVisualTesting",
3887
SETUP_PERCY_DESCRIPTION,
3988
SetUpPercyParamsShape,
4089
async (args) => {

src/tools/run-percy-scan.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export async function runPercyScan(
3434
- Node.js → npm test or yarn test
3535
- Cypress → cypress run
3636
or from package.json scripts`,
37-
`Wrap the inferred command with Percy:\nnpx percy exec -- <test command>`,
37+
`Wrap the inferred command with Percy along with label: \nnpx percy exec --labels=mcp -- <test command>`,
3838
`If the test command cannot be inferred confidently, ask the user directly for the correct test command.`,
3939
);
4040

src/tools/sdk-utils/common/constants.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export const IMPORTANT_SETUP_WARNING =
22
"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.";
33

44
export const SETUP_PERCY_DESCRIPTION =
5-
"Set up Percy visual testing for your project. This supports both Percy Web Standalone and Percy Automate.";
5+
"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}";
66

77
export const LIST_TEST_FILES_DESCRIPTION =
88
"Lists all test files for a given set of directories.";
@@ -13,8 +13,7 @@ export const PERCY_SNAPSHOT_COMMANDS_DESCRIPTION =
1313
export const RUN_ON_BROWSERSTACK_DESCRIPTION =
1414
"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.";
1515

16-
export const SIMULATE_PERCY_CHANGE_DESCRIPTION =
17-
"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";
16+
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}"`;
1817

1918
export const PERCY_REPLACE_REGEX =
2019
/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\./;
@@ -35,7 +34,7 @@ Process files in STRICT sequential order using tool addPercySnapshotCommands() w
3534
export const PERCY_SIMULATE_INSTRUCTION = `
3635
MANDATORY: Aggressive, Accurate Percy Snapshots Integration
3736
38-
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).
37+
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.
3938
4039
You MUST follow these steps with NO EXCEPTIONS:
4140
1. Identify 4-5 key test files in the project that are critical for visual testing.
@@ -53,7 +52,7 @@ STEP 1: At this case we have added Percy snapshot commands to the test files at
5352
- Do NOT add any popup injection or visual changes yet.
5453
- Do NOT run any Percy builds at this stage.
5554
56-
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."
55+
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."
5756
5857
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")).
5958
- Do NOT add a new snapshot name for the popup.
@@ -77,14 +76,14 @@ driver.execute_script(popup_script)
7776
percy_snapshot(self.driver, "Before Adding to Cart") # (Do NOT change the snapshot name, keep existing one)
7877
\`\`\`
7978
80-
STEP 4: Run a second Percy build.
79+
STEP 4: Run a second Percy build with same label and same test command as the baseline.
8180
- The snapshot names must remain the same as in the baseline.
8281
- The visual change should now appear in the same snapshot as before.
8382
- Use the same build command you ran for the baseline.
8483
8584
STEP 5: Compare the two Percy builds to see the detected visual difference.
8685
87-
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.
86+
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.
8887
8988
CONSTRAINTS:
9089
- Do NOT run any builds until explicitly instructed in the steps.
@@ -100,3 +99,6 @@ VALIDATION CHECKPOINTS (before proceeding to the next step):
10099
CRITICAL:
101100
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.
102101
`;
102+
103+
export const PERCY_VERIFICATION_REGEX =
104+
/\*\* Verification:\*\*\nPlease verify that you have completed all[\s\S]*?double-check each step and ensure all commands executed successfully\./s;

src/tools/sdk-utils/handler.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ import {
1717
getBootstrapFailedMessage,
1818
percyUnsupportedResult,
1919
} from "./common/utils.js";
20+
import {
21+
PERCY_SIMULATE_INSTRUCTION,
22+
PERCY_REPLACE_REGEX,
23+
PERCY_SIMULATION_DRIVER_INSTRUCTION,
24+
PERCY_VERIFICATION_REGEX,
25+
} from "./common/constants.js";
2026

2127
export async function runTestsOnBrowserStackHandler(
2228
rawInput: unknown,
@@ -155,3 +161,43 @@ export async function setUpPercyHandler(
155161
throw new Error(getBootstrapFailedMessage(error, { config }));
156162
}
157163
}
164+
165+
export async function simulatePercyChangeHandler(
166+
rawInput: unknown,
167+
config: BrowserStackConfig,
168+
): Promise<CallToolResult> {
169+
try {
170+
let percyInstruction;
171+
172+
try {
173+
percyInstruction = await setUpPercyHandler(rawInput, config);
174+
} catch {
175+
throw new Error("Failed to set up Percy");
176+
}
177+
178+
if (percyInstruction.isError) {
179+
return percyInstruction;
180+
}
181+
182+
if (Array.isArray(percyInstruction.content)) {
183+
percyInstruction.content = percyInstruction.content.map((item) => {
184+
if (typeof item.text === "string") {
185+
const updatedText = item.text
186+
.replace(PERCY_REPLACE_REGEX, PERCY_SIMULATE_INSTRUCTION)
187+
.replace(PERCY_VERIFICATION_REGEX, "");
188+
return { ...item, text: updatedText };
189+
}
190+
return item;
191+
});
192+
}
193+
194+
percyInstruction.content?.push({
195+
type: "text" as const,
196+
text: PERCY_SIMULATION_DRIVER_INSTRUCTION,
197+
});
198+
199+
return percyInstruction;
200+
} catch (error) {
201+
throw new Error(getBootstrapFailedMessage(error, { config }));
202+
}
203+
}

src/tools/sdk-utils/percy-web/handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ export function runPercyWeb(
3232
steps.push({
3333
type: "instruction",
3434
title: "Set Percy Token in Environment",
35-
content: `Here is percy token if required {${percyToken}}`,
35+
content: `Set the environment variable for your project:
36+
export PERCY_TOKEN="${percyToken}"
37+
(For Windows: use 'setx PERCY_TOKEN "${percyToken}"' or 'set PERCY_TOKEN=${percyToken}' as appropriate.)`,
3638
});
3739

3840
steps.push({

0 commit comments

Comments
 (0)