pgadmin4/web/regression/javascript/sqleditor/NLQChatPanel.spec.js

182 lines
5.5 KiB
JavaScript

/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2025, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
// Mock url_for
jest.mock('sources/url_for', () => ({
__esModule: true,
default: jest.fn((endpoint) => `/mock/${endpoint}`),
}));
// Mock preferences store
jest.mock('../../../pgadmin/preferences/static/js/store', () => ({
__esModule: true,
default: jest.fn(() => ({
getPreferencesForModule: jest.fn(() => ({})),
})),
}));
// Mock the QueryToolComponent to avoid importing all its dependencies
jest.mock('../../../pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx', () => {
const React = require('react');
return {
QueryToolContext: React.createContext(null),
QueryToolEventsContext: React.createContext(null),
};
});
// Mock CodeMirror
jest.mock('../../../pgadmin/static/js/components/ReactCodeMirror', () => ({
__esModule: true,
default: ({ value }) => <pre data-testid="codemirror">{value}</pre>,
}));
// Mock EmptyPanelMessage
jest.mock('../../../pgadmin/static/js/components/EmptyPanelMessage', () => ({
__esModule: true,
default: ({ text }) => <div data-testid="empty-message">{text}</div>,
}));
// Mock Loader
jest.mock('sources/components/Loader', () => ({
__esModule: true,
default: () => <div data-testid="loader">Loading...</div>,
}));
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { withTheme } from '../fake_theme';
import { NLQChatPanel } from '../../../pgadmin/tools/sqleditor/static/js/components/sections/NLQChatPanel.jsx';
import {
QueryToolContext,
QueryToolEventsContext,
} from '../../../pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx';
// Mock the EventBus
const createMockEventBus = () => ({
fireEvent: jest.fn(),
registerListener: jest.fn(),
});
// Mock the QueryToolContext
const createMockQueryToolCtx = (isQueryTool = true) => ({
params: {
trans_id: 12345,
is_query_tool: isQueryTool,
},
api: {
post: jest.fn(),
get: jest.fn(),
},
});
// Helper to render with contexts
const renderWithContexts = (component, { queryToolCtx, eventBus } = {}) => {
const mockEventBus = eventBus || createMockEventBus();
const mockQueryToolCtx = queryToolCtx || createMockQueryToolCtx();
return render(
<QueryToolContext.Provider value={mockQueryToolCtx}>
<QueryToolEventsContext.Provider value={mockEventBus}>
{component}
</QueryToolEventsContext.Provider>
</QueryToolContext.Provider>
);
};
describe('NLQChatPanel Component', () => {
let ThemedNLQChatPanel;
beforeAll(() => {
ThemedNLQChatPanel = withTheme(NLQChatPanel);
// Mock fetch for SSE
global.fetch = jest.fn();
});
afterEach(() => {
jest.clearAllMocks();
});
it('should render without crashing', () => {
const { container } = renderWithContexts(<ThemedNLQChatPanel />);
expect(container).toBeInTheDocument();
});
it('should show AI Assistant header', () => {
renderWithContexts(<ThemedNLQChatPanel />);
expect(screen.getByText('AI Assistant')).toBeInTheDocument();
});
it('should show empty state message when no messages', () => {
renderWithContexts(<ThemedNLQChatPanel />);
expect(
screen.getByText(/Describe what SQL you need/i)
).toBeInTheDocument();
});
it('should have input field for typing queries', () => {
renderWithContexts(<ThemedNLQChatPanel />);
const input = screen.getByPlaceholderText(/Describe the SQL you need/i);
expect(input).toBeInTheDocument();
});
it('should have send button', () => {
renderWithContexts(<ThemedNLQChatPanel />);
const sendButton = screen.getByLabelText('Send');
expect(sendButton).toBeInTheDocument();
});
it('should have clear conversation button', () => {
renderWithContexts(<ThemedNLQChatPanel />);
const clearButton = screen.getByText('Clear');
expect(clearButton).toBeInTheDocument();
});
it('should disable send button when input is empty', () => {
const { container } = renderWithContexts(<ThemedNLQChatPanel />);
const sendButton = container.querySelector('button[data-label="Send"]');
expect(sendButton).toBeDisabled();
});
it('should enable send button when input has text', () => {
const { container } = renderWithContexts(<ThemedNLQChatPanel />);
const input = screen.getByPlaceholderText(/Describe the SQL you need/i);
fireEvent.change(input, { target: { value: 'Find all users' } });
const sendButton = container.querySelector('button[data-label="Send"]');
expect(sendButton).not.toBeDisabled();
});
it('should show message when not in query tool mode', () => {
const mockQueryToolCtx = createMockQueryToolCtx(false);
renderWithContexts(<ThemedNLQChatPanel />, {
queryToolCtx: mockQueryToolCtx,
});
expect(
screen.getByText(/AI Assistant is only available in Query Tool mode/i)
).toBeInTheDocument();
});
it('should clear input after typing and clicking clear', () => {
renderWithContexts(<ThemedNLQChatPanel />);
const input = screen.getByPlaceholderText(/Describe the SQL you need/i);
fireEvent.change(input, { target: { value: 'Find all users' } });
expect(input.value).toBe('Find all users');
const clearButton = screen.getByText('Clear');
fireEvent.click(clearButton);
// Input should still have text (clear only clears messages)
expect(input.value).toBe('Find all users');
});
});