Unverified 提交 86582d20 authored 作者: wwwillchen-bot's avatar wwwillchen-bot 提交者: GitHub

Update E2E tests for UI label changes and editor interactions (#2611)

## Summary - Update selectors for renamed UI elements ("Max Chat Turns in Context", "Build" mode, "Add Environment Variable" button) - Fix copy_app test to use more specific dialog and heading selectors for robustness - Refactor edit_code tests to use keyboard interactions instead of fill() for Monaco editor - Update problem button text patterns to match new "Fix N problem(s)" format - Update related test snapshots ## Test plan - [ ] Run `npm test` to verify all unit tests pass - [ ] Run E2E tests to verify the updated selectors work correctly - [ ] Verify the modified tests match the current UI state 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2611" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Updates E2E tests to match renamed UI labels and make Monaco editor edits reliable, reducing flakiness and keeping snapshots in sync. Improves selectors and expectations across affected specs. - **Bug Fixes** - Update selectors for renamed labels: “Max Chat Turns in Context”, “Build”, “Add Environment Variable”. - Match new “Fix N problem(s)” button text. - Use scoped dialog and heading selectors in copy_app. - Refresh snapshots to reflect current UI. - **Refactors** - Switch Monaco editor edits to keyboard interactions instead of fill(). - Add auto-approve setup in edit_code tests for stability. <sup>Written for commit 0f1e771f52850e3a6b89286d22ade5a4ff6db606. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> Co-authored-by: 's avatarWill Chen <willchen90@gmail.com> Co-authored-by: 's avatarClaude Opus 4.5 <noreply@anthropic.com>
上级 50a72da9
...@@ -14,7 +14,7 @@ testSkipIfWindows("context window", async ({ po }) => { ...@@ -14,7 +14,7 @@ testSkipIfWindows("context window", async ({ po }) => {
await po.navigation.goToSettingsTab(); await po.navigation.goToSettingsTab();
const beforeSettings = po.settings.recordSettings(); const beforeSettings = po.settings.recordSettings();
await po.page await po.page
.getByRole("combobox", { name: "Maximum number of chat turns" }) .getByRole("combobox", { name: "Max Chat Turns in Context" })
.click(); .click();
await po.page.getByRole("option", { name: "Plus (5)" }).click(); await po.page.getByRole("option", { name: "Plus (5)" }).click();
......
...@@ -34,13 +34,17 @@ for (const { testName, newAppName, buttonName, expectedVersion } of tests) { ...@@ -34,13 +34,17 @@ for (const { testName, newAppName, buttonName, expectedVersion } of tests) {
await po.page.getByRole("button", { name: buttonName }).click(); await po.page.getByRole("button", { name: buttonName }).click();
// Wait for the copy dialog to close // Wait for the copy dialog to close
await expect(po.page.getByRole("dialog")).not.toBeVisible({ await expect(
po.page.getByRole("dialog", { name: new RegExp(`Copy "${newAppName}"`) }),
).not.toBeVisible({
timeout: Timeout.MEDIUM, timeout: Timeout.MEDIUM,
}); });
// Expect to be on the new app's detail page // Expect to be on the new app's detail page
await expect( await expect(
po.page.getByRole("heading", { name: newAppName }), po.page
.getByTestId("app-details-page")
.getByRole("heading", { name: newAppName }),
).toBeVisible({ ).toBeVisible({
// Potentially takes a while for the copy to complete // Potentially takes a while for the copy to complete
timeout: Timeout.MEDIUM, timeout: Timeout.MEDIUM,
......
...@@ -13,11 +13,11 @@ test("default chat mode - pro user defaults and setting change applies to new ch ...@@ -13,11 +13,11 @@ test("default chat mode - pro user defaults and setting change applies to new ch
.getByTestId("chat-mode-selector"), .getByTestId("chat-mode-selector"),
).toHaveText("Agent"); ).toHaveText("Agent");
// Change default chat mode to "agent" (Build with MCP) in settings // Change default chat mode to "Build" in settings
await po.navigation.goToSettingsTab(); await po.navigation.goToSettingsTab();
const beforeSettings = po.settings.recordSettings(); const beforeSettings = po.settings.recordSettings();
await po.page.getByLabel("Default Chat Mode").click(); await po.page.getByLabel("Default Chat Mode").click();
await po.page.getByRole("option", { name: "Build with MCP" }).click(); await po.page.getByRole("option", { name: /^Build/ }).click();
po.settings.snapshotSettingsDelta(beforeSettings); po.settings.snapshotSettingsDelta(beforeSettings);
// Import an app and create a new chat to verify the default is applied // Import an app and create a new chat to verify the default is applied
...@@ -27,7 +27,7 @@ test("default chat mode - pro user defaults and setting change applies to new ch ...@@ -27,7 +27,7 @@ test("default chat mode - pro user defaults and setting change applies to new ch
// Verify the chat mode selector shows the new default mode // Verify the chat mode selector shows the new default mode
await expect(po.page.getByTestId("chat-mode-selector")).toContainText( await expect(po.page.getByTestId("chat-mode-selector")).toContainText(
"Build (MCP)", "Build",
); );
}); });
......
...@@ -4,6 +4,7 @@ import fs from "fs"; ...@@ -4,6 +4,7 @@ import fs from "fs";
import path from "path"; import path from "path";
test("edit code", async ({ po }) => { test("edit code", async ({ po }) => {
await po.setUp({ autoApprove: true });
const editedFilePath = path.join("src", "components", "made-with-dyad.tsx"); const editedFilePath = path.join("src", "components", "made-with-dyad.tsx");
await po.sendPrompt("foo"); await po.sendPrompt("foo");
const appPath = await po.appManagement.getCurrentAppPath(); const appPath = await po.appManagement.getCurrentAppPath();
...@@ -12,15 +13,15 @@ test("edit code", async ({ po }) => { ...@@ -12,15 +13,15 @@ test("edit code", async ({ po }) => {
await po.previewPanel.selectPreviewMode("code"); await po.previewPanel.selectPreviewMode("code");
await po.page.getByText("made-with-dyad.tsx").click(); await po.page.getByText("made-with-dyad.tsx").click();
await po.page // Wait for the editor to load and then fill in the new content
.getByRole("code") const editorContent = po.page.getByRole("textbox", {
.locator("div") name: "Editor content",
.filter({ hasText: "export const" }) });
.nth(4) await expect(editorContent).toBeVisible();
.click(); // Monaco editor intercepts pointer events, so we need to use force: true
await po.page await editorContent.click({ force: true });
.getByRole("textbox", { name: "Editor content" }) await po.page.keyboard.press("ControlOrMeta+a");
.fill("export const MadeWithDyad = ;"); await po.page.keyboard.type("export const MadeWithDyad = ;");
// Save the file // Save the file
await po.page.getByTestId("save-file-button").click(); await po.page.getByTestId("save-file-button").click();
...@@ -38,6 +39,7 @@ test("edit code", async ({ po }) => { ...@@ -38,6 +39,7 @@ test("edit code", async ({ po }) => {
}); });
test("edit code edits the right file", async ({ po }) => { test("edit code edits the right file", async ({ po }) => {
await po.setUp({ autoApprove: true });
const editedFilePath = path.join("src", "components", "made-with-dyad.tsx"); const editedFilePath = path.join("src", "components", "made-with-dyad.tsx");
const robotsFilePath = path.join("public", "robots.txt"); const robotsFilePath = path.join("public", "robots.txt");
await po.sendPrompt("foo"); await po.sendPrompt("foo");
...@@ -51,15 +53,15 @@ test("edit code edits the right file", async ({ po }) => { ...@@ -51,15 +53,15 @@ test("edit code edits the right file", async ({ po }) => {
await po.previewPanel.selectPreviewMode("code"); await po.previewPanel.selectPreviewMode("code");
await po.page.getByText("made-with-dyad.tsx").click(); await po.page.getByText("made-with-dyad.tsx").click();
await po.page // Wait for the editor to load and then fill in the new content
.getByRole("code") const editorContent = po.page.getByRole("textbox", {
.locator("div") name: "Editor content",
.filter({ hasText: "export const" }) });
.nth(4) await expect(editorContent).toBeVisible();
.click(); // Monaco editor intercepts pointer events, so we need to use force: true
await po.page await editorContent.click({ force: true });
.getByRole("textbox", { name: "Editor content" }) await po.page.keyboard.press("ControlOrMeta+a");
.fill("export const MadeWithDyad = ;"); await po.page.keyboard.type("export const MadeWithDyad = ;");
// Save the file by switching files // Save the file by switching files
await po.page.getByText("robots.txt").click(); await po.page.getByText("robots.txt").click();
......
...@@ -34,7 +34,7 @@ testSkipIfWindows( ...@@ -34,7 +34,7 @@ testSkipIfWindows(
// After the agent runs type checks, the Problems panel should show errors // After the agent runs type checks, the Problems panel should show errors
// Wait for the fix button to be enabled and show errors // Wait for the fix button to be enabled and show errors
await expect(fixButton).toBeEnabled({ timeout: Timeout.LONG }); await expect(fixButton).toBeEnabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix \d+ problem/); await expect(fixButton).toContainText(/Fix \d+ problem\(s\)/);
// Verify the problems are displayed // Verify the problems are displayed
const problemRows = po.page.getByTestId("problem-row"); const problemRows = po.page.getByTestId("problem-row");
......
...@@ -105,8 +105,10 @@ testSkipIfWindows("mcp - call calculator via http", async ({ po }) => { ...@@ -105,8 +105,10 @@ testSkipIfWindows("mcp - call calculator via http", async ({ po }) => {
await po.page.getByRole("button", { name: "Add Server" }).click(); await po.page.getByRole("button", { name: "Add Server" }).click();
// Wait for the server to be created and the "Add Header" button to become visible // Wait for the server to be created and the "Add Environment Variable" button (for headers) to become visible
const addHeaderButton = po.page.getByRole("button", { name: "Add Header" }); const addHeaderButton = po.page.getByRole("button", {
name: "Add Environment Variable",
});
await expect(addHeaderButton).toBeVisible({ timeout: 10000 }); await expect(addHeaderButton).toBeVisible({ timeout: 10000 });
await addHeaderButton.click(); await addHeaderButton.click();
await po.page.getByRole("textbox", { name: "Key" }).fill("Authorization"); await po.page.getByRole("textbox", { name: "Key" }).fill("Authorization");
......
...@@ -114,7 +114,7 @@ export default App; ...@@ -114,7 +114,7 @@ export default App;
// Initially, all selected: button shows Fix X problems and Clear all is visible // Initially, all selected: button shows Fix X problems and Clear all is visible
const fixButton = po.page.getByTestId("fix-all-button"); const fixButton = po.page.getByTestId("fix-all-button");
await expect(fixButton).toBeVisible(); await expect(fixButton).toBeVisible();
await expect(fixButton).toContainText(/Fix \d+ problems/); await expect(fixButton).toContainText(/Fix \d+ problem\(s\)/);
// Click first two rows to toggle off (deselect) // Click first two rows to toggle off (deselect)
const rows = po.page.getByTestId("problem-row"); const rows = po.page.getByTestId("problem-row");
...@@ -124,7 +124,7 @@ export default App; ...@@ -124,7 +124,7 @@ export default App;
await rows.nth(1).click(); await rows.nth(1).click();
// Button should update to reflect remaining selected // Button should update to reflect remaining selected
await expect(fixButton).toContainText(/Fix 1 problem/); await expect(fixButton).toContainText(/Fix 1 problem\(s\)/);
// Clear all should switch to Select all when none selected // Clear all should switch to Select all when none selected
// Deselect remaining rows // Deselect remaining rows
...@@ -141,7 +141,7 @@ export default App; ...@@ -141,7 +141,7 @@ export default App;
await selectButton.click(); await selectButton.click();
// Unselect the second row // Unselect the second row
await rows.nth(1).click(); await rows.nth(1).click();
await expect(fixButton).toContainText(/Fix 2 problems/); await expect(fixButton).toContainText(/Fix 2 problem\(s\)/);
await fixButton.click(); await fixButton.click();
await po.chatActions.waitForChatCompletion(); await po.chatActions.waitForChatCompletion();
...@@ -170,13 +170,13 @@ export default App; ...@@ -170,13 +170,13 @@ export default App;
await po.previewPanel.selectPreviewMode("problems"); await po.previewPanel.selectPreviewMode("problems");
const fixButton = po.page.getByTestId("fix-all-button"); const fixButton = po.page.getByTestId("fix-all-button");
await expect(fixButton).toBeEnabled({ timeout: Timeout.LONG }); await expect(fixButton).toBeEnabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix 1 problem/); await expect(fixButton).toContainText(/Fix 1 problem\(s\)/);
fs.unlinkSync(badFilePath); fs.unlinkSync(badFilePath);
await po.previewPanel.clickRecheckProblems(); await po.previewPanel.clickRecheckProblems();
await expect(fixButton).toBeDisabled({ timeout: Timeout.LONG }); await expect(fixButton).toBeDisabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix 0 problems/); await expect(fixButton).toContainText(/Fix 0 problem\(s\)/);
}); });
testSkipIfWindows("problems - manual edit (next.js)", async ({ po }) => { testSkipIfWindows("problems - manual edit (next.js)", async ({ po }) => {
...@@ -200,11 +200,11 @@ testSkipIfWindows("problems - manual edit (next.js)", async ({ po }) => { ...@@ -200,11 +200,11 @@ testSkipIfWindows("problems - manual edit (next.js)", async ({ po }) => {
await po.previewPanel.selectPreviewMode("problems"); await po.previewPanel.selectPreviewMode("problems");
const fixButton = po.page.getByTestId("fix-all-button"); const fixButton = po.page.getByTestId("fix-all-button");
await expect(fixButton).toBeEnabled({ timeout: Timeout.LONG }); await expect(fixButton).toBeEnabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix 1 problem/); await expect(fixButton).toContainText(/Fix 1 problem\(s\)/);
fs.unlinkSync(badFilePath); fs.unlinkSync(badFilePath);
await po.previewPanel.clickRecheckProblems(); await po.previewPanel.clickRecheckProblems();
await expect(fixButton).toBeDisabled({ timeout: Timeout.LONG }); await expect(fixButton).toBeDisabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix 0 problems/); await expect(fixButton).toContainText(/Fix 0 problem\(s\)/);
}); });
- "defaultChatMode": "local-agent" - "defaultChatMode": "local-agent"
+ "defaultChatMode": "agent" + "defaultChatMode": "build"
\ No newline at end of file \ No newline at end of file
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- button "Run checks": - button "Run checks":
- img - img
- button "Clear all" - button "Clear all"
- button "Fix 3 problems": - button "Fix 3 problem(s)":
- img - img
- checkbox "Select problem src/test-errors.ts 2:7 Type 'string' is not assignable to type 'number'." [checked]: - checkbox "Select problem src/test-errors.ts 2:7 Type 'string' is not assignable to type 'number'." [checked]:
- checkbox "Select problem" [checked]: - checkbox "Select problem" [checked]:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论