feat: simplify navigation, make reload work (#4498)

This commit is contained in:
Jack Amadeo
2025-09-05 20:36:38 -04:00
committed by GitHub
parent 5410df3f1e
commit 7fb746bbcb
7 changed files with 235 additions and 306 deletions
+31 -31
View File
@@ -4,9 +4,9 @@
* @vitest-environment jsdom
*/
import React from 'react';
import { render, waitFor } from '@testing-library/react';
import { screen, render, waitFor } from '@testing-library/react';
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import App from './App';
import { AppInner } from './App';
// Set up globals for jsdom
Object.defineProperty(window, 'location', {
@@ -161,13 +161,19 @@ vi.mock('./components/AnnouncementModal', () => ({
default: () => null,
}));
// Create mocks that we can track and configure per test
const mockNavigate = vi.fn();
const mockSearchParams = new URLSearchParams();
const mockSetSearchParams = vi.fn();
// Mock react-router-dom to avoid HashRouter issues in tests
vi.mock('react-router-dom', () => ({
HashRouter: ({ children }: { children: React.ReactNode }) => <>{children}</>,
Routes: ({ children }: { children: React.ReactNode }) => <>{children}</>,
Route: ({ element }: { element: React.ReactNode }) => element,
useNavigate: () => vi.fn(),
useNavigate: () => mockNavigate,
useLocation: () => ({ state: null, pathname: '/' }),
useSearchParams: () => [mockSearchParams, mockSetSearchParams],
Outlet: () => null,
}));
@@ -216,6 +222,14 @@ Object.defineProperty(window, 'matchMedia', {
describe('App Component - Brand New State', () => {
beforeEach(() => {
vi.clearAllMocks();
mockNavigate.mockClear();
mockSetSearchParams.mockClear();
// Reset search params
mockSearchParams.forEach((_, key) => {
mockSearchParams.delete(key);
});
window.location.hash = '';
window.location.search = '';
window.location.pathname = '/';
@@ -235,21 +249,16 @@ describe('App Component - Brand New State', () => {
GOOSE_ALLOWLIST_WARNING: false,
});
render(<App />);
render(<AppInner />);
// Wait for initialization
await waitFor(() => {
expect(mockElectron.reactReady).toHaveBeenCalled();
});
// Check that we navigated to "/" not "/welcome"
await waitFor(() => {
// In some environments, the hash might be empty or just "#"
expect(window.location.hash).toMatch(/^(#\/?|)$/);
});
// History should have been updated to "/"
expect(window.history.replaceState).toHaveBeenCalledWith({}, '', '#/');
// The app should initialize without any navigation calls since we're already at "/"
// No navigate calls should be made when no provider is configured
expect(mockNavigate).not.toHaveBeenCalled();
});
it('should handle deep links correctly when app is brand new', async () => {
@@ -260,20 +269,17 @@ describe('App Component - Brand New State', () => {
GOOSE_ALLOWLIST_WARNING: false,
});
// Simulate a deep link
window.location.search = '?view=settings';
// Set up search params to simulate view=settings deep link
mockSearchParams.set('view', 'settings');
render(<App />);
render(<AppInner />);
// Wait for initialization
await waitFor(() => {
expect(mockElectron.reactReady).toHaveBeenCalled();
});
// Should redirect to settings route via hash
await waitFor(() => {
expect(window.location.hash).toBe('#/settings');
});
expect(screen.getByText(/^Select an AI model provider/)).toBeInTheDocument();
});
it('should not redirect to /welcome when provider is configured', async () => {
@@ -284,18 +290,15 @@ describe('App Component - Brand New State', () => {
GOOSE_ALLOWLIST_WARNING: false,
});
render(<App />);
render(<AppInner />);
// Wait for initialization
await waitFor(() => {
expect(mockElectron.reactReady).toHaveBeenCalled();
});
// Should stay at "/" since provider is configured
await waitFor(() => {
// In some environments, the hash might be empty or just "#"
expect(window.location.hash).toMatch(/^(#\/?|)$/);
});
// Should not navigate anywhere since provider is configured and we're already at "/"
expect(mockNavigate).not.toHaveBeenCalled();
});
it('should handle config recovery gracefully', async () => {
@@ -310,17 +313,14 @@ describe('App Component - Brand New State', () => {
GOOSE_ALLOWLIST_WARNING: false,
});
render(<App />);
render(<AppInner />);
// Wait for initialization and recovery
await waitFor(() => {
expect(mockElectron.reactReady).toHaveBeenCalled();
});
// App should still initialize and navigate to "/"
await waitFor(() => {
// In some environments, the hash might be empty or just "#"
expect(window.location.hash).toMatch(/^(#\/?|)$/);
});
// App should still initialize without any navigation calls
expect(mockNavigate).not.toHaveBeenCalled();
});
});