fix(redis): display console command before response
This commit is contained in:
1 parent
6ec3ff0ccc
commit
60e685d6e6
2 files changed
+67
-14
No files matched your search
@@ -844,6 +844,58 @@ describe("RedisKeyBrowser command completion", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("RedisKeyBrowser command console echo", () => {
|
||||
it("echoes the submitted command to the terminal before the response arrives, then fills in the result", async () => {
|
||||
const pending = deferred<{ value: unknown }>();
|
||||
mocks.redisExecuteCommand.mockReturnValueOnce(pending.promise);
|
||||
mountBrowser();
|
||||
await settle();
|
||||
await openCommandPanel();
|
||||
await setCommandInput("PING");
|
||||
|
||||
const input = requiredElement<HTMLInputElement>("[data-redis-command-input]");
|
||||
input.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
|
||||
await settle();
|
||||
|
||||
// The command must show up in the terminal right away — while the
|
||||
// request is still in flight — not only once the response resolves.
|
||||
const terminal = requiredElement<HTMLElement>(".redis-command-terminal");
|
||||
expect(terminal.textContent).toContain("PING");
|
||||
expect(terminal.querySelectorAll(".mb-2")).toHaveLength(1);
|
||||
expect(terminal.textContent).not.toContain("PONG");
|
||||
|
||||
pending.resolve({ value: "PONG" });
|
||||
await settle();
|
||||
|
||||
expect(terminal.textContent).toContain("PONG");
|
||||
// The result fills in the same echoed entry rather than adding a second one.
|
||||
expect(terminal.querySelectorAll(".mb-2")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("attaches a failed command's error to the same echoed entry", async () => {
|
||||
const pending = deferred<{ value: unknown }>();
|
||||
mocks.redisExecuteCommand.mockReturnValueOnce(pending.promise);
|
||||
mountBrowser();
|
||||
await settle();
|
||||
await openCommandPanel();
|
||||
await setCommandInput("PING");
|
||||
|
||||
const input = requiredElement<HTMLInputElement>("[data-redis-command-input]");
|
||||
input.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
|
||||
await settle();
|
||||
|
||||
const terminal = requiredElement<HTMLElement>(".redis-command-terminal");
|
||||
expect(terminal.textContent).toContain("PING");
|
||||
expect(terminal.querySelectorAll(".mb-2")).toHaveLength(1);
|
||||
|
||||
pending.reject(new Error("ERR unknown command"));
|
||||
await settle();
|
||||
|
||||
expect(terminal.textContent).toContain("ERR unknown command");
|
||||
expect(terminal.querySelectorAll(".mb-2")).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("RedisKeyBrowser expiry creation", () => {
|
||||
it.each(["string", "hash", "list", "set", "zset", "stream", "json"] as const)("writes %s before applying one relative TTL", async (type) => {
|
||||
mountBrowser();
|
||||
|
||||
@@ -936,8 +936,15 @@ function scrollCommandTerminalToEnd() {
|
||||
});
|
||||
}
|
||||
|
||||
function appendCommandHistory(entry: Omit<RedisCommandHistoryEntry, "id">) {
|
||||
commandHistory.value = [...commandHistory.value, { id: ++commandHistoryId, ...entry }];
|
||||
function appendCommandHistory(entry: Omit<RedisCommandHistoryEntry, "id">): number {
|
||||
const id = ++commandHistoryId;
|
||||
commandHistory.value = [...commandHistory.value, { id, ...entry }];
|
||||
scrollCommandTerminalToEnd();
|
||||
return id;
|
||||
}
|
||||
|
||||
function updateCommandHistory(id: number, patch: Partial<Omit<RedisCommandHistoryEntry, "id">>) {
|
||||
commandHistory.value = commandHistory.value.map((entry) => (entry.id === id ? { ...entry, ...patch } : entry));
|
||||
scrollCommandTerminalToEnd();
|
||||
}
|
||||
|
||||
@@ -955,14 +962,13 @@ function appendCommandOutput(entry: Omit<RedisCommandHistoryEntry, "id">) {
|
||||
async function runRedisCommand(command: string) {
|
||||
const prompt = commandPrompt.value;
|
||||
commandRunning.value = true;
|
||||
// Echo the command to the terminal immediately so it doesn't look like the
|
||||
// keystroke was lost while the request is in flight — the output is filled
|
||||
// in on the same entry once the response (or error) arrives.
|
||||
const entryId = appendCommandHistory({ prompt, command, output: "", error: false });
|
||||
try {
|
||||
const result = await api.redisExecuteCommand(props.connectionId, commandDb.value, command, !props.blockDangerousRedisCommands);
|
||||
appendCommandHistory({
|
||||
prompt,
|
||||
command,
|
||||
output: formatRedisConsoleValue(result.value),
|
||||
error: false,
|
||||
});
|
||||
updateCommandHistory(entryId, { output: formatRedisConsoleValue(result.value), error: false });
|
||||
// The db this command ran on — capture before nextRedisCommandDb() advances it.
|
||||
const executedDb = commandDb.value;
|
||||
commandDb.value = nextRedisCommandDb(commandDb.value, command, result.value);
|
||||
@@ -980,12 +986,7 @@ async function runRedisCommand(command: string) {
|
||||
persistRedisHistory(command, true, result.value);
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
appendCommandHistory({
|
||||
prompt,
|
||||
command,
|
||||
output: errorMessage,
|
||||
error: true,
|
||||
});
|
||||
updateCommandHistory(entryId, { output: errorMessage, error: true });
|
||||
// Persist failed command too
|
||||
persistRedisHistory(command, false, null, errorMessage);
|
||||
} finally {
|
||||
|
||||
Reference in new issue
Block a user