generated from nhcarrigan/template
f173892aaa
## Summary This PR includes major feature additions, bug fixes, comprehensive testing improvements, and responsive design enhancements! ## New Features โจ ### Plugin & MCP Management (#133, #134) - **Plugin Management Panel**: Install, uninstall, enable/disable, and update plugins - **MCP Server Management Panel**: Add/remove MCP servers, view detailed configuration - **Marketplace Management**: Add/remove plugin marketplaces from GitHub - Backend commands for full CLI integration (`list_plugins`, `install_plugin`, `add_mcp_server`, etc.) - Beautiful UI with proper loading states, error handling, and theme support ### Visual Todo List Panel (#132) - Real-time todo list display when Hikari uses the `TodoWrite` tool - Shows pending/in-progress/completed status with visual indicators - Progress bar and completion count - Automatically clears on disconnect - Theme-aware styling ### Clear Session History Button (#130) - "Clear All Sessions" button in Session History panel - Confirmation dialog with session count - Keyboard support and accessibility features - Gives users control over disk usage ### CLI Version Display (#131) - Displays Claude CLI version in status bar - Auto-polls every 30 seconds for updates - Useful for debugging and feature compatibility ## Bug Fixes ๐ ### Stats Panel Scrolling (#136) - **Fixed stats panel overflow**: Added scrollable container with `max-height` constraint - Stats panel now scrolls when content (Tools Used, Historical Costs, Budget sections) gets too long - Prevents content from overflowing off screen ### Agent Monitor Fixes (#122) - **Fixed agents stuck in "running" state**: Added `SubagentStop` hook parsing - **Fixed agents persisting after disconnect**: Call `clearConversation()` on disconnect - **Fixed "Kill All" button**: Now properly marks all agents as errored - **Fixed badge persisting after tab close**: Cleanup agents when conversation is deleted - Comprehensive tests for agent lifecycle management ### Discord RPC Cleanup (#129) - Removed file-based logging for Discord RPC - Replaced with proper `tracing` framework usage - Reduces disk usage and eliminates maintenance burden ### Close Modal Bug Fix (#128) - Fixed close confirmation modal not triggering after Discord RPC refactor - Removed frontend calls to deleted `log_discord_rpc` command - Modal now works correctly after all operations ### Responsive Design Fixes (#118) - Fixed top navigation icons getting cut off at small screen widths - Fixed Connect button disappearing on narrow screens - Fixed bottom status info (clock, CLI version) getting cut off - Added flex-wrap and mobile-optimised layouts - Icons-only mode on screens < 640px - Vertical stacking on screens < 768px ## Testing Improvements ๐งช ### Comprehensive Test Coverage (#114) - **417 backend tests** (up from 408) - **387 frontend tests** (up from 363) - **61%+ backend code coverage** - Added E2E integration tests for cross-platform notification commands - New test files: `agents.test.ts`, comprehensive CLI parsing tests - Tests for `debug_logger.rs`, `bridge_manager.rs`, `notifications.rs` - Console mocking for cleaner test output - Fixed flaky frontend tests ### Testing Documentation - Updated CLAUDE.md with comprehensive testing guidelines - Documented mocking approaches (console mocking, E2E command structure testing) - Added step-by-step guide for adding tests to new features - Goal to maintain ~100% test coverage documented ## Closes Closes #114 Closes #118 Closes #122 Closes #128 Closes #129 Closes #130 Closes #131 Closes #132 Closes #133 Closes #134 Closes #136 ## Technical Details - All new backend commands properly registered in `lib.rs` - CLI output parsing with comprehensive test coverage - Cross-platform compatibility verified through E2E tests (Linux CI can test Windows commands) - Theme-aware UI components using CSS variables throughout - Proper TypeScript types for all new stores and components - ESLint and Prettier compliant - All Clippy warnings addressed โจ This PR was created with help from Hikari~ ๐ธ Reviewed-on: #135 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
150 lines
4.6 KiB
TypeScript
150 lines
4.6 KiB
TypeScript
/**
|
|
* SystemClock Component Tests
|
|
*
|
|
* Note: This file tests the time formatting logic used by the SystemClock component.
|
|
* Full component rendering tests are challenging with Svelte 5 + @testing-library/svelte
|
|
* due to SSR/CSR compatibility issues. The component itself is simple and visually
|
|
* testable - it displays the current date and time, updating every second.
|
|
*
|
|
* What this component does:
|
|
* - Displays date in British format: "7 February 2026"
|
|
* - Displays time in 24-hour format: "14:35:42"
|
|
* - Updates every second via setInterval
|
|
* - Cleans up interval on unmount via $effect
|
|
*
|
|
* Manual testing checklist:
|
|
* - [ ] Clock appears above the Send button
|
|
* - [ ] Time updates every second
|
|
* - [ ] Date format is "DD Month YYYY"
|
|
* - [ ] Time format is "HH:MM:SS" (24-hour)
|
|
* - [ ] Hover effect works (border turns accent colour)
|
|
*/
|
|
|
|
import { describe, it, expect } from "vitest";
|
|
|
|
// Helper function that mirrors the component's formatting logic
|
|
function formatDateTime(date: Date): string {
|
|
const day = date.getDate();
|
|
const month = date.toLocaleString("en-GB", { month: "long" });
|
|
const year = date.getFullYear();
|
|
|
|
const hours = String(date.getHours()).padStart(2, "0");
|
|
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
|
|
return `${day} ${month} ${year}, ${hours}:${minutes}:${seconds}`;
|
|
}
|
|
|
|
describe("SystemClock date/time formatting", () => {
|
|
it("formats date in British format (DD Month YYYY)", () => {
|
|
// Use local timezone (not UTC) since the component uses local time
|
|
const date = new Date(2026, 1, 7, 14, 35, 42); // Feb 7, 2026 14:35:42 local
|
|
const formatted = formatDateTime(date);
|
|
|
|
expect(formatted).toContain("7 February 2026");
|
|
});
|
|
|
|
it("formats time in 24-hour format (HH:MM:SS)", () => {
|
|
const date = new Date(2026, 1, 7, 14, 35, 42);
|
|
const formatted = formatDateTime(date);
|
|
|
|
// Should have the pattern HH:MM:SS
|
|
expect(formatted).toMatch(/\d{2}:\d{2}:\d{2}/);
|
|
expect(formatted).toContain("14:35:42");
|
|
});
|
|
|
|
it("combines date and time with comma separator", () => {
|
|
const date = new Date(2026, 1, 7, 14, 35, 42);
|
|
const formatted = formatDateTime(date);
|
|
|
|
expect(formatted).toBe("7 February 2026, 14:35:42");
|
|
});
|
|
|
|
it("pads single-digit hours, minutes, and seconds with zeros", () => {
|
|
const date = new Date(2026, 1, 7, 3, 5, 8);
|
|
const formatted = formatDateTime(date);
|
|
|
|
// Should have leading zeros: 03:05:08, not 3:5:8
|
|
expect(formatted).toContain("03:05:08");
|
|
});
|
|
|
|
it("handles different months correctly", () => {
|
|
const date = new Date(2026, 11, 25, 12, 0, 0); // December is month 11
|
|
const formatted = formatDateTime(date);
|
|
|
|
expect(formatted).toContain("25 December 2026");
|
|
});
|
|
|
|
it("handles year changes correctly", () => {
|
|
const date = new Date(2027, 0, 1, 0, 0, 0); // January is month 0
|
|
const formatted = formatDateTime(date);
|
|
|
|
expect(formatted).toContain("1 January 2027");
|
|
expect(formatted).toContain("00:00:00");
|
|
});
|
|
|
|
it("handles midnight correctly", () => {
|
|
const date = new Date(2026, 1, 7, 0, 0, 0);
|
|
const formatted = formatDateTime(date);
|
|
|
|
expect(formatted).toContain("00:00:00");
|
|
});
|
|
|
|
it("handles noon correctly", () => {
|
|
const date = new Date(2026, 1, 7, 12, 0, 0);
|
|
const formatted = formatDateTime(date);
|
|
|
|
// 24-hour format, so noon is 12:00:00, not 00:00:00
|
|
expect(formatted).toContain("12:00:00");
|
|
});
|
|
|
|
it("handles end of day correctly", () => {
|
|
const date = new Date(2026, 1, 7, 23, 59, 59);
|
|
const formatted = formatDateTime(date);
|
|
|
|
expect(formatted).toContain("23:59:59");
|
|
});
|
|
|
|
it("handles month boundaries correctly", () => {
|
|
// Last day of January
|
|
const jan31 = new Date(2026, 0, 31, 23, 59, 59);
|
|
expect(formatDateTime(jan31)).toContain("31 January 2026");
|
|
|
|
// First day of February
|
|
const feb1 = new Date(2026, 1, 1, 0, 0, 0);
|
|
expect(formatDateTime(feb1)).toContain("1 February 2026");
|
|
});
|
|
|
|
it("handles leap year February correctly", () => {
|
|
// 2024 is a leap year
|
|
const feb29 = new Date(2024, 1, 29, 12, 0, 0);
|
|
const formatted = formatDateTime(feb29);
|
|
|
|
expect(formatted).toContain("29 February 2024");
|
|
});
|
|
|
|
it("handles all 12 months correctly", () => {
|
|
const months = [
|
|
"January",
|
|
"February",
|
|
"March",
|
|
"April",
|
|
"May",
|
|
"June",
|
|
"July",
|
|
"August",
|
|
"September",
|
|
"October",
|
|
"November",
|
|
"December",
|
|
];
|
|
|
|
months.forEach((month, index) => {
|
|
const date = new Date(2026, index, 15, 12, 0, 0);
|
|
const formatted = formatDateTime(date);
|
|
|
|
expect(formatted).toContain(month);
|
|
});
|
|
});
|
|
});
|