diff --git a/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/useTableOrSpreadsheet.test.skip.ts b/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/useTableOrSpreadsheet.test.skip.ts deleted file mode 100644 index a20431bc13..0000000000 --- a/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/useTableOrSpreadsheet.test.skip.ts +++ /dev/null @@ -1,364 +0,0 @@ -import { renderHook } from "@testing-library/react-hooks"; -import type { DatasourceTable } from "entities/Datasource"; -import { PluginPackageName } from "entities/Plugin"; -import { useTableOrSpreadsheet } from "./useTableOrSpreadsheet"; - -// Mock applicationSelectors -jest.mock("ee/selectors/applicationSelectors", () => ({ - getApplications: jest.fn(() => []), - getCurrentApplication: jest.fn(() => ({})), - getApplicationSearchKeyword: jest.fn(), - getIsDeletingApplication: jest.fn(() => false), - getIsDuplicatingApplication: jest.fn(() => false), - getIsImportingApplication: jest.fn(() => false), -})); - -// Mock AppState and create MockStore -jest.mock("store", () => ({ - store: { - getState: jest.fn(() => ({ - entities: { - app: { - mode: "EDIT", - }, - }, - ui: { - applications: { - searchKeyword: "", - deletingApplication: false, - }, - }, - })), - }, -})); - -// Mock pageListSelectors -jest.mock("selectors/pageListSelectors", () => ({ - getIsGeneratingTemplatePage: jest.fn(() => false), - getIsGeneratePageModalOpen: jest.fn(() => false), - getApplicationLastModifiedTime: jest.fn(), - getCurrentApplicationId: jest.fn(), - getCurrentPageId: jest.fn(), - getPageById: jest.fn(), - getPageList: jest.fn(() => []), - getPageListAsOptions: jest.fn(() => []), - getPageListState: jest.fn(() => ({ - isGeneratingTemplatePage: false, - isGeneratePageModalOpen: false, - })), -})); - -// Mock UI selectors -jest.mock("selectors/ui", () => ({ - getSelectedAppTheme: jest.fn(), - getAppThemes: jest.fn(() => []), - getSelectedAppThemeColor: jest.fn(), - getIsDatasourceInViewMode: jest.fn(() => false), - getDatasourceCollapsibleState: jest.fn(() => ({})), - getIsInOnboardingFlow: jest.fn(() => false), -})); - -// Mock datasourceActions -jest.mock("actions/datasourceActions", () => ({ - fetchGheetSheets: jest.fn(() => ({ type: "FETCH_GSHEET_SHEETS" })), -})); - -// Mock editor selectors -jest.mock("selectors/editorSelectors", () => ({ - getWidgets: jest.fn(() => ({})), - getWidgetsMeta: jest.fn(() => ({})), - getWidgetsForImport: jest.fn(() => ({})), -})); - -// Mock Reselect -jest.mock("reselect", () => ({ - createSelector: jest.fn((selectors, resultFunc) => { - if (typeof resultFunc === "function") { - return resultFunc(); - } - - return jest.fn(); - }), -})); - -// Mock selectors -jest.mock("selectors/dataTreeSelectors", () => ({ - getLayoutSystemType: jest.fn(), - getIsMobileBreakPoint: jest.fn(), -})); - -// Mock redux -jest.mock("react-redux", () => ({ - useSelector: jest.fn(), - useDispatch: () => jest.fn(), -})); - -// Mock redux-form -jest.mock("redux-form", () => ({ - getFormValues: jest.fn(), - Field: jest.fn(), - reduxForm: jest.fn(), -})); - -// Mock ee/selectors/entitiesSelector -jest.mock("ee/selectors/entitiesSelector", () => ({ - getDatasource: jest.fn(), - getDatasourceLoading: jest.fn(), - getDatasourceStructureById: jest.fn(), - getIsFetchingDatasourceStructure: jest.fn(), - getPluginPackageFromDatasourceId: jest.fn(), -})); - -// Mock selectors/datasourceSelectors -jest.mock("selectors/datasourceSelectors", () => ({ - getGsheetSpreadsheets: jest.fn(() => jest.fn()), - getIsFetchingGsheetSpreadsheets: jest.fn(), -})); - -// Mock selectors/oneClickBindingSelectors -jest.mock("selectors/oneClickBindingSelectors", () => ({ - getisOneClickBindingConnectingForWidget: jest.fn(() => jest.fn()), -})); - -// Mock sagas/selectors -jest.mock("sagas/selectors", () => ({ - getWidget: jest.fn(), -})); - -// Mock utils -jest.mock("utils/editorContextUtils", () => ({ - isGoogleSheetPluginDS: jest.fn( - (plugin) => plugin === PluginPackageName.GOOGLE_SHEETS, - ), - isMongoDBPluginDS: jest.fn((plugin) => plugin === PluginPackageName.MONGO), -})); - -// Mock utils/helpers -jest.mock("utils/helpers", () => ({ - getAppMode: jest.fn(() => "EDIT"), - isEllipsisActive: jest.fn(), - modText: jest.fn(() => "Ctrl +"), - isMacOrIOS: jest.fn(() => false), - shiftText: jest.fn(() => "Shift +"), -})); - -// Mock WidgetOperationUtils -jest.mock("sagas/WidgetOperationUtils", () => ({})); - -// Mock WidgetUtils -jest.mock("widgets/WidgetUtils", () => ({})); - -// Mock environmentSelectors -jest.mock("ee/selectors/environmentSelectors", () => ({ - getCurrentEnvironmentId: jest.fn(), - getCurrentEnvironmentName: jest.fn(), -})); - -// Mock the context -jest.mock("react", () => { - const originalModule = jest.requireActual("react"); - - return { - ...originalModule, - useContext: () => ({ - config: { datasource: "test-ds-id", table: "" }, - propertyName: "test-property", - updateConfig: jest.fn(), - widgetId: "test-widget-id", - }), - useCallback: unknown>(fn: T) => fn, - // useMemo will be mocked in each test - useMemo: jest.fn(), - }; -}); - -// Import after all mocks are set up -import * as React from "react"; -import { useSelector } from "react-redux"; - -// Create a simplified test -describe("useTableOrSpreadsheet", () => { - const mockUseSelector = useSelector as jest.Mock; - const mockUseMemo = React.useMemo as jest.Mock; - - beforeEach(() => { - jest.clearAllMocks(); - mockUseSelector.mockImplementation(() => ({})); - mockUseMemo.mockImplementation((fn) => fn()); - }); - - it("should render without crashing", () => { - // Mock minimum required values - mockUseSelector.mockImplementation(() => ({ - datasourceStructure: { tables: [] }, - selectedDatasourcePluginPackageName: PluginPackageName.POSTGRES, - selectedDatasource: { name: "Test" }, - widget: { widgetName: "Test" }, - })); - - const { result } = renderHook(() => useTableOrSpreadsheet()); - - expect(result.current).toBeTruthy(); - }); - - it("should disable tables without primary keys for non-MongoDB datasources", () => { - const mockTables: DatasourceTable[] = [ - { - name: "table1", - type: "TABLE", - columns: [], - keys: [], - templates: [], - }, - { - name: "table2", - type: "TABLE", - columns: [], - keys: [ - { - name: "id", - type: "primary", - columnNames: ["id"], - fromColumns: ["id"], - }, - ], - templates: [], - }, - ]; - - const mockOptions = [ - { - id: "table1", - label: "table1", - value: "table1", - data: { tableName: "table1" }, - disabled: true, - }, - { - id: "table2", - label: "table2", - value: "table2", - data: { tableName: "table2" }, - disabled: false, - }, - ]; - - mockUseSelector.mockImplementation(() => ({ - selectedDatasourcePluginPackageName: PluginPackageName.POSTGRES, - datasourceStructure: { tables: mockTables }, - widget: { widgetName: "TestWidget" }, - selectedDatasource: { name: "TestDatabase" }, - })); - - mockUseMemo.mockImplementation(() => mockOptions); - - const { result } = renderHook(() => useTableOrSpreadsheet()); - - expect(result.current.options).toHaveLength(2); - expect(result.current.options[0].value).toBe("table1"); - expect(result.current.options[0].disabled).toBe(true); - expect(result.current.options[1].value).toBe("table2"); - expect(result.current.options[1].disabled).toBe(false); - }); - - it("should not disable tables for MongoDB datasources regardless of primary keys", () => { - const mockTables: DatasourceTable[] = [ - { - name: "collection1", - type: "COLLECTION", - columns: [], - keys: [], - templates: [], - }, - { - name: "collection2", - type: "COLLECTION", - columns: [], - keys: [ - { - name: "_id", - type: "primary", - columnNames: ["_id"], - fromColumns: ["_id"], - }, - ], - templates: [], - }, - ]; - - const mockOptions = [ - { - id: "collection1", - label: "collection1", - value: "collection1", - data: { tableName: "collection1" }, - disabled: false, - }, - { - id: "collection2", - label: "collection2", - value: "collection2", - data: { tableName: "collection2" }, - disabled: false, - }, - ]; - - mockUseSelector.mockImplementation(() => ({ - selectedDatasourcePluginPackageName: PluginPackageName.MONGO, - datasourceStructure: { tables: mockTables }, - widget: { widgetName: "TestWidget" }, - selectedDatasource: { name: "TestMongoDB" }, - })); - - mockUseMemo.mockImplementation(() => mockOptions); - - const { result } = renderHook(() => useTableOrSpreadsheet()); - - expect(result.current.options).toHaveLength(2); - expect(result.current.options[0].value).toBe("collection1"); - expect(result.current.options[0].disabled).toBe(false); - expect(result.current.options[1].value).toBe("collection2"); - expect(result.current.options[1].disabled).toBe(false); - }); - - it("should not disable tables for Google Sheets datasource", () => { - const mockOptions = [ - { - id: "sheet1", - label: "Sheet1", - value: "Sheet1", - data: { tableName: "sheet1" }, - disabled: false, - }, - { - id: "sheet2", - label: "Sheet2", - value: "Sheet2", - data: { tableName: "sheet2" }, - disabled: false, - }, - ]; - - mockUseSelector.mockImplementation(() => ({ - selectedDatasourcePluginPackageName: PluginPackageName.GOOGLE_SHEETS, - spreadSheets: { - value: [ - { label: "Sheet1", value: "sheet1" }, - { label: "Sheet2", value: "sheet2" }, - ], - }, - widget: { widgetName: "TestWidget" }, - selectedDatasource: { name: "TestGoogleSheet" }, - })); - - mockUseMemo.mockImplementation(() => mockOptions); - - const { result } = renderHook(() => useTableOrSpreadsheet()); - - expect(result.current.options).toHaveLength(2); - expect(result.current.options[0].value).toBe("Sheet1"); - expect(result.current.options[0].disabled).toBe(false); - expect(result.current.options[1].value).toBe("Sheet2"); - expect(result.current.options[1].disabled).toBe(false); - }); -}); diff --git a/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/useTableOrSpreadsheet.test.tsx b/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/useTableOrSpreadsheet.test.tsx new file mode 100644 index 0000000000..ef106936c2 --- /dev/null +++ b/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/useTableOrSpreadsheet.test.tsx @@ -0,0 +1,407 @@ +import { renderHook } from "@testing-library/react-hooks"; +import React from "react"; +import { Provider } from "react-redux"; +import configureStore from "redux-mock-store"; +import { WidgetQueryGeneratorFormContext } from "../.."; +import { DROPDOWN_VARIANT } from "../DatasourceDropdown/types"; +import { useTableOrSpreadsheet } from "./useTableOrSpreadsheet"; + +const mockStore = configureStore([]); + +describe("useTableOrSpreadsheet", () => { + it("returns correct options and labelText for minimal valid state", () => { + const state = { + entities: { + datasources: { + list: [{ id: "ds1", name: "TestDatasource", pluginId: "plugin1" }], + structure: { + ds1: { + id: "ds1", + tables: [], + }, + }, + fetchingDatasourceStructure: { + ds1: false, + }, + gsheetStructure: { + spreadsheets: { + ds1: [{ id: "123", label: "Sheet1", value: "sheet1" }], + }, + isFetchingSpreadsheets: false, + }, + loading: false, + }, + plugins: { + list: [ + { id: "plugin1", packageName: "TestPlugin", name: "TestPlugin" }, + ], + }, + canvasWidgets: { + Widget1: { widgetName: "Widget1", type: "TABLE_WIDGET" }, + }, + }, + ui: { + oneClickBinding: { + isConnecting: false, + config: { + widgetId: "Widget1", + }, + showOptions: false, + }, + }, + }; + const store = mockStore(state); + + const contextValue = { + config: { + datasource: "ds1", + datasourcePluginType: "DB", + table: "", + datasourcePluginName: "TestPlugin", + datasourceConnectionMode: "READ_WRITE", + alias: {}, + sheet: "", + searchableColumn: "", + tableHeaderIndex: 1, + }, + propertyName: "table", + updateConfig: jest.fn(), + widgetId: "Widget1", + propertyValue: "", + addBinding: jest.fn(), + isSourceOpen: false, + onSourceClose: jest.fn(), + errorMsg: "", + expectedType: "", + sampleData: "", + aliases: [], + otherFields: [], + excludePrimaryColumnFromQueryGeneration: false, + isConnectableToWidget: false, + datasourceDropdownVariant: DROPDOWN_VARIANT.CONNECT_TO_DATASOURCE, // Use the correct enum or string + alertMessage: null, + // Add any other required fields with dummy values + }; + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + + {children} + + + ); + + const { result } = renderHook(() => useTableOrSpreadsheet(), { wrapper }); + + expect(result.current.labelText).toContain("Select"); + expect(result.current.labelText).toContain("TestDatasource"); + expect(Array.isArray(result.current.options)).toBe(true); + }); + + it("should disable tables without primary keys for non-MongoDB datasources", () => { + const state = { + entities: { + datasources: { + list: [{ id: "ds1", name: "PostgresDB", pluginId: "plugin1" }], + structure: { + ds1: { + id: "ds1", + tables: [ + { + name: "table_with_pk", + keys: [ + { name: "id_pk", type: "primary key", columnNames: ["id"] }, + ], + columns: [{ name: "id", type: "integer" }], + }, + { + name: "table_without_pk", + keys: [], + columns: [{ name: "data", type: "text" }], + }, + ], + }, + }, + fetchingDatasourceStructure: { + ds1: false, + }, + gsheetStructure: { + spreadsheets: { + ds1: [], + }, + isFetchingSpreadsheets: false, + }, + loading: false, + }, + plugins: { + list: [ + { id: "plugin1", packageName: "postgres", name: "PostgreSQL" }, + ], + }, + canvasWidgets: { + Widget1: { widgetName: "Widget1", type: "TABLE_WIDGET" }, + }, + }, + ui: { + oneClickBinding: { + isConnecting: false, + config: { + widgetId: "Widget1", + }, + showOptions: false, + }, + }, + }; + const store = mockStore(state); + + const contextValue = { + config: { + datasource: "ds1", + datasourcePluginType: "DB", + table: "", + datasourcePluginName: "postgres", + datasourceConnectionMode: "READ_WRITE", + alias: {}, + sheet: "", + searchableColumn: "", + tableHeaderIndex: 1, + }, + propertyName: "table", + updateConfig: jest.fn(), + widgetId: "Widget1", + propertyValue: "", + addBinding: jest.fn(), + isSourceOpen: false, + onSourceClose: jest.fn(), + errorMsg: "", + expectedType: "", + sampleData: "", + aliases: [], + otherFields: [], + excludePrimaryColumnFromQueryGeneration: false, + isConnectableToWidget: false, + datasourceDropdownVariant: DROPDOWN_VARIANT.CONNECT_TO_DATASOURCE, + alertMessage: null, + }; + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + + {children} + + + ); + + const { result } = renderHook(() => useTableOrSpreadsheet(), { wrapper }); + + // Verify that table with primary key is enabled + const tableWithPkOption = result.current.options.find( + (option) => option.value === "table_with_pk", + ); + + expect(tableWithPkOption?.disabled).toBe(false); + + // Verify that table without primary key is disabled + const tableWithoutPkOption = result.current.options.find( + (option) => option.value === "table_without_pk", + ); + + expect(tableWithoutPkOption?.disabled).toBe(true); + }); + + it("should not disable tables for MongoDB datasources regardless of primary keys", () => { + const state = { + entities: { + datasources: { + list: [{ id: "ds1", name: "MongoDB", pluginId: "plugin1" }], + structure: { + ds1: { + id: "ds1", + tables: [ + { + name: "collection1", + keys: [], + columns: [{ name: "_id", type: "objectId" }], + }, + { + name: "collection2", + keys: [], + columns: [{ name: "data", type: "object" }], + }, + ], + }, + }, + fetchingDatasourceStructure: { + ds1: false, + }, + gsheetStructure: { + spreadsheets: { + ds1: [], + }, + isFetchingSpreadsheets: false, + }, + loading: false, + }, + plugins: { + list: [ + { id: "plugin1", packageName: "mongo-plugin", name: "MongoDB" }, + ], + }, + canvasWidgets: { + Widget1: { widgetName: "Widget1", type: "TABLE_WIDGET" }, + }, + }, + ui: { + oneClickBinding: { + isConnecting: false, + config: { + widgetId: "Widget1", + }, + showOptions: false, + }, + }, + }; + const store = mockStore(state); + + const contextValue = { + config: { + datasource: "ds1", + datasourcePluginType: "DB", + table: "", + datasourcePluginName: "mongo", + datasourceConnectionMode: "READ_WRITE", + alias: {}, + sheet: "", + searchableColumn: "", + tableHeaderIndex: 1, + }, + propertyName: "table", + updateConfig: jest.fn(), + widgetId: "Widget1", + propertyValue: "", + addBinding: jest.fn(), + isSourceOpen: false, + onSourceClose: jest.fn(), + errorMsg: "", + expectedType: "", + sampleData: "", + aliases: [], + otherFields: [], + excludePrimaryColumnFromQueryGeneration: false, + isConnectableToWidget: false, + datasourceDropdownVariant: DROPDOWN_VARIANT.CONNECT_TO_DATASOURCE, + alertMessage: null, + }; + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + + {children} + + + ); + + const { result } = renderHook(() => useTableOrSpreadsheet(), { wrapper }); + + // Verify that all tables for MongoDB are not disabled, regardless of primary keys + result.current.options.forEach((option) => { + expect(option.disabled).toBe(false); + }); + }); + + it("should not disable tables for Google Sheets datasource", () => { + const state = { + entities: { + datasources: { + list: [{ id: "ds1", name: "Sheets", pluginId: "plugin1" }], + structure: { + ds1: { + id: "ds1", + tables: [], + }, + }, + fetchingDatasourceStructure: { + ds1: false, + }, + gsheetStructure: { + spreadsheets: { + ds1: [ + { id: "sheet1", label: "Sheet1", value: "sheet1" }, + { id: "sheet2", label: "Sheet2", value: "sheet2" }, + ], + }, + isFetchingSpreadsheets: false, + }, + loading: false, + }, + plugins: { + list: [ + { + id: "plugin1", + packageName: "google-sheets-plugin", + name: "Google Sheets", + }, + ], + }, + canvasWidgets: { + Widget1: { widgetName: "Widget1", type: "TABLE_WIDGET" }, + }, + }, + ui: { + oneClickBinding: { + isConnecting: false, + config: { + widgetId: "Widget1", + }, + showOptions: false, + }, + }, + }; + const store = mockStore(state); + + const contextValue = { + config: { + datasource: "ds1", + datasourcePluginType: "SAAS", + table: "", + datasourcePluginName: "google-sheets", + datasourceConnectionMode: "READ_WRITE", + alias: {}, + sheet: "", + searchableColumn: "", + tableHeaderIndex: 1, + }, + propertyName: "table", + updateConfig: jest.fn(), + widgetId: "Widget1", + propertyValue: "", + addBinding: jest.fn(), + isSourceOpen: false, + onSourceClose: jest.fn(), + errorMsg: "", + expectedType: "", + sampleData: "", + aliases: [], + otherFields: [], + excludePrimaryColumnFromQueryGeneration: false, + isConnectableToWidget: false, + datasourceDropdownVariant: DROPDOWN_VARIANT.CONNECT_TO_DATASOURCE, + alertMessage: null, + }; + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + + {children} + + + ); + + const { result } = renderHook(() => useTableOrSpreadsheet(), { wrapper }); + + // Verify that all spreadsheets are not disabled + result.current.options.forEach((option) => { + expect(option.disabled).toBe(false); + }); + }); +});