Show BanGUI app version in sidebar, fix version tooltips
- Inject __APP_VERSION__ at build time via vite.config.ts define (reads
frontend/package.json#version); declare the global in vite-env.d.ts.
- Render 'BanGUI v{__APP_VERSION__}' in the sidebar footer (MainLayout)
when expanded; hidden when collapsed.
- Rename fail2ban version tooltip to 'fail2ban daemon version' in
ServerStatusBar so it is visually distinct from the app version.
- Sync frontend/package.json version (0.9.0 → 0.9.3) to match
Docker/VERSION; update release.sh to keep them in sync on every bump.
- Add vitest define stub for __APP_VERSION__ so tests compile cleanly.
- Add ServerStatusBar and MainLayout test suites (10 new test cases).
This commit is contained in:
@@ -109,7 +109,7 @@ export function ServerStatusBar(): React.JSX.Element {
|
||||
{/* Version */}
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
{status?.version != null && (
|
||||
<Tooltip content="fail2ban version" relationship="description">
|
||||
<Tooltip content="fail2ban daemon version" relationship="description">
|
||||
<Text size={200} className={styles.statValue}>
|
||||
v{status.version}
|
||||
</Text>
|
||||
|
||||
151
frontend/src/components/__tests__/ServerStatusBar.test.tsx
Normal file
151
frontend/src/components/__tests__/ServerStatusBar.test.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Tests for the ServerStatusBar component.
|
||||
*
|
||||
* Covers loading state, online / offline rendering, and correct tooltip
|
||||
* wording that distinguishes the fail2ban daemon version from the BanGUI
|
||||
* application version.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
|
||||
import { ServerStatusBar } from "../ServerStatusBar";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mock useServerStatus so tests never touch the network.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
vi.mock("../../hooks/useServerStatus");
|
||||
|
||||
import { useServerStatus } from "../../hooks/useServerStatus";
|
||||
|
||||
const mockedUseServerStatus = vi.mocked(useServerStatus);
|
||||
|
||||
function renderBar(): void {
|
||||
render(
|
||||
<FluentProvider theme={webLightTheme}>
|
||||
<ServerStatusBar />
|
||||
</FluentProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("ServerStatusBar", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("shows a spinner while the initial load is in progress", () => {
|
||||
mockedUseServerStatus.mockReturnValue({
|
||||
status: null,
|
||||
loading: true,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
});
|
||||
renderBar();
|
||||
// The status-area spinner is labelled "Checking\u2026".
|
||||
expect(screen.getByText("Checking\u2026")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an Online badge when the server is reachable", () => {
|
||||
mockedUseServerStatus.mockReturnValue({
|
||||
status: {
|
||||
online: true,
|
||||
version: "1.1.0",
|
||||
active_jails: 3,
|
||||
total_bans: 10,
|
||||
total_failures: 5,
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
});
|
||||
renderBar();
|
||||
expect(screen.getByText("Online")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an Offline badge when the server is unreachable", () => {
|
||||
mockedUseServerStatus.mockReturnValue({
|
||||
status: {
|
||||
online: false,
|
||||
version: null,
|
||||
active_jails: 0,
|
||||
total_bans: 0,
|
||||
total_failures: 0,
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
});
|
||||
renderBar();
|
||||
expect(screen.getByText("Offline")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays the daemon version string when available", () => {
|
||||
mockedUseServerStatus.mockReturnValue({
|
||||
status: {
|
||||
online: true,
|
||||
version: "1.2.3",
|
||||
active_jails: 1,
|
||||
total_bans: 0,
|
||||
total_failures: 0,
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
});
|
||||
renderBar();
|
||||
expect(screen.getByText("v1.2.3")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render the version element when version is null", () => {
|
||||
mockedUseServerStatus.mockReturnValue({
|
||||
status: {
|
||||
online: false,
|
||||
version: null,
|
||||
active_jails: 0,
|
||||
total_bans: 0,
|
||||
total_failures: 0,
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
});
|
||||
renderBar();
|
||||
// No version string should appear in the document.
|
||||
expect(screen.queryByText(/^v\d/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows jail / ban / failure counts when the server is online", () => {
|
||||
mockedUseServerStatus.mockReturnValue({
|
||||
status: {
|
||||
online: true,
|
||||
version: "1.0.0",
|
||||
active_jails: 4,
|
||||
total_bans: 21,
|
||||
total_failures: 99,
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
});
|
||||
renderBar();
|
||||
expect(screen.getByText("4")).toBeInTheDocument();
|
||||
expect(screen.getByText("21")).toBeInTheDocument();
|
||||
expect(screen.getByText("99")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders an error message when the status fetch fails", () => {
|
||||
mockedUseServerStatus.mockReturnValue({
|
||||
status: null,
|
||||
loading: false,
|
||||
error: "Network error",
|
||||
refresh: vi.fn(),
|
||||
});
|
||||
renderBar();
|
||||
expect(screen.getByText("Network error")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user