diff --git a/frontend/src/components/QueryEditor.external-sql-save.test.tsx b/frontend/src/components/QueryEditor.external-sql-save.test.tsx new file mode 100644 index 0000000..711ea7c --- /dev/null +++ b/frontend/src/components/QueryEditor.external-sql-save.test.tsx @@ -0,0 +1,279 @@ +import React from 'react'; +import { act, create, type ReactTestRenderer } from 'react-test-renderer'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import type { SavedQuery, TabData } from '../types'; +import QueryEditor from './QueryEditor'; + +const storeState = vi.hoisted(() => ({ + connections: [ + { + id: 'conn-1', + name: 'local', + config: { + type: 'mysql', + host: '127.0.0.1', + port: 3306, + user: 'root', + password: '', + database: 'main', + }, + }, + ], + addSqlLog: vi.fn(), + addTab: vi.fn(), + savedQueries: [] as SavedQuery[], + saveQuery: vi.fn(), + theme: 'light', + sqlFormatOptions: { keywordCase: 'upper' as const }, + setSqlFormatOptions: vi.fn(), + queryOptions: { maxRows: 5000 }, + setQueryOptions: vi.fn(), + shortcutOptions: { + runQuery: { enabled: false, combo: '' }, + }, + activeTabId: 'tab-1', + aiPanelVisible: false, + setAIPanelVisible: vi.fn(), +})); + +const backendApp = vi.hoisted(() => ({ + DBQueryWithCancel: vi.fn(), + DBQueryMulti: vi.fn(), + DBGetTables: vi.fn(), + DBGetAllColumns: vi.fn(), + DBGetDatabases: vi.fn(), + DBGetColumns: vi.fn(), + CancelQuery: vi.fn(), + GenerateQueryID: vi.fn(), + WriteSQLFile: vi.fn(), +})); + +const messageApi = vi.hoisted(() => ({ + error: vi.fn(), + info: vi.fn(), + success: vi.fn(), + warning: vi.fn(), +})); + +const editorState = vi.hoisted(() => { + const state = { + value: '', + editor: null as any, + }; + state.editor = { + getValue: vi.fn(() => state.value), + setValue: vi.fn((value: string) => { + state.value = value; + }), + getModel: vi.fn(() => ({ + getValue: () => state.value, + setValue: (value: string) => { + state.value = value; + }, + getValueInRange: () => '', + getLineContent: () => '', + getWordUntilPosition: () => ({ startColumn: 1, endColumn: 1 }), + })), + getSelection: vi.fn(() => null), + addAction: vi.fn(), + onDidChangeModelContent: vi.fn(() => ({ dispose: vi.fn() })), + hasTextFocus: vi.fn(() => true), + }; + return state; +}); + +vi.mock('../store', () => { + const useStore = Object.assign( + (selector: (state: typeof storeState) => any) => selector(storeState), + { getState: () => storeState }, + ); + return { useStore }; +}); + +vi.mock('../../wailsjs/go/app/App', () => backendApp); + +vi.mock('../utils/autoFetchVisibility', () => ({ + useAutoFetchVisibility: () => false, +})); + +vi.mock('@monaco-editor/react', () => ({ + default: ({ defaultValue, onMount }: any) => { + React.useEffect(() => { + editorState.value = String(defaultValue || ''); + onMount?.(editorState.editor, { + editor: { setTheme: vi.fn() }, + languages: { + CompletionItemKind: { Keyword: 1, Function: 2, Field: 3 }, + registerCompletionItemProvider: vi.fn(), + }, + }); + }, []); + return