Skip to content

Commit 16e8c0e

Browse files
committed
add test suite for history
1 parent 4e16bca commit 16e8c0e

File tree

2 files changed

+114
-3
lines changed

2 files changed

+114
-3
lines changed

src/index.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ const Terminal = ({
4747
greenBtnCallback,
4848
TopButtonsPanel = WindowButtons,
4949
}: Props) => {
50+
// local storage key
51+
const terminalHistoryKey = name
52+
? `terminal-history-${name}`
53+
: "terminal-history";
54+
5055
// command history handling
5156
const [historyIndex, setHistoryIndex] = useState(-1);
5257
const [history, setHistory] = useState<string[]>([]);
@@ -115,7 +120,8 @@ const Terminal = ({
115120

116121
// history update
117122
setHistory((previousHistory) =>
118-
currentLineInput.trim() === ""
123+
currentLineInput.trim() === "" ||
124+
previousHistory[previousHistory.length - 1] === currentLineInput.trim()
119125
? previousHistory
120126
: [...previousHistory, currentLineInput],
121127
);
@@ -179,14 +185,14 @@ const Terminal = ({
179185

180186
// history local storage persistency
181187
useEffect(() => {
182-
const storedHistory = localStorage.getItem("terminal-history");
188+
const storedHistory = localStorage.getItem(terminalHistoryKey);
183189
if (storedHistory) {
184190
setHistory(JSON.parse(storedHistory));
185191
}
186192
}, []);
187193

188194
useEffect(() => {
189-
localStorage.setItem("terminalhistory", JSON.stringify(history));
195+
localStorage.setItem(terminalHistoryKey, JSON.stringify(history));
190196
}, [history]);
191197

192198
// We use a hidden input to capture terminal input; make sure the hidden input is focused when clicking anywhere on the terminal

tests/terminal.history.spec.tsx

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React from "react";
2+
import { render, fireEvent, screen } from "@testing-library/react";
3+
import "@testing-library/jest-dom";
4+
import Terminal from "../src/index";
5+
6+
// mock localStorage
7+
beforeEach(() => {
8+
localStorage.clear();
9+
});
10+
11+
describe("Terminal history persistence", () => {
12+
test("loads history from localStorage on mount", () => {
13+
const storedHistory = ["ls", "echo hi"];
14+
localStorage.setItem("terminal-history", JSON.stringify(storedHistory));
15+
16+
render(<Terminal onInput={() => { }} />);
17+
18+
const input = screen.getByPlaceholderText("Terminal Hidden Input");
19+
20+
fireEvent.keyDown(input, { key: "ArrowUp" });
21+
expect(input).toHaveValue("echo hi");
22+
23+
fireEvent.keyDown(input, { key: "ArrowUp" });
24+
expect(input).toHaveValue("ls");
25+
});
26+
27+
test("persists history to localStorage after entering a command", () => {
28+
render(<Terminal onInput={() => { }} />);
29+
30+
const input = screen.getByPlaceholderText("Terminal Hidden Input");
31+
32+
fireEvent.change(input, { target: { value: "help" } });
33+
fireEvent.keyDown(input, { key: "Enter" });
34+
35+
const stored = JSON.parse(localStorage.getItem("terminal-history")!);
36+
37+
expect(stored).toEqual(["help"]);
38+
});
39+
40+
test("does not persist empty commands", () => {
41+
render(<Terminal onInput={() => { }} />);
42+
43+
const input = screen.getByPlaceholderText("Terminal Hidden Input");
44+
45+
fireEvent.change(input, { target: { value: " " } });
46+
fireEvent.keyDown(input, { key: "Enter" });
47+
48+
expect(localStorage.getItem("terminal-history")).toBe(JSON.stringify([]));
49+
});
50+
51+
test("avoids persisting duplicate consecutive commands", () => {
52+
render(<Terminal onInput={() => { }} />);
53+
54+
const input = screen.getByPlaceholderText("Terminal Hidden Input");
55+
56+
fireEvent.change(input, { target: { value: "date" } });
57+
fireEvent.keyDown(input, { key: "Enter" });
58+
59+
fireEvent.change(input, { target: { value: "date" } });
60+
fireEvent.keyDown(input, { key: "Enter" });
61+
62+
const stored = JSON.parse(localStorage.getItem("terminal-history")!);
63+
64+
expect(stored).toEqual(["date"]);
65+
});
66+
67+
test("ArrowUp navigates persisted history", () => {
68+
localStorage.setItem(
69+
"terminal-history",
70+
JSON.stringify(["cmd1", "cmd2", "cmd3"]),
71+
);
72+
73+
render(<Terminal onInput={() => { }} />);
74+
const input = screen.getByPlaceholderText("Terminal Hidden Input");
75+
76+
fireEvent.keyDown(input, { key: "ArrowUp" });
77+
expect(input).toHaveValue("cmd3");
78+
79+
fireEvent.keyDown(input, { key: "ArrowUp" });
80+
expect(input).toHaveValue("cmd2");
81+
82+
fireEvent.keyDown(input, { key: "ArrowUp" });
83+
expect(input).toHaveValue("cmd1");
84+
});
85+
86+
test("ArrowUp stops at oldest history entry and does not go out of bounds", () => {
87+
render(<Terminal onInput={() => { }} />);
88+
89+
const input = screen.getByPlaceholderText("Terminal Hidden Input");
90+
91+
fireEvent.change(input, { target: { value: "first" } });
92+
fireEvent.keyDown(input, { key: "Enter" });
93+
fireEvent.change(input, { target: { value: "second" } });
94+
fireEvent.keyDown(input, { key: "Enter" });
95+
fireEvent.change(input, { target: { value: "third" } });
96+
fireEvent.keyDown(input, { key: "Enter" });
97+
98+
fireEvent.keyDown(input, { key: "ArrowUp" });
99+
fireEvent.keyDown(input, { key: "ArrowUp" });
100+
fireEvent.keyDown(input, { key: "ArrowUp" });
101+
fireEvent.keyDown(input, { key: "ArrowUp" });
102+
103+
expect(input).toHaveValue("first");
104+
});
105+
});

0 commit comments

Comments
 (0)