feat(HAI-049): add recycleWorktrees checkbox to Settings modal
- Add recycleWorktrees toggle to the Worktrees section in SettingsModal - Wire checkbox state to settings store - Add unit tests for recycleWorktrees setting behavior
This commit is contained in:
@@ -26,7 +26,7 @@ interface SettingsModalProps {
|
||||
}
|
||||
|
||||
export function SettingsModal({ onClose, addToast }: SettingsModalProps) {
|
||||
const [form, setForm] = useState<Settings & { worktreeInitCommand?: string }>({ maxConcurrent: 2, maxWorktrees: 4, pollIntervalMs: 15000, groupOverlappingFiles: false, autoMerge: false, worktreeInitCommand: "" });
|
||||
const [form, setForm] = useState<Settings & { worktreeInitCommand?: string }>({ maxConcurrent: 2, maxWorktrees: 4, pollIntervalMs: 15000, groupOverlappingFiles: false, autoMerge: false, recycleWorktrees: false, worktreeInitCommand: "" });
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [activeSection, setActiveSection] = useState<SectionId>(SETTINGS_SECTIONS[0].id);
|
||||
|
||||
@@ -150,6 +150,20 @@ export function SettingsModal({ onClose, addToast }: SettingsModalProps) {
|
||||
/>
|
||||
<small>Shell command to run in each new worktree after creation</small>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="recycleWorktrees" style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<input
|
||||
id="recycleWorktrees"
|
||||
type="checkbox"
|
||||
checked={form.recycleWorktrees}
|
||||
onChange={(e) =>
|
||||
setForm((f) => ({ ...f, recycleWorktrees: e.target.checked }))
|
||||
}
|
||||
/>
|
||||
Recycle worktrees
|
||||
</label>
|
||||
<small>When enabled, completed task worktrees are returned to an idle pool instead of being deleted, preserving build caches for faster startup</small>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
case "commands":
|
||||
|
||||
@@ -9,6 +9,7 @@ const defaultSettings: Settings = {
|
||||
pollIntervalMs: 15000,
|
||||
groupOverlappingFiles: false,
|
||||
autoMerge: false,
|
||||
recycleWorktrees: false,
|
||||
worktreeInitCommand: "",
|
||||
testCommand: "",
|
||||
buildCommand: "",
|
||||
@@ -75,6 +76,7 @@ describe("SettingsModal", () => {
|
||||
fireEvent.click(screen.getByText("Worktrees"));
|
||||
expect(screen.getByLabelText("Max Worktrees")).toBeTruthy();
|
||||
expect(screen.getByLabelText("Worktree Init Command")).toBeTruthy();
|
||||
expect(screen.getByText("Recycle worktrees")).toBeTruthy();
|
||||
|
||||
// Commands
|
||||
fireEvent.click(screen.getByText("Commands"));
|
||||
@@ -86,6 +88,31 @@ describe("SettingsModal", () => {
|
||||
expect(screen.getByText("Auto-merge completed tasks")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows Recycle worktrees checkbox in Worktrees section", async () => {
|
||||
render(<SettingsModal onClose={onClose} addToast={addToast} />);
|
||||
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
||||
|
||||
fireEvent.click(screen.getByText("Worktrees"));
|
||||
const checkbox = screen.getByLabelText("Recycle worktrees");
|
||||
expect(checkbox).toBeTruthy();
|
||||
expect(checkbox.getAttribute("type")).toBe("checkbox");
|
||||
});
|
||||
|
||||
it("toggling recycleWorktrees checkbox sends true in save payload", async () => {
|
||||
render(<SettingsModal onClose={onClose} addToast={addToast} />);
|
||||
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
||||
|
||||
fireEvent.click(screen.getByText("Worktrees"));
|
||||
const checkbox = screen.getByLabelText("Recycle worktrees");
|
||||
fireEvent.click(checkbox);
|
||||
|
||||
fireEvent.click(screen.getByText("Save"));
|
||||
await waitFor(() => expect(updateSettings).toHaveBeenCalledTimes(1));
|
||||
|
||||
const payload = (updateSettings as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
||||
expect(payload.recycleWorktrees).toBe(true);
|
||||
});
|
||||
|
||||
it("groupOverlappingFiles input has type checkbox", async () => {
|
||||
render(<SettingsModal onClose={onClose} addToast={addToast} />);
|
||||
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
||||
|
||||
Reference in New Issue
Block a user