aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/client/components/SyncStatusIndicator.test.tsx
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-07 23:34:03 +0900
committernsfisis <nsfisis@gmail.com>2025-12-07 23:34:03 +0900
commit0c042ac89fc0822fcbe09c48702857faa5494ae1 (patch)
treeea1f1d180f747613343040d441a07f92b2760840 /src/client/components/SyncStatusIndicator.test.tsx
parentae5a0bb97fbf013417a6962f7e077f0408b2a951 (diff)
downloadkioku-0c042ac89fc0822fcbe09c48702857faa5494ae1.tar.gz
kioku-0c042ac89fc0822fcbe09c48702857faa5494ae1.tar.zst
kioku-0c042ac89fc0822fcbe09c48702857faa5494ae1.zip
feat(client): add sync status indicator component
Add SyncStatusIndicator component to display current sync state in the UI header. The component shows online/offline status, syncing progress, pending changes count, and sync errors. - Create SyncProvider context to wrap SyncManager for React components - Add SyncStatusIndicator component with visual status indicators - Integrate indicator into HomePage header - Add comprehensive tests for SyncStatusIndicator and SyncProvider - Update existing tests to include SyncProvider wrapper 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/client/components/SyncStatusIndicator.test.tsx')
-rw-r--r--src/client/components/SyncStatusIndicator.test.tsx160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/client/components/SyncStatusIndicator.test.tsx b/src/client/components/SyncStatusIndicator.test.tsx
new file mode 100644
index 0000000..a607e11
--- /dev/null
+++ b/src/client/components/SyncStatusIndicator.test.tsx
@@ -0,0 +1,160 @@
+/**
+ * @vitest-environment jsdom
+ */
+import "fake-indexeddb/auto";
+import { cleanup, render, screen } from "@testing-library/react";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { SyncStatusIndicator } from "./SyncStatusIndicator";
+
+// Mock the useSync hook
+const mockUseSync = vi.fn();
+vi.mock("../stores", () => ({
+ useSync: () => mockUseSync(),
+}));
+
+// Mock the SyncStatus constant
+vi.mock("../sync", () => ({
+ SyncStatus: {
+ Idle: "idle",
+ Syncing: "syncing",
+ Error: "error",
+ },
+}));
+
+describe("SyncStatusIndicator", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ afterEach(() => {
+ cleanup();
+ });
+
+ it("displays 'Synced' when online with no pending changes", () => {
+ mockUseSync.mockReturnValue({
+ isOnline: true,
+ isSyncing: false,
+ pendingCount: 0,
+ lastError: null,
+ status: "idle",
+ });
+
+ render(<SyncStatusIndicator />);
+
+ expect(screen.getByText("Synced")).toBeDefined();
+ expect(screen.getByTestId("sync-status-indicator")).toBeDefined();
+ });
+
+ it("displays 'Offline' when not online", () => {
+ mockUseSync.mockReturnValue({
+ isOnline: false,
+ isSyncing: false,
+ pendingCount: 0,
+ lastError: null,
+ status: "idle",
+ });
+
+ render(<SyncStatusIndicator />);
+
+ expect(screen.getByText("Offline")).toBeDefined();
+ });
+
+ it("displays 'Syncing...' when syncing", () => {
+ mockUseSync.mockReturnValue({
+ isOnline: true,
+ isSyncing: true,
+ pendingCount: 0,
+ lastError: null,
+ status: "syncing",
+ });
+
+ render(<SyncStatusIndicator />);
+
+ expect(screen.getByText("Syncing...")).toBeDefined();
+ });
+
+ it("displays pending count when there are pending changes", () => {
+ mockUseSync.mockReturnValue({
+ isOnline: true,
+ isSyncing: false,
+ pendingCount: 5,
+ lastError: null,
+ status: "idle",
+ });
+
+ render(<SyncStatusIndicator />);
+
+ expect(screen.getByText("5 pending")).toBeDefined();
+ });
+
+ it("displays 'Sync error' when there is an error", () => {
+ mockUseSync.mockReturnValue({
+ isOnline: true,
+ isSyncing: false,
+ pendingCount: 0,
+ lastError: "Network error",
+ status: "error",
+ });
+
+ render(<SyncStatusIndicator />);
+
+ expect(screen.getByText("Sync error")).toBeDefined();
+ });
+
+ it("shows error message in title when there is an error", () => {
+ mockUseSync.mockReturnValue({
+ isOnline: true,
+ isSyncing: false,
+ pendingCount: 0,
+ lastError: "Network error",
+ status: "error",
+ });
+
+ render(<SyncStatusIndicator />);
+
+ const indicator = screen.getByTestId("sync-status-indicator");
+ expect(indicator.getAttribute("title")).toBe("Network error");
+ });
+
+ it("prioritizes offline status over other states", () => {
+ mockUseSync.mockReturnValue({
+ isOnline: false,
+ isSyncing: true,
+ pendingCount: 5,
+ lastError: "Error",
+ status: "error",
+ });
+
+ render(<SyncStatusIndicator />);
+
+ expect(screen.getByText("Offline")).toBeDefined();
+ });
+
+ it("prioritizes syncing status over pending and error", () => {
+ mockUseSync.mockReturnValue({
+ isOnline: true,
+ isSyncing: true,
+ pendingCount: 5,
+ lastError: null,
+ status: "syncing",
+ });
+
+ render(<SyncStatusIndicator />);
+
+ expect(screen.getByText("Syncing...")).toBeDefined();
+ });
+
+ it("prioritizes error status over pending", () => {
+ mockUseSync.mockReturnValue({
+ isOnline: true,
+ isSyncing: false,
+ pendingCount: 5,
+ lastError: "Network error",
+ status: "error",
+ });
+
+ render(<SyncStatusIndicator />);
+
+ expect(screen.getByText("Sync error")).toBeDefined();
+ });
+});