fix: Fix linter and tests

This commit is contained in:
Vanalite 2025-09-30 15:39:45 +07:00
parent 43d20e2a32
commit 4718203960
7 changed files with 63 additions and 13 deletions

View File

@ -1,4 +1,3 @@
/* eslint-disable react-hooks/exhaustive-deps */
import ReactMarkdown, { Components } from 'react-markdown'
import remarkGfm from 'remark-gfm'
import remarkEmoji from 'remark-emoji'
@ -154,7 +153,7 @@ const CodeComponent = memo(
)}
</button>
</div>
{React.createElement(SyntaxHighlighter as React.ComponentType<any>, {
{React.createElement(SyntaxHighlighter as React.ComponentType<Record<string, unknown>>, {
style:
prismStyles[
codeBlockStyle

View File

@ -6,6 +6,37 @@ import { useNavigate, useMatches } from '@tanstack/react-router'
import { useGeneralSetting } from '@/hooks/useGeneralSetting'
import { useModelProvider } from '@/hooks/useModelProvider'
// Mock global platform constants - simulate desktop (Tauri) environment
Object.defineProperty(global, 'IS_IOS', { value: false, writable: true })
Object.defineProperty(global, 'IS_ANDROID', { value: false, writable: true })
Object.defineProperty(global, 'IS_WEB_APP', { value: false, writable: true })
// Mock platform features
vi.mock('@/lib/platform/const', () => ({
PlatformFeatures: {
hardwareMonitoring: true,
shortcut: true, // Desktop has shortcuts enabled
localInference: true,
localApiServer: true,
modelHub: true,
systemIntegrations: true,
httpsProxy: true,
defaultProviders: true,
analytics: true,
webAutoModelSelection: false,
modelProviderSettings: true,
mcpAutoApproveTools: false,
mcpServersSettings: true,
extensionsSettings: true,
assistants: true,
authentication: false,
googleAnalytics: false,
alternateShortcutBindings: false,
firstMessagePersistedThread: false,
temporaryChat: false,
},
}))
// Mock dependencies
vi.mock('@tanstack/react-router', () => ({
Link: ({ children, to, className }: any) => (
@ -81,6 +112,12 @@ describe('SettingsMenu', () => {
expect(screen.getByText('common:appearance')).toBeInTheDocument()
expect(screen.getByText('common:privacy')).toBeInTheDocument()
expect(screen.getByText('common:modelProviders')).toBeInTheDocument()
// Platform-specific features tested separately
})
it('renders keyboard shortcuts on desktop platforms', () => {
// This test assumes desktop platform (mocked in setup with shortcut: true)
render(<SettingsMenu />)
expect(screen.getByText('common:keyboardShortcuts')).toBeInTheDocument()
expect(screen.getByText('common:hardware')).toBeInTheDocument()
expect(screen.getByText('common:local_api_server')).toBeInTheDocument()

View File

@ -7,6 +7,18 @@ import {
import { PlatformFeature } from '@/lib/platform/types'
import { PlatformFeatures } from '@/lib/platform/const'
// Type definition for the auth service from extensions-web
interface AuthServiceInterface {
getAllProviders: () => string[]
loginWithProvider: (providerId: ProviderType) => Promise<void>
handleProviderCallback: (providerId: ProviderType, code: string, state?: string) => Promise<void>
isAuthenticatedWithProvider: (providerId: ProviderType) => boolean
logout: () => Promise<void>
getCurrentUser: (forceRefresh?: boolean) => Promise<User | null>
isAuthenticated: () => boolean
onAuthEvent: (callback: (event: MessageEvent) => void) => () => void
}
interface AuthState {
// Auth service
authService: JanAuthService | null
@ -69,7 +81,7 @@ const useAuthStore = create<AuthState>()((set, get) => ({
if (!authService) {
return []
}
return (authService as any).getAllProviders()
return (authService as AuthServiceInterface).getAllProviders()
},
loginWithProvider: async (providerId: ProviderType) => {
@ -78,7 +90,7 @@ const useAuthStore = create<AuthState>()((set, get) => ({
throw new Error('Authentication not available on this platform')
}
await (authService as any).loginWithProvider(providerId)
await (authService as AuthServiceInterface).loginWithProvider(providerId)
},
handleProviderCallback: async (
@ -91,7 +103,7 @@ const useAuthStore = create<AuthState>()((set, get) => ({
throw new Error('Authentication not available on this platform')
}
await (authService as any).handleProviderCallback(providerId, code, state)
await (authService as AuthServiceInterface).handleProviderCallback(providerId, code, state)
// Reload auth state after successful callback
await loadAuthState()
},
@ -102,7 +114,7 @@ const useAuthStore = create<AuthState>()((set, get) => ({
return false
}
return (authService as any).isAuthenticatedWithProvider(providerId)
return (authService as AuthServiceInterface).isAuthenticatedWithProvider(providerId)
},
logout: async () => {
@ -133,7 +145,7 @@ const useAuthStore = create<AuthState>()((set, get) => ({
}
try {
const profile = await (authService as any).getCurrentUser(forceRefresh)
const profile = await (authService as AuthServiceInterface).getCurrentUser(forceRefresh)
set({
user: profile,
isAuthenticated: profile !== null,
@ -156,11 +168,11 @@ const useAuthStore = create<AuthState>()((set, get) => ({
set({ isLoading: true })
// Check if user is authenticated with any provider
const isAuth = (authService as any).isAuthenticated()
const isAuth = (authService as AuthServiceInterface).isAuthenticated()
// Load user profile if authenticated
if (isAuth) {
const profile = await (authService as any).getCurrentUser(forceRefresh)
const profile = await (authService as AuthServiceInterface).getCurrentUser(forceRefresh)
set({
user: profile,
isAuthenticated: profile !== null,
@ -184,12 +196,12 @@ const useAuthStore = create<AuthState>()((set, get) => ({
subscribeToAuthEvents: (callback: (event: MessageEvent) => void) => {
const { authService } = get()
if (!authService || typeof (authService as any).onAuthEvent !== 'function') {
if (!authService || typeof (authService as AuthServiceInterface).onAuthEvent !== 'function') {
return () => {} // Return no-op cleanup
}
try {
return (authService as any).onAuthEvent(callback)
return (authService as AuthServiceInterface).onAuthEvent(callback)
} catch (error) {
console.warn('Failed to subscribe to auth events:', error)
return () => {}

View File

@ -131,6 +131,7 @@ export const useChat = () => {
})
}
return currentThread
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [createThread, retrieveThread, router])
const restartModel = useCallback(

View File

@ -41,7 +41,7 @@ export const useLocalApiServer = create<LocalApiServerState>()(
serverHost: '127.0.0.1',
setServerHost: (value) => set({ serverHost: value }),
// Use port 0 (auto-assign) for mobile to avoid conflicts, 1337 for desktop
serverPort: (typeof window !== 'undefined' && (window as any).IS_ANDROID) || (typeof window !== 'undefined' && (window as any).IS_IOS) ? 0 : 1337,
serverPort: (typeof window !== 'undefined' && (window as { IS_ANDROID?: boolean }).IS_ANDROID) || (typeof window !== 'undefined' && (window as { IS_IOS?: boolean }).IS_IOS) ? 0 : 1337,
setServerPort: (value) => set({ serverPort: value }),
apiPrefix: '/v1',
setApiPrefix: (value) => set({ apiPrefix: value }),

View File

@ -16,7 +16,7 @@ const setupMobileViewport = () => {
if (isMobile) {
// Update viewport meta tag to disable zoom
let viewportMeta = document.querySelector('meta[name="viewport"]')
const viewportMeta = document.querySelector('meta[name="viewport"]')
if (viewportMeta) {
viewportMeta.setAttribute('content',
'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover'

View File

@ -91,6 +91,7 @@ export default defineConfig(({ mode }) => {
watch: {
// 3. tell vite to ignore watching `src-tauri`
ignored: ['**/src-tauri/**'],
usePolling: true
},
},
}