236 lines
8.4 KiB
TypeScript
236 lines
8.4 KiB
TypeScript
|
|
import { describe, it, expect, beforeEach } from "vitest";
|
||
|
|
import {
|
||
|
|
useBottomPanelStore,
|
||
|
|
BottomPanelTabType,
|
||
|
|
DEFAULT_PANEL_HEIGHT,
|
||
|
|
MIN_PANEL_HEIGHT,
|
||
|
|
MAX_PANEL_HEIGHT,
|
||
|
|
} from "@/stores/bottomPanelStore";
|
||
|
|
|
||
|
|
describe("bottomPanelStore", () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
localStorage.clear();
|
||
|
|
// Reset store to initial state
|
||
|
|
useBottomPanelStore.setState({
|
||
|
|
isOpen: false,
|
||
|
|
height: DEFAULT_PANEL_HEIGHT,
|
||
|
|
tabs: [],
|
||
|
|
activeTabId: null,
|
||
|
|
nextTabIndex: 1,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("initial state", () => {
|
||
|
|
it("is closed by default", () => {
|
||
|
|
expect(useBottomPanelStore.getState().isOpen).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("has default height", () => {
|
||
|
|
expect(useBottomPanelStore.getState().height).toBe(DEFAULT_PANEL_HEIGHT);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("has no tabs", () => {
|
||
|
|
expect(useBottomPanelStore.getState().tabs).toEqual([]);
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBeNull();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("openPanel / closePanel / togglePanel", () => {
|
||
|
|
it("openPanel sets isOpen to true", () => {
|
||
|
|
useBottomPanelStore.getState().openPanel();
|
||
|
|
expect(useBottomPanelStore.getState().isOpen).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("closePanel sets isOpen to false", () => {
|
||
|
|
useBottomPanelStore.getState().openPanel();
|
||
|
|
useBottomPanelStore.getState().closePanel();
|
||
|
|
expect(useBottomPanelStore.getState().isOpen).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("togglePanel flips isOpen", () => {
|
||
|
|
const { togglePanel } = useBottomPanelStore.getState();
|
||
|
|
togglePanel();
|
||
|
|
expect(useBottomPanelStore.getState().isOpen).toBe(true);
|
||
|
|
togglePanel();
|
||
|
|
expect(useBottomPanelStore.getState().isOpen).toBe(false);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("setHeight", () => {
|
||
|
|
it("clamps to minimum", () => {
|
||
|
|
useBottomPanelStore.getState().setHeight(MIN_PANEL_HEIGHT - 50);
|
||
|
|
expect(useBottomPanelStore.getState().height).toBe(MIN_PANEL_HEIGHT);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("clamps to maximum", () => {
|
||
|
|
useBottomPanelStore.getState().setHeight(MAX_PANEL_HEIGHT + 1000);
|
||
|
|
expect(useBottomPanelStore.getState().height).toBe(MAX_PANEL_HEIGHT);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("accepts a value within range", () => {
|
||
|
|
useBottomPanelStore.getState().setHeight(450);
|
||
|
|
expect(useBottomPanelStore.getState().height).toBe(450);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("openTab", () => {
|
||
|
|
it("creates a new tab and opens the panel", () => {
|
||
|
|
const id = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.POD_LOGS,
|
||
|
|
title: "Pod Logs: nginx",
|
||
|
|
data: { clusterId: "c1", namespace: "default", podName: "nginx", containers: ["nginx"] },
|
||
|
|
});
|
||
|
|
|
||
|
|
const state = useBottomPanelStore.getState();
|
||
|
|
expect(state.tabs).toHaveLength(1);
|
||
|
|
expect(state.tabs[0]!.id).toBe(id);
|
||
|
|
expect(state.tabs[0]!.type).toBe(BottomPanelTabType.POD_LOGS);
|
||
|
|
expect(state.activeTabId).toBe(id);
|
||
|
|
expect(state.isOpen).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("returns existing tab id when same type+key already open", () => {
|
||
|
|
const a = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.POD_LOGS,
|
||
|
|
title: "nginx",
|
||
|
|
key: "default/nginx",
|
||
|
|
});
|
||
|
|
const b = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.POD_LOGS,
|
||
|
|
title: "nginx",
|
||
|
|
key: "default/nginx",
|
||
|
|
});
|
||
|
|
expect(a).toBe(b);
|
||
|
|
expect(useBottomPanelStore.getState().tabs).toHaveLength(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("supports all 6 tab types", () => {
|
||
|
|
const types = [
|
||
|
|
BottomPanelTabType.POD_LOGS,
|
||
|
|
BottomPanelTabType.TERMINAL,
|
||
|
|
BottomPanelTabType.EDIT_RESOURCE,
|
||
|
|
BottomPanelTabType.CREATE_RESOURCE,
|
||
|
|
BottomPanelTabType.INSTALL_CHART,
|
||
|
|
BottomPanelTabType.UPGRADE_CHART,
|
||
|
|
];
|
||
|
|
for (const t of types) {
|
||
|
|
useBottomPanelStore.getState().openTab({ type: t, title: t });
|
||
|
|
}
|
||
|
|
expect(useBottomPanelStore.getState().tabs).toHaveLength(6);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("closeTab", () => {
|
||
|
|
it("removes the tab and updates active tab", () => {
|
||
|
|
const a = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.TERMINAL,
|
||
|
|
title: "term1",
|
||
|
|
});
|
||
|
|
const b = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.TERMINAL,
|
||
|
|
title: "term2",
|
||
|
|
});
|
||
|
|
useBottomPanelStore.getState().closeTab(b);
|
||
|
|
expect(useBottomPanelStore.getState().tabs).toHaveLength(1);
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(a);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("closes the panel when last tab is removed", () => {
|
||
|
|
const id = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.TERMINAL,
|
||
|
|
title: "term1",
|
||
|
|
});
|
||
|
|
useBottomPanelStore.getState().closeTab(id);
|
||
|
|
expect(useBottomPanelStore.getState().isOpen).toBe(false);
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("focuses previous tab when active tab is closed", () => {
|
||
|
|
const a = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.TERMINAL,
|
||
|
|
title: "a",
|
||
|
|
});
|
||
|
|
const b = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.TERMINAL,
|
||
|
|
title: "b",
|
||
|
|
});
|
||
|
|
const c = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.TERMINAL,
|
||
|
|
title: "c",
|
||
|
|
});
|
||
|
|
// active is c — close c, should fall back to b
|
||
|
|
useBottomPanelStore.getState().closeTab(c);
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(b);
|
||
|
|
// close a (not active) — active should remain b
|
||
|
|
useBottomPanelStore.getState().closeTab(a);
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(b);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("setActiveTab", () => {
|
||
|
|
it("updates the active tab id", () => {
|
||
|
|
const a = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.TERMINAL,
|
||
|
|
title: "a",
|
||
|
|
});
|
||
|
|
const b = useBottomPanelStore.getState().openTab({
|
||
|
|
type: BottomPanelTabType.TERMINAL,
|
||
|
|
title: "b",
|
||
|
|
});
|
||
|
|
useBottomPanelStore.getState().setActiveTab(a);
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(a);
|
||
|
|
useBottomPanelStore.getState().setActiveTab(b);
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(b);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("nextTab / previousTab", () => {
|
||
|
|
it("cycles forward through tabs", () => {
|
||
|
|
const a = useBottomPanelStore.getState().openTab({ type: BottomPanelTabType.TERMINAL, title: "a" });
|
||
|
|
const b = useBottomPanelStore.getState().openTab({ type: BottomPanelTabType.TERMINAL, title: "b" });
|
||
|
|
const c = useBottomPanelStore.getState().openTab({ type: BottomPanelTabType.TERMINAL, title: "c" });
|
||
|
|
useBottomPanelStore.getState().setActiveTab(a);
|
||
|
|
|
||
|
|
useBottomPanelStore.getState().nextTab();
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(b);
|
||
|
|
useBottomPanelStore.getState().nextTab();
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(c);
|
||
|
|
// wraps
|
||
|
|
useBottomPanelStore.getState().nextTab();
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(a);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("cycles backward through tabs", () => {
|
||
|
|
const a = useBottomPanelStore.getState().openTab({ type: BottomPanelTabType.TERMINAL, title: "a" });
|
||
|
|
const b = useBottomPanelStore.getState().openTab({ type: BottomPanelTabType.TERMINAL, title: "b" });
|
||
|
|
const c = useBottomPanelStore.getState().openTab({ type: BottomPanelTabType.TERMINAL, title: "c" });
|
||
|
|
useBottomPanelStore.getState().setActiveTab(a);
|
||
|
|
|
||
|
|
useBottomPanelStore.getState().previousTab();
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(c);
|
||
|
|
useBottomPanelStore.getState().previousTab();
|
||
|
|
expect(useBottomPanelStore.getState().activeTabId).toBe(b);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("closeActiveTab", () => {
|
||
|
|
it("removes whatever tab is currently active", () => {
|
||
|
|
const a = useBottomPanelStore.getState().openTab({ type: BottomPanelTabType.TERMINAL, title: "a" });
|
||
|
|
useBottomPanelStore.getState().openTab({ type: BottomPanelTabType.TERMINAL, title: "b" });
|
||
|
|
useBottomPanelStore.getState().setActiveTab(a);
|
||
|
|
useBottomPanelStore.getState().closeActiveTab();
|
||
|
|
const remaining = useBottomPanelStore.getState().tabs.map((t) => t.id);
|
||
|
|
expect(remaining).not.toContain(a);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("persistence", () => {
|
||
|
|
it("persists height to localStorage", () => {
|
||
|
|
useBottomPanelStore.getState().setHeight(420);
|
||
|
|
const raw = localStorage.getItem("tftsr-bottom-panel");
|
||
|
|
expect(raw).not.toBeNull();
|
||
|
|
expect(raw!).toContain("420");
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|