mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-28 00:43:57 +00:00
fix: restore reveal-to-edit raw config flow
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { render } from "lit";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ThemeMode, ThemeName } from "../theme.ts";
|
||||
import { renderConfig } from "./config.ts";
|
||||
import { renderConfig, type ConfigProps } from "./config.ts";
|
||||
|
||||
describe("config view", () => {
|
||||
const baseProps = () => ({
|
||||
@@ -59,6 +59,49 @@ describe("config view", () => {
|
||||
};
|
||||
}
|
||||
|
||||
function renderConfigView(overrides: Partial<ConfigProps> = {}): {
|
||||
container: HTMLElement;
|
||||
props: ConfigProps;
|
||||
} {
|
||||
const container = document.createElement("div");
|
||||
const props = {
|
||||
...baseProps(),
|
||||
...overrides,
|
||||
};
|
||||
const rerender = () =>
|
||||
render(
|
||||
renderConfig({
|
||||
...props,
|
||||
onRequestUpdate: rerender,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
rerender();
|
||||
return { container, props };
|
||||
}
|
||||
|
||||
function normalizedText(container: HTMLElement): string {
|
||||
return container.textContent?.replace(/\s+/g, " ").trim() ?? "";
|
||||
}
|
||||
|
||||
function resetRawRevealState() {
|
||||
const { container } = renderConfigView({
|
||||
formMode: "raw",
|
||||
raw: '{\n "openai": { "apiKey": "supersecret" }\n}\n',
|
||||
originalRaw: '{\n "openai": { "apiKey": "supersecret" }\n}\n',
|
||||
formValue: {
|
||||
openai: {
|
||||
apiKey: "supersecret",
|
||||
},
|
||||
},
|
||||
});
|
||||
container.querySelector<HTMLButtonElement>(".config-raw-toggle.active")?.click();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
resetRawRevealState();
|
||||
});
|
||||
|
||||
it("allows save when form is unsafe", () => {
|
||||
const container = document.createElement("div");
|
||||
render(
|
||||
@@ -242,7 +285,6 @@ describe("config view", () => {
|
||||
expect(tabs).toContain("Settings");
|
||||
expect(tabs).toContain("Agents");
|
||||
expect(tabs).toContain("Gateway");
|
||||
expect(tabs).toContain("Appearance");
|
||||
});
|
||||
|
||||
it("clears the active search query", () => {
|
||||
@@ -262,4 +304,52 @@ describe("config view", () => {
|
||||
clearButton?.click();
|
||||
expect(onSearchChange).toHaveBeenCalledWith("");
|
||||
});
|
||||
|
||||
it("keeps sensitive raw config hidden until reveal", () => {
|
||||
const { container } = renderConfigView({
|
||||
formMode: "raw",
|
||||
raw: '{\n "openai": { "apiKey": "supersecret" }\n}\n',
|
||||
originalRaw: '{\n "openai": { "apiKey": "supersecret" }\n}\n',
|
||||
formValue: {
|
||||
openai: {
|
||||
apiKey: "supersecret",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const text = normalizedText(container);
|
||||
expect(text).toContain("1 secret redacted");
|
||||
expect(text).toContain("Use the reveal button above to edit the raw config.");
|
||||
expect(text).not.toContain("supersecret");
|
||||
expect(container.querySelector("textarea")).toBeNull();
|
||||
});
|
||||
|
||||
it("reveals sensitive raw config before editing", () => {
|
||||
const onRawChange = vi.fn();
|
||||
const { container } = renderConfigView({
|
||||
formMode: "raw",
|
||||
raw: '{\n "openai": { "apiKey": "supersecret" }\n}\n',
|
||||
originalRaw: '{\n "openai": { "apiKey": "supersecret" }\n}\n',
|
||||
formValue: {
|
||||
openai: {
|
||||
apiKey: "supersecret",
|
||||
},
|
||||
},
|
||||
onRawChange,
|
||||
});
|
||||
|
||||
const revealButton = container.querySelector<HTMLButtonElement>(".config-raw-toggle");
|
||||
expect(revealButton).toBeTruthy();
|
||||
revealButton?.click();
|
||||
|
||||
const textarea = container.querySelector<HTMLTextAreaElement>("textarea");
|
||||
expect(textarea).not.toBeNull();
|
||||
expect(textarea?.value).toContain("supersecret");
|
||||
if (!textarea) {
|
||||
return;
|
||||
}
|
||||
textarea.value = textarea.value.replace("supersecret", "updatedsecret");
|
||||
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
expect(onRawChange).toHaveBeenCalledWith(textarea.value);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1117,18 +1117,24 @@ export function renderConfig(props: ConfigProps) {
|
||||
: nothing
|
||||
}
|
||||
</span>
|
||||
<textarea
|
||||
class="${blurred ? "config-raw-redacted" : ""}"
|
||||
placeholder=${blurred ? REDACTED_PLACEHOLDER : "Raw config (JSON/JSON5)"}
|
||||
.value=${blurred ? "" : props.raw}
|
||||
?readonly=${blurred}
|
||||
@input=${(e: Event) => {
|
||||
if (blurred) {
|
||||
return;
|
||||
}
|
||||
props.onRawChange((e.target as HTMLTextAreaElement).value);
|
||||
}}
|
||||
></textarea>
|
||||
${
|
||||
blurred
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
${sensitiveCount} sensitive value${sensitiveCount === 1 ? "" : "s"} hidden. Use the
|
||||
reveal button above to edit the raw config.
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<textarea
|
||||
placeholder="Raw config (JSON/JSON5)"
|
||||
.value=${props.raw}
|
||||
@input=${(e: Event) => {
|
||||
props.onRawChange((e.target as HTMLTextAreaElement).value);
|
||||
}}
|
||||
></textarea>
|
||||
`
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
})()
|
||||
|
||||
Reference in New Issue
Block a user