import { DEFAULT_UNIFIED_SETTINGS } from "@t3tools/contracts/settings";
import { act, StrictMode, type ReactNode } from "react";
import { create, type ReactTestRenderer } from "react-test-renderer";
import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test";
import type { ScopedSettingsPatch } from "./scopedSettings";
type WritingStyle = typeof DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle;
const state = vi.hoisted(() => ({
styles: [] as WritingStyle[],
updateSettings: vi.fn<(patch: ScopedSettingsPatch) => void>(),
}));
vi.mock("@tanstack/react-router", () => ({ useNavigate: () => vi.fn() }));
vi.mock("./useScopedSettings", () => ({
useScopedSettings: () => ({
...DEFAULT_UNIFIED_SETTINGS,
sourceControlWritingStyle: state.styles[0],
}),
useScopedSettingsMixed: () => JSON.stringify(state.styles[0]) !== JSON.stringify(state.styles[1]),
useUpdateScopedSettings: () => state.updateSettings,
}));
vi.mock("./SettingsScopeContext", () => ({
useSettingsScope: () => ({
scope: { kind: "all", environmentIds: [] },
environment: null,
connectedEnvironments: [],
targets: [],
}),
}));
vi.mock("./useScopedModelAvailability", () => ({
useScopedModelDisabledReason: () => () => null,
}));
vi.mock("../ui/toast", () => ({ toastManager: { add: vi.fn() } }));
vi.mock("../../state/server", () => ({ EMPTY_SERVER_PROVIDERS: [] }));
vi.mock("../chat/ProviderModelPicker", () => ({ ProviderModelPicker: () => null }));
vi.mock("./settingsSearch", () => ({ searchableSetting: (id: string) => ({ id, title: id }) }));
vi.mock("./settingsLayout", () => ({
SETTINGS_PICKER_TRIGGER_CLASSNAME: "",
SettingResetButton: ({ label, onClick }: { label: string; onClick: () => void }) => (
),
SettingsSection: ({ children }: { children: ReactNode }) => children,
SettingsRow: ({
children,
control,
resetAction,
}: {
children: ReactNode;
control: ReactNode;
resetAction: ReactNode;
}) => (
{control}
{resetAction}
{children}
),
}));
vi.mock("../ui/select", () => ({
Select: ({ children }: { children: ReactNode }) => children,
SelectItem: "span",
SelectPopup: "div",
SelectTrigger: "div",
SelectValue: "span",
}));
vi.mock("../ui/switch", () => ({ Switch: "input" }));
vi.mock("../ui/textarea", () => ({ Textarea: "textarea" }));
vi.mock("../ui/button", () => ({ Button: "button" }));
import { SourceControlWritingSettingsSection } from "./SourceControlWritingSettings";
let renderer: ReactTestRenderer | null;
function button(label: string) {
return renderer!.root.findAllByType("button").find((item) => item.children.includes(label))!;
}
function openEditor() {
act(() => button("Write custom instructions for all").props.onClick());
}
function editInstructions(value: string) {
act(() => renderer!.root.findByType("textarea").props.onChange({ target: { value } }));
}
function applyInstructions() {
act(() => button("Apply instructions to all").props.onClick());
}
beforeEach(() => {
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
state.styles = [
{
...DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle,
mode: "custom",
customInstructions: "First environment instructions",
followChangeRequestTemplates: true,
},
{
...DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle,
mode: "custom",
customInstructions: "Second environment instructions",
followChangeRequestTemplates: false,
},
];
state.updateSettings.mockReset().mockImplementation((patch) => {
state.styles = state.styles.map((style) => ({ ...style, ...patch.sourceControlWritingStyle }));
});
act(() => {
renderer = create(
,
);
});
});
afterEach(async () => {
await act(async () => renderer?.unmount());
renderer = null;
vi.unstubAllGlobals();
});
describe("mixed source control instructions", () => {
it("resets mixed template preferences without replacing each environment's instructions", () => {
const initialInstructions = state.styles.map(({ mode, customInstructions }) => ({
mode,
customInstructions,
}));
act(() => button("Reset change request templates").props.onClick());
expect(state.updateSettings).toHaveBeenCalledTimes(1);
expect(state.styles.map((style) => style.followChangeRequestTemplates)).toEqual([
DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle.followChangeRequestTemplates,
DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle.followChangeRequestTemplates,
]);
expect(
state.styles.map(({ mode, customInstructions }) => ({ mode, customInstructions })),
).toEqual(initialInstructions);
});
it("resets every environment even when the representative already has default instructions", () => {
state.styles[0] = { ...DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle };
act(() => {
renderer!.update(
,
);
});
act(() => button("Reset source control writing style").props.onClick());
expect(state.updateSettings).toHaveBeenCalledTimes(1);
expect(
state.styles.map(({ mode, customInstructions }) => ({ mode, customInstructions })),
).toEqual(
[0, 1].map(() => ({
mode: DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle.mode,
customInstructions: DEFAULT_UNIFIED_SETTINGS.sourceControlWritingStyle.customInstructions,
})),
);
expect(state.styles[1]!.followChangeRequestTemplates).toBe(false);
});
it("does not write an untouched bulk draft", () => {
const initialStyles = state.styles;
openEditor();
expect(renderer!.root.findByType("textarea").props.value).toBe("");
expect(button("Apply instructions to all").props.disabled).toBe(true);
applyInstructions();
expect(state.updateSettings).not.toHaveBeenCalled();
expect(state.styles).toEqual(initialStyles);
});
it("applies edited instructions to every selected environment", () => {
openEditor();
editInstructions(" Keep titles concise. ");
expect(button("Apply instructions to all").props.disabled).toBe(false);
applyInstructions();
expect(state.updateSettings).toHaveBeenCalledTimes(1);
expect(state.styles.map((style) => style.customInstructions)).toEqual([
"Keep titles concise.",
"Keep titles concise.",
]);
expect(state.styles.map((style) => style.followChangeRequestTemplates)).toEqual([true, false]);
});
it("allows an intentional clear after editing", () => {
openEditor();
editInstructions("Temporary instructions");
editInstructions("");
expect(button("Apply instructions to all").props.disabled).toBe(false);
applyInstructions();
expect(state.updateSettings).toHaveBeenCalledTimes(1);
expect(state.styles.map((style) => style.customInstructions)).toEqual(["", ""]);
});
it("resets the draft when the bulk editor reopens", () => {
openEditor();
editInstructions("Shared instructions");
applyInstructions();
// Template preferences still differ, so this row remains mixed after the save.
openEditor();
expect(renderer!.root.findByType("textarea").props.value).toBe("");
expect(button("Apply instructions to all").props.disabled).toBe(true);
applyInstructions();
expect(state.updateSettings).toHaveBeenCalledTimes(1);
expect(state.styles.map((style) => style.customInstructions)).toEqual([
"Shared instructions",
"Shared instructions",
]);
});
});