mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Desktop startup can fail before React mounts on older WebViews, so an HTML-level watchdog now renders startup diagnostics even when the module bundle never reaches the app code. Persistent provider migration now imports legacy root provider config into cc-haha-owned storage without deleting the old source file, and plugin marketplace cleanup refuses obvious corrupted cache roots or outside paths. Constraint: User explicitly accepted the current reviewed state for landing despite remaining review concerns. Constraint: Global ~/.claude state is user-owned and protected; automatic repair must avoid deleting shared config, transcripts, skills, MCP, plugins, OAuth, adapters, and teams. Rejected: Tell users to delete ~/.claude or ~/.claude/cc-haha | unsafe because it can destroy user-owned Claude state and still may not fix WebView compatibility failures. Confidence: medium Scope-risk: moderate Directive: Do not weaken protected-path checks; future deletion paths should validate real paths and symlink behavior before recursive rm. Tested: bun test src/utils/plugins/installedPluginsManager.test.ts src/utils/plugins/marketplaceManager.test.ts Tested: bun run check:server Tested: cd desktop && bun run test -- --run src/main.test.tsx index-html.test.ts vite-config.test.ts src/theme/globals.test.ts Tested: cd desktop && bun run build Tested: bun run check:coverage Not-tested: live macOS 12/Safari 15 WKWebView startup on an affected machine. Not-tested: H5 diagnostic URL redaction and symlink-escape hardening are known follow-up risks from review.
84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
import React from 'react'
|
|
import '@testing-library/jest-dom'
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { act, cleanup, screen } from '@testing-library/react'
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
runDesktopPersistenceMigrations: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('./lib/persistenceMigrations', () => ({
|
|
runDesktopPersistenceMigrations: mocks.runDesktopPersistenceMigrations,
|
|
}))
|
|
|
|
vi.mock('./theme/globals.css', () => ({}))
|
|
|
|
vi.mock('./App', () => ({
|
|
App: () => <div>Auto boot app</div>,
|
|
}))
|
|
|
|
vi.mock('./components/ErrorBoundary', () => ({
|
|
ErrorBoundary: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
|
}))
|
|
|
|
vi.mock('./lib/diagnosticsCapture', () => ({
|
|
installClientDiagnosticsCapture: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('./stores/uiStore', () => ({
|
|
initializeTheme: vi.fn(),
|
|
}))
|
|
|
|
describe('desktop bootstrap', () => {
|
|
afterEach(() => {
|
|
cleanup()
|
|
document.body.innerHTML = ''
|
|
delete window.__CC_HAHA_BOOTSTRAPPED__
|
|
delete window.__CC_HAHA_SHOW_STARTUP_ERROR__
|
|
vi.restoreAllMocks()
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('runs startup migrations and renders the app without top-level await', async () => {
|
|
document.body.innerHTML = '<div id="root"></div>'
|
|
|
|
await act(async () => {
|
|
await import('./main')
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(await screen.findByText('Auto boot app')).toBeInTheDocument()
|
|
expect(mocks.runDesktopPersistenceMigrations).toHaveBeenCalledTimes(1)
|
|
expect(window.__CC_HAHA_BOOTSTRAPPED__).toBe(true)
|
|
})
|
|
|
|
it('surfaces bootstrap failures in the root element', async () => {
|
|
const { bootstrapDesktopApp } = await import('./main')
|
|
const root = document.createElement('div')
|
|
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
|
|
await bootstrapDesktopApp(root, async () => {
|
|
throw new Error('bootstrap failed')
|
|
})
|
|
|
|
expect(root.textContent).toBe('bootstrap failed')
|
|
expect(consoleError).toHaveBeenCalledWith('[desktop] Failed to bootstrap app', expect.any(Error))
|
|
})
|
|
|
|
it('delegates bootstrap failures to the HTML startup watchdog when available', async () => {
|
|
const { bootstrapDesktopApp } = await import('./main')
|
|
const root = document.createElement('div')
|
|
const showStartupError = vi.fn()
|
|
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
window.__CC_HAHA_SHOW_STARTUP_ERROR__ = showStartupError
|
|
|
|
await bootstrapDesktopApp(root, async () => {
|
|
throw new Error('module failed')
|
|
})
|
|
|
|
expect(showStartupError).toHaveBeenCalledWith(expect.any(Error))
|
|
expect(root.textContent).toBe('')
|
|
expect(consoleError).toHaveBeenCalledWith('[desktop] Failed to bootstrap app', expect.any(Error))
|
|
})
|
|
})
|