feat(FN-3891): expose verification auto-fix retries in merge settings
- Add verificationFixRetries input to SettingsModal merge section with 0-3 clamping - Default to 3 retries with proper empty-value fallback to undefined - Cover edge cases with SettingsModal tests: default, valid values, clamping, clearing - Add patch changeset for @runfusion/fusion - Update settings-reference.md with revised default and description Fusion-Task-Id: FN-3891
This commit is contained in:
@@ -3560,19 +3560,38 @@ export function SettingsModal({
|
||||
</details>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="verificationFixRetries">Verification fix retries</label>
|
||||
<label htmlFor="verificationFixRetries">Verification auto-fix retries</label>
|
||||
<input
|
||||
id="verificationFixRetries"
|
||||
className="input"
|
||||
type="number"
|
||||
min={0}
|
||||
max={3}
|
||||
value={form.verificationFixRetries ?? ""}
|
||||
step={1}
|
||||
value={form.verificationFixRetries ?? 3}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setForm((f) => ({ ...f, verificationFixRetries: val === "" ? undefined : Number(val) } as SettingsFormState));
|
||||
const rawValue = e.target.value;
|
||||
if (rawValue === "") {
|
||||
setForm((f) => ({ ...f, verificationFixRetries: undefined } as SettingsFormState));
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedValue = Number.parseInt(rawValue, 10);
|
||||
if (!Number.isFinite(parsedValue)) {
|
||||
setForm((f) => ({ ...f, verificationFixRetries: undefined } as SettingsFormState));
|
||||
return;
|
||||
}
|
||||
|
||||
const clampedValue = Math.max(0, Math.min(3, parsedValue));
|
||||
setForm((f) => ({ ...f, verificationFixRetries: clampedValue } as SettingsFormState));
|
||||
}}
|
||||
/>
|
||||
<small>Number of automatic fix attempts after failed merge verification (0–3)</small>
|
||||
<details className="settings-option-details">
|
||||
<summary>More details</summary>
|
||||
<small>
|
||||
Controls in-merge fix attempts after deterministic test/build verification failures (0-3).
|
||||
</small>
|
||||
</details>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="mergeStrategy">Auto-completion mode</label>
|
||||
|
||||
@@ -1896,23 +1896,65 @@ describe("SettingsModal", () => {
|
||||
expect(payload.pushRemote).toBe("upstream main");
|
||||
});
|
||||
|
||||
it("renders and saves verification fix retries", async () => {
|
||||
renderModal({ initialSection: "merge" });
|
||||
await waitForSettingsModalReady();
|
||||
describe("verificationFixRetries", () => {
|
||||
it("shows default value 3 when verificationFixRetries is not set", async () => {
|
||||
mockFetchSettings.mockResolvedValueOnce({
|
||||
...defaultSettings,
|
||||
verificationFixRetries: undefined,
|
||||
});
|
||||
|
||||
const retriesInput = screen.getByLabelText("Verification fix retries") as HTMLInputElement;
|
||||
expect(retriesInput.value).toBe("2");
|
||||
renderModal({ initialSection: "merge" });
|
||||
await waitForSettingsModalReady();
|
||||
|
||||
await userEvent.clear(retriesInput);
|
||||
await userEvent.type(retriesInput, "1");
|
||||
await userEvent.click(screen.getByRole("button", { name: "Save" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateSettings).toHaveBeenCalled();
|
||||
const retriesInput = screen.getByLabelText("Verification auto-fix retries") as HTMLInputElement;
|
||||
expect(retriesInput.value).toBe("3");
|
||||
});
|
||||
|
||||
const payload = mockUpdateSettings.mock.calls[0][0] as Record<string, unknown>;
|
||||
expect(payload.verificationFixRetries).toBe(1);
|
||||
it.each([0, 1, 2, 3])("persists valid value %i", async (value) => {
|
||||
renderModal({ initialSection: "merge" });
|
||||
await waitForSettingsModalReady();
|
||||
|
||||
const retriesInput = screen.getByLabelText("Verification auto-fix retries") as HTMLInputElement;
|
||||
fireEvent.change(retriesInput, { target: { value: String(value) } });
|
||||
await userEvent.click(screen.getByRole("button", { name: "Save" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateSettings).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const payload = mockUpdateSettings.mock.calls[0][0] as Record<string, unknown>;
|
||||
expect(payload.verificationFixRetries).toBe(value);
|
||||
});
|
||||
|
||||
it("clamps out-of-range values", async () => {
|
||||
renderModal({ initialSection: "merge" });
|
||||
await waitForSettingsModalReady();
|
||||
|
||||
const retriesInput = screen.getByLabelText("Verification auto-fix retries") as HTMLInputElement;
|
||||
|
||||
fireEvent.change(retriesInput, { target: { value: "5" } });
|
||||
expect(retriesInput.value).toBe("3");
|
||||
|
||||
fireEvent.change(retriesInput, { target: { value: "-1" } });
|
||||
expect(retriesInput.value).toBe("0");
|
||||
});
|
||||
|
||||
it("saving after clearing input persists undefined and falls back to visible default 3", async () => {
|
||||
renderModal({ initialSection: "merge" });
|
||||
await waitForSettingsModalReady();
|
||||
|
||||
const retriesInput = screen.getByLabelText("Verification auto-fix retries") as HTMLInputElement;
|
||||
await userEvent.clear(retriesInput);
|
||||
await userEvent.click(screen.getByRole("button", { name: "Save" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateSettings).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const payload = mockUpdateSettings.mock.calls[0][0] as Record<string, unknown>;
|
||||
expect(payload.verificationFixRetries).toBeUndefined();
|
||||
expect(retriesInput.value).toBe("3");
|
||||
});
|
||||
});
|
||||
|
||||
it("renders and saves github issue tracking controls", async () => {
|
||||
|
||||
Reference in New Issue
Block a user