diff --git a/web/src/components/layout/AppShell.dom.test.tsx b/web/src/components/layout/AppShell.dom.test.tsx new file mode 100644 index 0000000..0256aa6 --- /dev/null +++ b/web/src/components/layout/AppShell.dom.test.tsx @@ -0,0 +1,64 @@ +// @vitest-environment jsdom +import { render, screen } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import { describe, expect, it, vi } from 'vitest'; +import { AppShell } from './AppShell'; + +vi.mock('./Sidebar', () => ({ + Sidebar: () => , +})); + +vi.mock('./Header', () => ({ + Header: () =>
Header
, +})); + +vi.mock('./RightPanel', () => ({ + RightPanel: () => , +})); + +vi.mock('../../views/HomeView', () => ({ + HomeView: () =>
Home view
, +})); + +vi.mock('../../views/PortfolioView', () => ({ + PortfolioView: () =>
Portfolio view
, +})); + +vi.mock('../../views/ResearchView', () => ({ + ResearchView: () =>
Research view
, +})); + +vi.mock('../../views/MarketsView', () => ({ + MarketsView: () =>
Markets view
, +})); + +vi.mock('../../views/ScreenerView', () => ({ + ScreenerView: () =>
Screener view
, +})); + +vi.mock('../../views/WatchlistView', () => ({ + WatchlistView: () =>
Watchlist view
, +})); + +vi.mock('../../views/AlertsView', () => ({ + AlertsView: () =>
Alerts view
, +})); + +vi.mock('../../views/SettingsView', () => ({ + SettingsView: () =>
Settings view
, +})); + +describe('AppShell routing fallback', () => { + it('shows a useful 404 state for unknown routes', () => { + render( + + + , + ); + + expect(screen.getByRole('heading', { name: 'Route not found' })).toBeInTheDocument(); + expect(screen.getByText('/missing/workspace')).toBeInTheDocument(); + expect(screen.getByRole('link', { name: 'Return home' })).toHaveAttribute('href', '/'); + expect(screen.queryByText('Home view')).not.toBeInTheDocument(); + }); +}); diff --git a/web/src/components/layout/AppShell.tsx b/web/src/components/layout/AppShell.tsx index b216f6d..ccfe5cc 100644 --- a/web/src/components/layout/AppShell.tsx +++ b/web/src/components/layout/AppShell.tsx @@ -1,4 +1,4 @@ -import { Routes, Route } from 'react-router-dom'; +import { Link, Routes, Route, useLocation } from 'react-router-dom'; import { Sidebar } from './Sidebar'; import { Header } from './Header'; import { RightPanel } from './RightPanel'; @@ -11,6 +11,54 @@ import { WatchlistView } from '../../views/WatchlistView'; import { AlertsView } from '../../views/AlertsView'; import { SettingsView } from '../../views/SettingsView'; +function NotFoundView() { + const location = useLocation(); + + return ( +
+
+ 404 +
+

+ Route not found +

+

+ No trading workspace exists at {location.pathname}. The app is still running normally. +

+ + Return home + +
+ ); +} + export function AppShell() { return (
@@ -35,8 +83,7 @@ export function AppShell() { } /> } /> } /> - {/* Fallback */} - } /> + } />