Allow selecting problems (#1568)

Fixes #672 


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Add selectable problem rows with Select all/Clear all and Fix N
selected, and update tests to cover selection behavior.
> 
> - **UI (Problems panel)**:
> - Add checkbox selection for each problem row (`ProblemItem`) with row
click-to-toggle, `data-testid="problem-row"`, and accessibility
attributes.
> - Introduce selection state in `_Problems` with auto-select-all on
report load; provide Select all / Clear all controls.
> - Change Fix button to operate on selected problems only, showing
dynamic label `Fix N problem(s)` and disabled when none selected.
> - Wire `RecheckButton` to clear selection before rechecking; minor
hover style tweaks; add `Checkbox` component.
> - **E2E Tests**:
> - New test: selecting specific problems and fixing only selected; add
snapshots for prompt content.
> - Update manual edit tests (React/Vite, Next.js) to assert Fix button
enabled/disabled and counts; remove old ARIA snapshots.
>   - Minor import addition for `Timeout` and related expectations.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8324e26f9d2d265e7e0d1f1b7538e2a8db40f674. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This commit is contained in:
Will Chen
2025-10-23 10:18:55 -07:00
committed by GitHub
parent 517ce5134d
commit 7bed92f782
8 changed files with 243 additions and 61 deletions

View File

@@ -1,4 +1,4 @@
import { test, testSkipIfWindows } from "./helpers/test_helper";
import { test, testSkipIfWindows, Timeout } from "./helpers/test_helper";
import { expect } from "@playwright/test";
import fs from "fs";
import path from "path";
@@ -83,6 +83,72 @@ export default App;
await po.snapshotMessages({ replaceDumpPath: true });
});
testSkipIfWindows(
"problems - select specific problems and fix",
async ({ po }) => {
await po.setUp();
await po.importApp(MINIMAL_APP);
// Create multiple TS errors in one file
const appPath = await po.getCurrentAppPath();
const badFilePath = path.join(appPath, "src", "bad-file.tsx");
fs.writeFileSync(
badFilePath,
`const App = () => <div>Minimal imported app</div>;
nonExistentFunction1();
nonExistentFunction2();
nonExistentFunction3();
export default App;
`,
);
await po.ensurePnpmInstall();
// Trigger creation of problems and open problems panel
// await po.sendPrompt("tc=create-ts-errors");
await po.selectPreviewMode("problems");
await po.clickRecheckProblems();
// Initially, all selected: button shows Fix X problems and Clear all is visible
const fixButton = po.page.getByTestId("fix-all-button");
await expect(fixButton).toBeVisible();
await expect(fixButton).toContainText(/Fix \d+ problems/);
// Click first two rows to toggle off (deselect)
const rows = po.page.getByTestId("problem-row");
const rowCount = await rows.count();
expect(rowCount).toBeGreaterThan(2);
await rows.nth(0).click();
await rows.nth(1).click();
// Button should update to reflect remaining selected
await expect(fixButton).toContainText(/Fix 1 problem/);
// Clear all should switch to Select all when none selected
// Deselect remaining rows
for (let i = 2; i < rowCount; i++) {
await rows.nth(i).click();
}
const selectButton = po.page.getByRole("button", {
name: /Select all/,
});
await expect(selectButton).toHaveText("Select all");
// Select all, then fix selected
await selectButton.click();
// Unselect the second row
await rows.nth(1).click();
await expect(fixButton).toContainText(/Fix 2 problems/);
await fixButton.click();
await po.waitForChatCompletion();
await po.snapshotServerDump("last-message");
await po.snapshotMessages({ replaceDumpPath: true });
},
);
testSkipIfWindows("problems - manual edit (react/vite)", async ({ po }) => {
await po.setUp({ enableAutoFixProblems: true });
await po.sendPrompt("tc=1");
@@ -101,13 +167,15 @@ export default App;
await po.clickTogglePreviewPanel();
await po.selectPreviewMode("problems");
await po.clickRecheckProblems();
await po.snapshotProblemsPane();
const fixButton = po.page.getByTestId("fix-all-button");
await expect(fixButton).toBeEnabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix 1 problem/);
fs.unlinkSync(badFilePath);
await po.clickRecheckProblems();
await po.snapshotProblemsPane();
await expect(fixButton).toBeDisabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix 0 problems/);
});
testSkipIfWindows("problems - manual edit (next.js)", async ({ po }) => {
@@ -129,11 +197,13 @@ testSkipIfWindows("problems - manual edit (next.js)", async ({ po }) => {
await po.clickTogglePreviewPanel();
await po.selectPreviewMode("problems");
await po.clickRecheckProblems();
await po.snapshotProblemsPane();
const fixButton = po.page.getByTestId("fix-all-button");
await expect(fixButton).toBeEnabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix 1 problem/);
fs.unlinkSync(badFilePath);
await po.clickRecheckProblems();
await po.snapshotProblemsPane();
await expect(fixButton).toBeDisabled({ timeout: Timeout.LONG });
await expect(fixButton).toContainText(/Fix 0 problems/);
});

View File

@@ -1,10 +0,0 @@
- img
- text: 1 error
- button "Run checks":
- img
- button "Fix All":
- img
- img
- img
- text: src/bad-file.tsx 2:3
- paragraph: Cannot find name 'nonExistentFunction'.

View File

@@ -1,4 +0,0 @@
- paragraph: No problems found
- img
- button "Run checks":
- img

View File

@@ -1,10 +0,0 @@
- img
- text: 1 error
- button "Run checks":
- img
- button "Fix All":
- img
- img
- img
- text: src/bad-file.tsx 2:1
- paragraph: Cannot find name 'nonExistentFunction'.

View File

@@ -1,4 +0,0 @@
- paragraph: No problems found
- img
- button "Run checks":
- img

View File

@@ -0,0 +1,21 @@
- paragraph: "Fix these 2 TypeScript compile-time errors:"
- list:
- listitem: src/bad-file.tsx:2:1 - Cannot find name 'nonExistentFunction1'. (TS2304)
- code: const App = () => <div>Minimal imported app</div>; nonExistentFunction1(); // <-- TypeScript compiler error here nonExistentFunction2();
- list:
- listitem: src/bad-file.tsx:4:1 - Cannot find name 'nonExistentFunction3'. (TS2304)
- code: nonExistentFunction2(); nonExistentFunction3(); // <-- TypeScript compiler error here
- paragraph: Please fix all errors in a concise way.
- img
- text: bad-file.ts
- button "Edit":
- img
- img
- text: "src/bad-file.ts Summary: Fix 2 errors and introduce a new error."
- paragraph: "[[dyad-dump-path=*]]"
- button:
- img
- img
- text: less than a minute ago
- button "Retry":
- img

View File

@@ -0,0 +1,19 @@
===
role: user
message: Fix these 2 TypeScript compile-time errors:
1. src/bad-file.tsx:2:1 - Cannot find name 'nonExistentFunction1'. (TS2304)
```
const App = () => <div>Minimal imported app</div>;
nonExistentFunction1(); // <-- TypeScript compiler error here
nonExistentFunction2();
```
2. src/bad-file.tsx:4:1 - Cannot find name 'nonExistentFunction3'. (TS2304)
```
nonExistentFunction2();
nonExistentFunction3(); // <-- TypeScript compiler error here
```
Please fix all errors in a concise way.