mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-05-18 15:26:17 +02:00
76f3a59d83
Push history on user-initiated navigation so 'Back' moves between views instead of leaving the app. - urlHash: add pushUrlHash/pushSettingsHash (pushState variants) - useConversationRouter: pushState on nav; popstate resolves hash to conversation without re-pushing; switch over if-chain - useAppShell: pushState on settings open; popstate syncs visibility; close via history.back() when we own the entry - Tests: popstate coverage in appStartupHash + useAppShell
142 lines
3.7 KiB
TypeScript
142 lines
3.7 KiB
TypeScript
import { act, renderHook, waitFor } from '@testing-library/react';
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
|
import { useAppShell } from '../hooks/useAppShell';
|
|
|
|
describe('useAppShell', () => {
|
|
let originalHash: string;
|
|
|
|
beforeEach(() => {
|
|
originalHash = window.location.hash;
|
|
});
|
|
|
|
afterEach(() => {
|
|
window.location.hash = originalHash;
|
|
});
|
|
|
|
it('opens new-message modal and closes the sidebar', () => {
|
|
const { result } = renderHook(() => useAppShell());
|
|
|
|
act(() => {
|
|
result.current.setSidebarOpen(true);
|
|
result.current.handleOpenNewMessage();
|
|
});
|
|
|
|
expect(result.current.showNewMessage).toBe(true);
|
|
expect(result.current.sidebarOpen).toBe(false);
|
|
});
|
|
|
|
it('toggles settings mode and closes the sidebar', () => {
|
|
const { result } = renderHook(() => useAppShell());
|
|
|
|
act(() => {
|
|
result.current.setSidebarOpen(true);
|
|
result.current.handleToggleSettingsView();
|
|
});
|
|
|
|
expect(result.current.showSettings).toBe(true);
|
|
expect(result.current.sidebarOpen).toBe(false);
|
|
|
|
act(() => {
|
|
result.current.handleCloseSettingsView();
|
|
});
|
|
|
|
expect(result.current.showSettings).toBe(false);
|
|
});
|
|
|
|
it('initializes settings mode from the URL hash', () => {
|
|
window.location.hash = '#settings/database';
|
|
|
|
const { result } = renderHook(() => useAppShell());
|
|
|
|
expect(result.current.showSettings).toBe(true);
|
|
expect(result.current.settingsSection).toBe('database');
|
|
});
|
|
|
|
it('syncs the selected settings section into the URL hash', async () => {
|
|
const { result } = renderHook(() => useAppShell());
|
|
|
|
act(() => {
|
|
result.current.handleToggleSettingsView();
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(window.location.hash).toBe('#settings/radio');
|
|
});
|
|
|
|
act(() => {
|
|
result.current.setSettingsSection('fanout');
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(window.location.hash).toBe('#settings/fanout');
|
|
});
|
|
});
|
|
|
|
it('restores the previous hash when settings close', async () => {
|
|
window.location.hash = '#channel/test/Public';
|
|
|
|
const { result } = renderHook(() => useAppShell());
|
|
|
|
act(() => {
|
|
result.current.handleToggleSettingsView();
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(window.location.hash).toBe('#settings/radio');
|
|
});
|
|
|
|
act(() => {
|
|
result.current.handleCloseSettingsView();
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(window.location.hash).toBe('#channel/test/Public');
|
|
});
|
|
});
|
|
|
|
it('pushes a new history entry when opening settings', async () => {
|
|
const { result } = renderHook(() => useAppShell());
|
|
const lengthBefore = window.history.length;
|
|
|
|
act(() => {
|
|
result.current.handleToggleSettingsView();
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(window.location.hash).toBe('#settings/radio');
|
|
});
|
|
|
|
expect(window.history.length).toBe(lengthBefore + 1);
|
|
});
|
|
|
|
it('closes settings when popstate fires with a non-settings hash', async () => {
|
|
window.location.hash = '#settings/radio';
|
|
|
|
const { result } = renderHook(() => useAppShell());
|
|
|
|
expect(result.current.showSettings).toBe(true);
|
|
|
|
act(() => {
|
|
window.location.hash = '#channel/abc/Public';
|
|
window.dispatchEvent(new PopStateEvent('popstate', { state: null }));
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.showSettings).toBe(false);
|
|
});
|
|
});
|
|
|
|
it('toggles the cracker shell without affecting sidebar state', () => {
|
|
const { result } = renderHook(() => useAppShell());
|
|
|
|
act(() => {
|
|
result.current.setSidebarOpen(true);
|
|
result.current.handleToggleCracker();
|
|
});
|
|
|
|
expect(result.current.showCracker).toBe(true);
|
|
expect(result.current.sidebarOpen).toBe(true);
|
|
});
|
|
});
|