@libredb/studio 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +127 -0
- package/.cursorrules +426 -0
- package/.devin/wiki.json +143 -0
- package/.dockerignore +80 -0
- package/.env.example +159 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +49 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +29 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +57 -0
- package/.github/workflows/ci.yml +185 -0
- package/.github/workflows/codeql.yml +57 -0
- package/.github/workflows/docker-build-push.yml +118 -0
- package/.github/workflows/helm-release.yml +113 -0
- package/CLAUDE.md +265 -0
- package/CODE_OF_CONDUCT.md +124 -0
- package/CONTRIBUTING.md +154 -0
- package/Dockerfile +73 -0
- package/LICENSE +21 -0
- package/README.md +614 -0
- package/SECURITY.md +107 -0
- package/artifacthub-repo.yml +4 -0
- package/bun.lock +1714 -0
- package/bunfig.toml +3 -0
- package/charts/libredb-studio/.helmignore +11 -0
- package/charts/libredb-studio/Chart.lock +6 -0
- package/charts/libredb-studio/Chart.yaml +50 -0
- package/charts/libredb-studio/README.md +206 -0
- package/charts/libredb-studio/templates/NOTES.txt +59 -0
- package/charts/libredb-studio/templates/_helpers.tpl +135 -0
- package/charts/libredb-studio/templates/configmap.yaml +37 -0
- package/charts/libredb-studio/templates/deployment.yaml +184 -0
- package/charts/libredb-studio/templates/hpa.yaml +32 -0
- package/charts/libredb-studio/templates/ingress.yaml +41 -0
- package/charts/libredb-studio/templates/networkpolicy.yaml +50 -0
- package/charts/libredb-studio/templates/pdb.yaml +18 -0
- package/charts/libredb-studio/templates/pvc.yaml +23 -0
- package/charts/libredb-studio/templates/secret.yaml +30 -0
- package/charts/libredb-studio/templates/seed-configmap.yaml +11 -0
- package/charts/libredb-studio/templates/service.yaml +22 -0
- package/charts/libredb-studio/templates/serviceaccount.yaml +13 -0
- package/charts/libredb-studio/values.schema.json +246 -0
- package/charts/libredb-studio/values.yaml +286 -0
- package/components.json +22 -0
- package/conductor/code_styleguides/typescript.md +43 -0
- package/conductor/product-guidelines.md +43 -0
- package/conductor/product.md +3 -0
- package/conductor/setup_state.json +1 -0
- package/conductor/tech-stack.md +39 -0
- package/conductor/tracks/enhance_postgres_monitoring_20251227/metadata.json +8 -0
- package/conductor/tracks/enhance_postgres_monitoring_20251227/plan.md +44 -0
- package/conductor/tracks/enhance_postgres_monitoring_20251227/spec.md +31 -0
- package/conductor/tracks.md +8 -0
- package/conductor/workflow.md +333 -0
- package/database-compose.yml +55 -0
- package/docker/postgres-init/01-extensions.sql +10 -0
- package/docker/postgres-init/02-sample-data.sql +585 -0
- package/docker/postgres.yml +68 -0
- package/docker-compose.yml +38 -0
- package/docs/AI_PLAN.md +74 -0
- package/docs/API_DOCS.md +875 -0
- package/docs/ARCHITECTURE.md +218 -0
- package/docs/DATABASE_PROVIDERS.md +358 -0
- package/docs/FEATURES.md +116 -0
- package/docs/HELM_CHART.md +252 -0
- package/docs/LOGIN_PAGE.md +178 -0
- package/docs/MONACO_EDITOR_PERFORMANCE.md +315 -0
- package/docs/OIDC_ARCH.md +681 -0
- package/docs/OIDC_SETUP.md +322 -0
- package/docs/POSTGRES_METRICS.md +516 -0
- package/docs/QUERY_OPTIMIZATION.md +370 -0
- package/docs/SEED_CONNECTIONS.md +468 -0
- package/docs/SQL_ALIAS_COMPLETION.md +190 -0
- package/docs/STORAGE_ARCHITECTURE.md +565 -0
- package/docs/STORAGE_QUICK_SETUP.md +419 -0
- package/docs/TECHNICAL_PLAN.md +36 -0
- package/docs/THEMING.md +345 -0
- package/docs/adding-a-new-database-provider.md +642 -0
- package/docs/backlogs/000-PLATFORM_DATA_SYNC_DATABASE.md +360 -0
- package/docs/backlogs/001-INLINE_DATA_EDITING.md +118 -0
- package/docs/backlogs/002-DATA_IMPORT.md +215 -0
- package/docs/backlogs/003-QUERY_TIME_MACHINE.md +183 -0
- package/docs/backlogs/004-AI_DATA_STORYTELLER.md +292 -0
- package/docs/backlogs/005-QUERY_PLAYGROUND.md +352 -0
- package/docs/backlogs/006-DATA_MASKING.md +418 -0
- package/docs/enterprise-features.md +718 -0
- package/docs/kubernetes-helm-chart-artifacthub-plan.md +803 -0
- package/docs/medium-koyeb-article-en.md +215 -0
- package/docs/plans/test-plans.md +445 -0
- package/docs/releases/RELEASE.V0.3.0.md +22 -0
- package/docs/releases/RELEASE.V0.4.0.md +154 -0
- package/docs/releases/RELEASE.V0.5.0.md +252 -0
- package/docs/releases/RELEASE_v0.5.6.md +145 -0
- package/docs/releases/RELEASE_v0.6.1.md +303 -0
- package/docs/releases/RELEASE_v0.6.7.md +292 -0
- package/docs/releases/RELEASE_v0.7.0.md +332 -0
- package/docs/releases/RELEASE_v0.8.0.md +521 -0
- package/docs/sampledb/titanic.sql +1379 -0
- package/docs/superpowers/plans/2026-03-25-seed-connections.md +1362 -0
- package/docs/superpowers/specs/2026-03-25-seed-connections-design.md +590 -0
- package/e2e/admin-dashboard.spec.ts +64 -0
- package/e2e/connection-management.spec.ts +58 -0
- package/e2e/export.spec.ts +34 -0
- package/e2e/login.spec.ts +85 -0
- package/e2e/query-execution.spec.ts +35 -0
- package/e2e/tab-management.spec.ts +64 -0
- package/eslint.config.mjs +28 -0
- package/fly.toml +43 -0
- package/next.config.ts +32 -0
- package/package.json +130 -0
- package/playwright.config.ts +34 -0
- package/postcss.config.mjs +7 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/logo.svg +32 -0
- package/public/next.svg +1 -0
- package/public/screenshots/code-generator.png +0 -0
- package/public/screenshots/connection-modal.png +0 -0
- package/public/screenshots/data-profiler.png +0 -0
- package/public/screenshots/erd-diagram.png +0 -0
- package/public/screenshots/hero-editor.png +0 -0
- package/public/screenshots/nl2sql.png +0 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/render.yaml +58 -0
- package/scripts/merge-lcov.mjs +239 -0
- package/sonar-project.properties +16 -0
- package/src/app/admin/error.tsx +46 -0
- package/src/app/admin/page.tsx +10 -0
- package/src/app/api/admin/audit/route.ts +52 -0
- package/src/app/api/admin/fleet-health/route.ts +81 -0
- package/src/app/api/ai/autopilot/route.ts +105 -0
- package/src/app/api/ai/chat/route.ts +132 -0
- package/src/app/api/ai/describe-schema/route.ts +52 -0
- package/src/app/api/ai/explain/route.ts +86 -0
- package/src/app/api/ai/impact/route.ts +97 -0
- package/src/app/api/ai/index-advisor/route.ts +98 -0
- package/src/app/api/ai/nl2sql/route.ts +87 -0
- package/src/app/api/ai/query-safety/route.ts +87 -0
- package/src/app/api/auth/login/route.ts +62 -0
- package/src/app/api/auth/logout/route.ts +25 -0
- package/src/app/api/auth/me/route.ts +10 -0
- package/src/app/api/auth/oidc/callback/route.ts +82 -0
- package/src/app/api/auth/oidc/login/route.ts +43 -0
- package/src/app/api/connections/managed/route.ts +35 -0
- package/src/app/api/db/cancel/route.ts +42 -0
- package/src/app/api/db/disconnect/route.ts +28 -0
- package/src/app/api/db/health/route.ts +49 -0
- package/src/app/api/db/maintenance/route.ts +72 -0
- package/src/app/api/db/monitoring/route.ts +62 -0
- package/src/app/api/db/multi-query/route.ts +116 -0
- package/src/app/api/db/pool-stats/route.ts +37 -0
- package/src/app/api/db/profile/route.ts +144 -0
- package/src/app/api/db/provider-meta/route.ts +49 -0
- package/src/app/api/db/query/route.ts +50 -0
- package/src/app/api/db/schema/route.ts +47 -0
- package/src/app/api/db/schema-snapshot/route.ts +42 -0
- package/src/app/api/db/test-connection/route.ts +55 -0
- package/src/app/api/db/transaction/route.ts +111 -0
- package/src/app/api/storage/[collection]/route.ts +67 -0
- package/src/app/api/storage/config/route.ts +17 -0
- package/src/app/api/storage/migrate/route.ts +45 -0
- package/src/app/api/storage/route.ts +32 -0
- package/src/app/error.tsx +49 -0
- package/src/app/global-error.tsx +55 -0
- package/src/app/globals.css +146 -0
- package/src/app/icon.svg +42 -0
- package/src/app/layout.tsx +34 -0
- package/src/app/login/login-form.tsx +301 -0
- package/src/app/login/page.tsx +11 -0
- package/src/app/monitoring/page.tsx +8 -0
- package/src/app/not-found.tsx +29 -0
- package/src/app/page.tsx +5 -0
- package/src/components/AIAutopilotPanel.tsx +238 -0
- package/src/components/CodeGenerator.tsx +271 -0
- package/src/components/CommandPalette.tsx +227 -0
- package/src/components/ConnectionModal.tsx +759 -0
- package/src/components/CreateTableModal.tsx +281 -0
- package/src/components/DataCharts.tsx +962 -0
- package/src/components/DataImportModal.tsx +582 -0
- package/src/components/DataProfiler.tsx +335 -0
- package/src/components/DatabaseDocs.tsx +251 -0
- package/src/components/MaskingSettings.tsx +414 -0
- package/src/components/MobileNav.tsx +50 -0
- package/src/components/NL2SQLPanel.tsx +281 -0
- package/src/components/PivotTable.tsx +257 -0
- package/src/components/QueryEditor.tsx +760 -0
- package/src/components/QueryHistory.tsx +344 -0
- package/src/components/QuerySafetyDialog.tsx +290 -0
- package/src/components/ResultsGrid.tsx +644 -0
- package/src/components/SaveQueryModal.tsx +104 -0
- package/src/components/SavedQueries.tsx +128 -0
- package/src/components/SchemaDiagram.tsx +473 -0
- package/src/components/SchemaDiff.tsx +473 -0
- package/src/components/SnapshotTimeline.tsx +116 -0
- package/src/components/Studio.tsx +639 -0
- package/src/components/TestDataGenerator.tsx +261 -0
- package/src/components/VisualExplain.tsx +820 -0
- package/src/components/admin/AdminDashboard.tsx +163 -0
- package/src/components/admin/tabs/AuditTab.tsx +531 -0
- package/src/components/admin/tabs/MonitoringEmbed.tsx +11 -0
- package/src/components/admin/tabs/OperationsTab.tsx +646 -0
- package/src/components/admin/tabs/OverviewTab.tsx +1328 -0
- package/src/components/admin/tabs/SecurityTab.tsx +284 -0
- package/src/components/community-section.tsx +92 -0
- package/src/components/icons/db-icons.tsx +84 -0
- package/src/components/libredb-logo.tsx +61 -0
- package/src/components/monitoring/MonitoringDashboard.tsx +345 -0
- package/src/components/monitoring/tabs/MetricChart.tsx +82 -0
- package/src/components/monitoring/tabs/OverviewTab.tsx +263 -0
- package/src/components/monitoring/tabs/PerformanceTab.tsx +254 -0
- package/src/components/monitoring/tabs/PoolTab.tsx +174 -0
- package/src/components/monitoring/tabs/QueriesTab.tsx +287 -0
- package/src/components/monitoring/tabs/SessionsTab.tsx +316 -0
- package/src/components/monitoring/tabs/StorageTab.tsx +335 -0
- package/src/components/monitoring/tabs/TablesTab.tsx +300 -0
- package/src/components/results-grid/ResultCard.tsx +111 -0
- package/src/components/results-grid/RowDetailSheet.tsx +178 -0
- package/src/components/results-grid/StatsBar.tsx +201 -0
- package/src/components/results-grid/index.ts +1 -0
- package/src/components/results-grid/utils.ts +23 -0
- package/src/components/schema-explorer/ColumnList.tsx +53 -0
- package/src/components/schema-explorer/SchemaExplorer.tsx +182 -0
- package/src/components/schema-explorer/TableItem.tsx +210 -0
- package/src/components/schema-explorer/index.ts +1 -0
- package/src/components/sidebar/ConnectionItem.tsx +105 -0
- package/src/components/sidebar/ConnectionsList.tsx +62 -0
- package/src/components/sidebar/Sidebar.tsx +130 -0
- package/src/components/sidebar/index.ts +2 -0
- package/src/components/studio/BottomPanel.tsx +286 -0
- package/src/components/studio/QueryToolbar.tsx +180 -0
- package/src/components/studio/StudioDesktopHeader.tsx +114 -0
- package/src/components/studio/StudioMobileHeader.tsx +340 -0
- package/src/components/studio/StudioTabBar.tsx +82 -0
- package/src/components/studio/index.ts +5 -0
- package/src/components/ui/accordion.tsx +66 -0
- package/src/components/ui/alert-dialog.tsx +157 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/aspect-ratio.tsx +11 -0
- package/src/components/ui/avatar.tsx +53 -0
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/breadcrumb.tsx +109 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/calendar.tsx +216 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/carousel.tsx +241 -0
- package/src/components/ui/chart.tsx +357 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/collapsible.tsx +33 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/context-menu.tsx +252 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/drawer.tsx +135 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/empty.tsx +104 -0
- package/src/components/ui/field.tsx +248 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/hover-card.tsx +44 -0
- package/src/components/ui/input-group.tsx +170 -0
- package/src/components/ui/input-otp.tsx +77 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/item.tsx +193 -0
- package/src/components/ui/kbd.tsx +28 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/menubar.tsx +276 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/pagination.tsx +127 -0
- package/src/components/ui/popover.tsx +48 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/resizable.tsx +56 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +187 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +139 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +63 -0
- package/src/components/ui/sonner.tsx +40 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +31 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +66 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +83 -0
- package/src/components/ui/toggle.tsx +47 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/exports/components.ts +15 -0
- package/src/exports/index.ts +4 -0
- package/src/exports/providers.ts +4 -0
- package/src/exports/types.ts +26 -0
- package/src/hooks/use-ai-chat.ts +182 -0
- package/src/hooks/use-all-connections.ts +66 -0
- package/src/hooks/use-api-call.ts +71 -0
- package/src/hooks/use-auth.ts +51 -0
- package/src/hooks/use-connection-form.ts +349 -0
- package/src/hooks/use-connection-manager.ts +169 -0
- package/src/hooks/use-connection-payload.ts +15 -0
- package/src/hooks/use-inline-editing.ts +109 -0
- package/src/hooks/use-mobile.ts +20 -0
- package/src/hooks/use-monitoring-data.ts +270 -0
- package/src/hooks/use-provider-metadata.ts +62 -0
- package/src/hooks/use-query-execution.ts +478 -0
- package/src/hooks/use-storage-sync.ts +259 -0
- package/src/hooks/use-tab-manager.ts +231 -0
- package/src/hooks/use-toast.ts +20 -0
- package/src/hooks/use-transaction-control.ts +64 -0
- package/src/lib/api/error-codes.ts +30 -0
- package/src/lib/api/errors.ts +236 -0
- package/src/lib/api/with-error-handler.ts +41 -0
- package/src/lib/audit.ts +105 -0
- package/src/lib/auth.ts +87 -0
- package/src/lib/connection-string-parser.ts +172 -0
- package/src/lib/data-masking.ts +385 -0
- package/src/lib/db/base-provider.ts +325 -0
- package/src/lib/db/errors.ts +317 -0
- package/src/lib/db/factory.ts +324 -0
- package/src/lib/db/index.ts +123 -0
- package/src/lib/db/providers/document/index.ts +6 -0
- package/src/lib/db/providers/document/mongodb.ts +992 -0
- package/src/lib/db/providers/keyvalue/redis.ts +554 -0
- package/src/lib/db/providers/sql/index.ts +11 -0
- package/src/lib/db/providers/sql/mssql.ts +1065 -0
- package/src/lib/db/providers/sql/mysql.ts +978 -0
- package/src/lib/db/providers/sql/oracle.ts +1044 -0
- package/src/lib/db/providers/sql/postgres.ts +1179 -0
- package/src/lib/db/providers/sql/sql-base.ts +174 -0
- package/src/lib/db/providers/sql/sqlite.ts +721 -0
- package/src/lib/db/types.ts +437 -0
- package/src/lib/db/utils/pool-manager.ts +287 -0
- package/src/lib/db/utils/query-limiter.ts +239 -0
- package/src/lib/db-ui-config.ts +86 -0
- package/src/lib/editor/mongodb-completions.ts +172 -0
- package/src/lib/editor/sql-completions.ts +280 -0
- package/src/lib/llm/base-provider.ts +117 -0
- package/src/lib/llm/factory.ts +102 -0
- package/src/lib/llm/index.ts +90 -0
- package/src/lib/llm/providers/custom.ts +181 -0
- package/src/lib/llm/providers/gemini.ts +126 -0
- package/src/lib/llm/providers/ollama.ts +154 -0
- package/src/lib/llm/providers/openai.ts +146 -0
- package/src/lib/llm/types.ts +173 -0
- package/src/lib/llm/utils/config.ts +187 -0
- package/src/lib/llm/utils/retry.ts +119 -0
- package/src/lib/llm/utils/streaming.ts +202 -0
- package/src/lib/logger.ts +127 -0
- package/src/lib/monitoring-thresholds.ts +44 -0
- package/src/lib/oidc.ts +262 -0
- package/src/lib/query-generators.ts +61 -0
- package/src/lib/schema-diff/diff-engine.ts +273 -0
- package/src/lib/schema-diff/migration-generator.ts +208 -0
- package/src/lib/schema-diff/types.ts +55 -0
- package/src/lib/seed/config-loader.ts +79 -0
- package/src/lib/seed/connection-filter.ts +49 -0
- package/src/lib/seed/credential-resolver.ts +62 -0
- package/src/lib/seed/index.ts +40 -0
- package/src/lib/seed/resolve-connection.ts +57 -0
- package/src/lib/seed/types.ts +69 -0
- package/src/lib/sql/alias-extractor.ts +267 -0
- package/src/lib/sql/index.ts +8 -0
- package/src/lib/sql/statement-splitter.ts +167 -0
- package/src/lib/sql/types.ts +40 -0
- package/src/lib/ssh/tunnel.ts +142 -0
- package/src/lib/storage/factory.ts +84 -0
- package/src/lib/storage/index.ts +14 -0
- package/src/lib/storage/local-storage.ts +99 -0
- package/src/lib/storage/providers/postgres.ts +225 -0
- package/src/lib/storage/providers/sqlite.ts +153 -0
- package/src/lib/storage/storage-facade.ts +272 -0
- package/src/lib/storage/types.ts +75 -0
- package/src/lib/time-series-buffer.ts +58 -0
- package/src/lib/types.ts +173 -0
- package/src/lib/utils.ts +6 -0
- package/src/proxy.ts +104 -0
- package/src/types/db-drivers.d.ts +23 -0
- package/src/types/html2canvas.d.ts +9 -0
- package/tests/api/admin/audit.test.ts +178 -0
- package/tests/api/admin/fleet-health.test.ts +183 -0
- package/tests/api/ai/autopilot.test.ts +174 -0
- package/tests/api/ai/chat.test.ts +250 -0
- package/tests/api/ai/describe-schema.test.ts +266 -0
- package/tests/api/ai/explain.test.ts +199 -0
- package/tests/api/ai/impact.test.ts +168 -0
- package/tests/api/ai/index-advisor.test.ts +171 -0
- package/tests/api/ai/nl2sql.test.ts +202 -0
- package/tests/api/ai/query-safety.test.ts +196 -0
- package/tests/api/auth/login.test.ts +170 -0
- package/tests/api/auth/logout.test.ts +140 -0
- package/tests/api/auth/me.test.ts +73 -0
- package/tests/api/auth/oidc-callback.test.ts +215 -0
- package/tests/api/auth/oidc-login.test.ts +127 -0
- package/tests/api/db/cancel.test.ts +198 -0
- package/tests/api/db/disconnect.test.ts +124 -0
- package/tests/api/db/health.test.ts +222 -0
- package/tests/api/db/maintenance.test.ts +263 -0
- package/tests/api/db/monitoring.test.ts +221 -0
- package/tests/api/db/multi-query.test.ts +316 -0
- package/tests/api/db/pool-stats.test.ts +135 -0
- package/tests/api/db/profile.test.ts +330 -0
- package/tests/api/db/provider-meta.test.ts +193 -0
- package/tests/api/db/query.test.ts +314 -0
- package/tests/api/db/schema-snapshot.test.ts +170 -0
- package/tests/api/db/schema.test.ts +191 -0
- package/tests/api/db/test-connection.test.ts +185 -0
- package/tests/api/db/transaction.test.ts +314 -0
- package/tests/api/proxy.test.ts +191 -0
- package/tests/api/seed/managed-route.test.ts +113 -0
- package/tests/api/storage/config.test.ts +42 -0
- package/tests/api/storage/storage-routes.test.ts +309 -0
- package/tests/components/AIAutopilotPanel.test.tsx +756 -0
- package/tests/components/AdminPage.test.tsx +33 -0
- package/tests/components/CodeGenerator.test.tsx +182 -0
- package/tests/components/CommandPalette.test.tsx +428 -0
- package/tests/components/CommunitySection.test.tsx +91 -0
- package/tests/components/ConnectionModal.mobile.test.tsx +284 -0
- package/tests/components/ConnectionModal.test.tsx +570 -0
- package/tests/components/CreateTableModal.test.tsx +383 -0
- package/tests/components/DataCharts.test.tsx +739 -0
- package/tests/components/DataImportModal.test.tsx +751 -0
- package/tests/components/DataProfiler.test.tsx +589 -0
- package/tests/components/DatabaseDocs.test.tsx +353 -0
- package/tests/components/LoginPage.test.tsx +163 -0
- package/tests/components/LoginPageOIDC.test.tsx +92 -0
- package/tests/components/MaskingSettings.test.tsx +498 -0
- package/tests/components/MobileNav.test.tsx +30 -0
- package/tests/components/MonitoringPage.test.tsx +32 -0
- package/tests/components/NL2SQLPanel.test.tsx +621 -0
- package/tests/components/Page.test.tsx +33 -0
- package/tests/components/PivotTable.test.tsx +350 -0
- package/tests/components/QueryEditor.test.tsx +1730 -0
- package/tests/components/QueryHistory.test.tsx +572 -0
- package/tests/components/QuerySafetyDialog.test.tsx +586 -0
- package/tests/components/ResultsGrid.test.tsx +804 -0
- package/tests/components/RootLayout.test.tsx +83 -0
- package/tests/components/SaveQueryModal.test.tsx +25 -0
- package/tests/components/SavedQueries.test.tsx +43 -0
- package/tests/components/SchemaDiagram.test.tsx +1034 -0
- package/tests/components/SchemaDiff.test.tsx +906 -0
- package/tests/components/SnapshotTimeline.test.tsx +174 -0
- package/tests/components/Studio.test.tsx +1030 -0
- package/tests/components/TestDataGenerator.test.tsx +291 -0
- package/tests/components/VisualExplain.test.tsx +704 -0
- package/tests/components/admin/AdminDashboard.test.tsx +205 -0
- package/tests/components/admin/AuditTab.test.tsx +220 -0
- package/tests/components/admin/MonitoringEmbed.test.tsx +58 -0
- package/tests/components/admin/OperationsTab.test.tsx +975 -0
- package/tests/components/admin/OverviewTab.test.tsx +254 -0
- package/tests/components/admin/SecurityTab.test.tsx +467 -0
- package/tests/components/monitoring/MetricChart.test.tsx +111 -0
- package/tests/components/monitoring/MonitoringDashboard.test.tsx +259 -0
- package/tests/components/monitoring/OverviewTab.test.tsx +78 -0
- package/tests/components/monitoring/PerformanceTab.test.tsx +87 -0
- package/tests/components/monitoring/PoolTab.test.tsx +42 -0
- package/tests/components/monitoring/QueriesTab.test.tsx +80 -0
- package/tests/components/monitoring/SessionsTab.test.tsx +154 -0
- package/tests/components/monitoring/StorageTab.test.tsx +127 -0
- package/tests/components/monitoring/TablesTab.test.tsx +153 -0
- package/tests/components/results-grid/ResultCard.test.tsx +105 -0
- package/tests/components/results-grid/RowDetailSheet.test.tsx +308 -0
- package/tests/components/results-grid/StatsBar.test.tsx +162 -0
- package/tests/components/schema-explorer/ColumnList.test.tsx +151 -0
- package/tests/components/schema-explorer/SchemaExplorer.test.tsx +461 -0
- package/tests/components/schema-explorer/TableItem.test.tsx +415 -0
- package/tests/components/sidebar/ConnectionItem.test.tsx +201 -0
- package/tests/components/sidebar/ConnectionsList.test.tsx +176 -0
- package/tests/components/sidebar/Sidebar.test.tsx +187 -0
- package/tests/components/studio/BottomPanel.test.tsx +383 -0
- package/tests/components/studio/QueryToolbar.test.tsx +321 -0
- package/tests/components/studio/StudioDesktopHeader.test.tsx +377 -0
- package/tests/components/studio/StudioMobileHeader.test.tsx +198 -0
- package/tests/components/studio/StudioTabBar.test.tsx +331 -0
- package/tests/fixtures/connections.ts +96 -0
- package/tests/fixtures/masking-configs.ts +86 -0
- package/tests/fixtures/query-results.ts +71 -0
- package/tests/fixtures/schemas.ts +64 -0
- package/tests/fixtures/seed-connections/invalid-config.yaml +7 -0
- package/tests/fixtures/seed-connections/minimal-config.yaml +8 -0
- package/tests/fixtures/seed-connections/mixed-credentials.yaml +23 -0
- package/tests/fixtures/seed-connections/multi-role-config.yaml +30 -0
- package/tests/fixtures/seed-connections/valid-config.json +15 -0
- package/tests/fixtures/seed-connections/valid-config.yaml +51 -0
- package/tests/helpers/mock-fetch.ts +59 -0
- package/tests/helpers/mock-monaco.ts +112 -0
- package/tests/helpers/mock-navigation.ts +28 -0
- package/tests/helpers/mock-next.ts +80 -0
- package/tests/helpers/mock-provider.ts +133 -0
- package/tests/helpers/mock-sonner.ts +29 -0
- package/tests/helpers/render-with-providers.tsx +19 -0
- package/tests/hooks/use-ai-chat.test.ts +600 -0
- package/tests/hooks/use-auth.test.ts +371 -0
- package/tests/hooks/use-connection-form.test.ts +743 -0
- package/tests/hooks/use-connection-manager.test.ts +466 -0
- package/tests/hooks/use-inline-editing.test.ts +321 -0
- package/tests/hooks/use-mobile.test.ts +177 -0
- package/tests/hooks/use-monitoring-data.test.ts +819 -0
- package/tests/hooks/use-provider-metadata.test.ts +228 -0
- package/tests/hooks/use-query-execution.test.ts +1212 -0
- package/tests/hooks/use-tab-manager.test.ts +756 -0
- package/tests/hooks/use-toast.test.ts +74 -0
- package/tests/hooks/use-transaction-control.test.ts +211 -0
- package/tests/integration/db/mongodb-provider.test.ts +698 -0
- package/tests/integration/db/mssql-provider.test.ts +840 -0
- package/tests/integration/db/mysql-provider.test.ts +872 -0
- package/tests/integration/db/oracle-provider.test.ts +843 -0
- package/tests/integration/db/postgres-provider.test.ts +1382 -0
- package/tests/integration/db/redis-provider.test.ts +526 -0
- package/tests/integration/db/sqlite-provider.test.ts +480 -0
- package/tests/integration/seed/seed-pipeline.test.ts +102 -0
- package/tests/isolated/factory-singleton.test.ts +150 -0
- package/tests/isolated/use-storage-sync.test.ts +389 -0
- package/tests/run-components.sh +196 -0
- package/tests/setup-dom.ts +58 -0
- package/tests/setup.ts +40 -0
- package/tests/unit/api-errors.test.ts +210 -0
- package/tests/unit/code-generator-functions.test.ts +271 -0
- package/tests/unit/components/column-list.test.tsx +190 -0
- package/tests/unit/components/data-import-modal.test.tsx +441 -0
- package/tests/unit/components/studio-mobile-header.test.tsx +327 -0
- package/tests/unit/data-charts-functions.test.ts +496 -0
- package/tests/unit/data-import-functions.test.ts +320 -0
- package/tests/unit/data-import-utils.test.ts +125 -0
- package/tests/unit/db/base-provider.test.ts +517 -0
- package/tests/unit/db/errors.test.ts +403 -0
- package/tests/unit/db/factory.test.ts +436 -0
- package/tests/unit/db/pool-manager.test.ts +440 -0
- package/tests/unit/db/query-limiter.test.ts +387 -0
- package/tests/unit/db/sql-base.test.ts +438 -0
- package/tests/unit/lib/api/error-codes.test.ts +39 -0
- package/tests/unit/lib/audit.test.ts +326 -0
- package/tests/unit/lib/auth.test.ts +146 -0
- package/tests/unit/lib/connection-string-parser.test.ts +424 -0
- package/tests/unit/lib/data-masking.test.ts +583 -0
- package/tests/unit/lib/db-icons.test.tsx +41 -0
- package/tests/unit/lib/monitoring-thresholds.test.ts +133 -0
- package/tests/unit/lib/oidc.test.ts +509 -0
- package/tests/unit/lib/query-generators.test.ts +127 -0
- package/tests/unit/lib/storage/factory.test.ts +71 -0
- package/tests/unit/lib/storage/local-storage.test.ts +114 -0
- package/tests/unit/lib/storage/providers/postgres.test.ts +312 -0
- package/tests/unit/lib/storage/providers/sqlite.test.ts +232 -0
- package/tests/unit/lib/storage/storage-facade-extended.test.ts +331 -0
- package/tests/unit/lib/storage/storage-facade.test.ts +184 -0
- package/tests/unit/lib/storage.test.ts +317 -0
- package/tests/unit/lib/time-series-buffer.test.ts +212 -0
- package/tests/unit/lib/utils.test.ts +24 -0
- package/tests/unit/llm/base-provider.test.ts +238 -0
- package/tests/unit/llm/config.test.ts +262 -0
- package/tests/unit/llm/custom-provider.test.ts +281 -0
- package/tests/unit/llm/gemini-provider.test.ts +248 -0
- package/tests/unit/llm/llm-factory.test.ts +155 -0
- package/tests/unit/llm/ollama-provider.test.ts +288 -0
- package/tests/unit/llm/openai-provider.test.ts +324 -0
- package/tests/unit/llm/retry.test.ts +180 -0
- package/tests/unit/llm/streaming.test.ts +355 -0
- package/tests/unit/logger.test.ts +198 -0
- package/tests/unit/mongodb-completions.test.ts +516 -0
- package/tests/unit/pivot-table-functions.test.ts +76 -0
- package/tests/unit/query-cancelled-error.test.ts +81 -0
- package/tests/unit/schema-diff/diff-engine.test.ts +367 -0
- package/tests/unit/schema-diff/migration-generator.test.ts +513 -0
- package/tests/unit/seed/config-loader.test.ts +73 -0
- package/tests/unit/seed/connection-filter.test.ts +91 -0
- package/tests/unit/seed/credential-resolver.test.ts +85 -0
- package/tests/unit/seed/index.test.ts +72 -0
- package/tests/unit/seed/resolve-connection.test.ts +74 -0
- package/tests/unit/seed/types.test.ts +129 -0
- package/tests/unit/sql/alias-extractor.test.ts +444 -0
- package/tests/unit/sql/statement-splitter.test.ts +348 -0
- package/tests/unit/sql-completions.test.ts +463 -0
- package/tests/unit/ssh-tunnel.test.ts +465 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import '../setup-dom';
|
|
2
|
+
import { mockToastError } from '../helpers/mock-sonner';
|
|
3
|
+
import '../helpers/mock-navigation';
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
7
|
+
import { act, cleanup, fireEvent, render, within } from '@testing-library/react';
|
|
8
|
+
import { CreateTableModal } from '@/components/CreateTableModal';
|
|
9
|
+
|
|
10
|
+
describe('CreateTableModal', () => {
|
|
11
|
+
afterEach(() => { cleanup(); });
|
|
12
|
+
beforeEach(() => { mockToastError.mockClear(); });
|
|
13
|
+
|
|
14
|
+
test('renders dialog content when isOpen', () => {
|
|
15
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
16
|
+
const body = within(baseElement);
|
|
17
|
+
expect(body.queryByText('Create New Table')).not.toBeNull();
|
|
18
|
+
expect(body.queryByText('SQL Preview')).not.toBeNull();
|
|
19
|
+
expect(body.queryByText('Add Column')).not.toBeNull();
|
|
20
|
+
expect(body.queryByText('General Settings')).not.toBeNull();
|
|
21
|
+
expect(body.queryByText('Column Definitions')).not.toBeNull();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('shows default id column and SQL placeholder', () => {
|
|
25
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
26
|
+
expect(baseElement.textContent).toContain('-- Name your table to see SQL');
|
|
27
|
+
expect(baseElement.textContent).toContain('SERIAL (Auto-Inc)');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// ── 1. Add Column button adds new row ──────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
test('Add Column button adds a new column row', () => {
|
|
33
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
34
|
+
const body = within(baseElement);
|
|
35
|
+
|
|
36
|
+
// Initially there is 1 column (the default "id" column)
|
|
37
|
+
const initialInputs = baseElement.querySelectorAll('input[placeholder="column_name"]');
|
|
38
|
+
expect(initialInputs.length).toBe(1);
|
|
39
|
+
|
|
40
|
+
// Click "Add Column"
|
|
41
|
+
const addBtn = body.getByText('Add Column');
|
|
42
|
+
act(() => { fireEvent.click(addBtn); });
|
|
43
|
+
|
|
44
|
+
// Now there should be 2 column rows
|
|
45
|
+
const updatedInputs = baseElement.querySelectorAll('input[placeholder="column_name"]');
|
|
46
|
+
expect(updatedInputs.length).toBe(2);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// ── 2. Remove column removes a row ─────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
test('remove button removes a column row', () => {
|
|
52
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
53
|
+
const body = within(baseElement);
|
|
54
|
+
|
|
55
|
+
// Add a second column first
|
|
56
|
+
act(() => { fireEvent.click(body.getByText('Add Column')); });
|
|
57
|
+
expect(baseElement.querySelectorAll('input[placeholder="column_name"]').length).toBe(2);
|
|
58
|
+
|
|
59
|
+
// Find trash/remove buttons — they are the last button in each column row
|
|
60
|
+
const trashButtons = baseElement.querySelectorAll('button');
|
|
61
|
+
const removeButtons = Array.from(trashButtons).filter(btn => {
|
|
62
|
+
const icon = btn.querySelector('[data-icon="Trash2"]') || btn.querySelector('svg');
|
|
63
|
+
return icon !== null && btn.textContent === '';
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Click the last remove button (removes the newly added column)
|
|
67
|
+
act(() => { fireEvent.click(removeButtons[removeButtons.length - 1]); });
|
|
68
|
+
|
|
69
|
+
expect(baseElement.querySelectorAll('input[placeholder="column_name"]').length).toBe(1);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ── 3. Cannot remove last remaining column ─────────────────────────────────
|
|
73
|
+
|
|
74
|
+
test('cannot remove the last remaining column', () => {
|
|
75
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
76
|
+
|
|
77
|
+
// Only 1 column (id) exists
|
|
78
|
+
expect(baseElement.querySelectorAll('input[placeholder="column_name"]').length).toBe(1);
|
|
79
|
+
|
|
80
|
+
// Find trash/remove buttons
|
|
81
|
+
const trashButtons = baseElement.querySelectorAll('button');
|
|
82
|
+
const removeButtons = Array.from(trashButtons).filter(btn => {
|
|
83
|
+
const icon = btn.querySelector('[data-icon="Trash2"]') || btn.querySelector('svg');
|
|
84
|
+
return icon !== null && btn.textContent === '';
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Click the only remove button
|
|
88
|
+
if (removeButtons.length > 0) {
|
|
89
|
+
act(() => { fireEvent.click(removeButtons[0]); });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Still 1 column — guard prevents removal
|
|
93
|
+
expect(baseElement.querySelectorAll('input[placeholder="column_name"]').length).toBe(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// ── 4. Table name sanitization (lowercase + underscores) ───────────────────
|
|
97
|
+
|
|
98
|
+
test('table name input sanitizes to lowercase and underscores', () => {
|
|
99
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
100
|
+
|
|
101
|
+
const tableNameInput = baseElement.querySelector('#tableName') as HTMLInputElement;
|
|
102
|
+
expect(tableNameInput).not.toBeNull();
|
|
103
|
+
|
|
104
|
+
// Type mixed-case with special characters
|
|
105
|
+
act(() => { fireEvent.change(tableNameInput, { target: { value: 'My Table-Name!123' } }); });
|
|
106
|
+
|
|
107
|
+
// The onChange handler lowercases and replaces non-alphanumeric/underscore with _
|
|
108
|
+
expect(tableNameInput.value).toBe('my_table_name_123');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// ── 5. SQL preview with column definitions ─────────────────────────────────
|
|
112
|
+
|
|
113
|
+
test('SQL preview shows CREATE TABLE with column definitions', () => {
|
|
114
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
115
|
+
|
|
116
|
+
// Set table name to trigger SQL generation
|
|
117
|
+
const tableNameInput = baseElement.querySelector('#tableName') as HTMLInputElement;
|
|
118
|
+
act(() => { fireEvent.change(tableNameInput, { target: { value: 'users' } }); });
|
|
119
|
+
|
|
120
|
+
const text = baseElement.textContent || '';
|
|
121
|
+
expect(text).toContain('CREATE TABLE users');
|
|
122
|
+
expect(text).toContain('id SERIAL PRIMARY KEY');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ── 6. NOT NULL in SQL for non-nullable columns ────────────────────────────
|
|
126
|
+
|
|
127
|
+
test('NOT NULL appears in SQL for non-nullable non-PK column', () => {
|
|
128
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
129
|
+
const body = within(baseElement);
|
|
130
|
+
|
|
131
|
+
// Set table name
|
|
132
|
+
const tableNameInput = baseElement.querySelector('#tableName') as HTMLInputElement;
|
|
133
|
+
act(() => { fireEvent.change(tableNameInput, { target: { value: 'orders' } }); });
|
|
134
|
+
|
|
135
|
+
// Add a new column
|
|
136
|
+
act(() => { fireEvent.click(body.getByText('Add Column')); });
|
|
137
|
+
|
|
138
|
+
// Set column name for the new column (second input)
|
|
139
|
+
const colInputs = baseElement.querySelectorAll('input[placeholder="column_name"]');
|
|
140
|
+
act(() => { fireEvent.change(colInputs[1], { target: { value: 'status' } }); });
|
|
141
|
+
|
|
142
|
+
// New column defaults: isNullable=true, isPrimary=false
|
|
143
|
+
// Find Nullable checkboxes (role="checkbox") — Radix Checkbox renders as button[role="checkbox"]
|
|
144
|
+
const checkboxes = baseElement.querySelectorAll('button[role="checkbox"]');
|
|
145
|
+
// Each column row has 3 checkboxes: PK, Null, Unq
|
|
146
|
+
// Row 0: checkboxes[0]=PK, checkboxes[1]=Null, checkboxes[2]=Unq
|
|
147
|
+
// Row 1: checkboxes[3]=PK, checkboxes[4]=Null, checkboxes[5]=Unq
|
|
148
|
+
const nullCheckbox = checkboxes[4];
|
|
149
|
+
|
|
150
|
+
// New column is nullable by default (checked)
|
|
151
|
+
expect(nullCheckbox.getAttribute('data-state')).toBe('checked');
|
|
152
|
+
|
|
153
|
+
// Uncheck nullable
|
|
154
|
+
act(() => { fireEvent.click(nullCheckbox); });
|
|
155
|
+
expect(nullCheckbox.getAttribute('data-state')).toBe('unchecked');
|
|
156
|
+
|
|
157
|
+
// SQL should now contain NOT NULL for the status column
|
|
158
|
+
const text = baseElement.textContent || '';
|
|
159
|
+
expect(text).toContain('status VARCHAR(255) NOT NULL');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// ── 7. UNIQUE in SQL for unique columns ────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
test('UNIQUE appears in SQL for unique non-PK column', () => {
|
|
165
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
166
|
+
const body = within(baseElement);
|
|
167
|
+
|
|
168
|
+
// Set table name
|
|
169
|
+
const tableNameInput = baseElement.querySelector('#tableName') as HTMLInputElement;
|
|
170
|
+
act(() => { fireEvent.change(tableNameInput, { target: { value: 'products' } }); });
|
|
171
|
+
|
|
172
|
+
// Add a new column
|
|
173
|
+
act(() => { fireEvent.click(body.getByText('Add Column')); });
|
|
174
|
+
|
|
175
|
+
// Set column name
|
|
176
|
+
const colInputs = baseElement.querySelectorAll('input[placeholder="column_name"]');
|
|
177
|
+
act(() => { fireEvent.change(colInputs[1], { target: { value: 'sku' } }); });
|
|
178
|
+
|
|
179
|
+
// Find checkboxes for column row 1
|
|
180
|
+
const checkboxes = baseElement.querySelectorAll('button[role="checkbox"]');
|
|
181
|
+
// Row 1: PK=checkboxes[3], Null=checkboxes[4], Unq=checkboxes[5]
|
|
182
|
+
const uniqueCheckbox = checkboxes[5];
|
|
183
|
+
|
|
184
|
+
// Initially unchecked
|
|
185
|
+
expect(uniqueCheckbox.getAttribute('data-state')).toBe('unchecked');
|
|
186
|
+
|
|
187
|
+
// Check unique
|
|
188
|
+
act(() => { fireEvent.click(uniqueCheckbox); });
|
|
189
|
+
expect(uniqueCheckbox.getAttribute('data-state')).toBe('checked');
|
|
190
|
+
|
|
191
|
+
// Also uncheck nullable so NOT NULL + UNIQUE both appear
|
|
192
|
+
const nullCheckbox = checkboxes[4];
|
|
193
|
+
act(() => { fireEvent.click(nullCheckbox); });
|
|
194
|
+
|
|
195
|
+
const text = baseElement.textContent || '';
|
|
196
|
+
expect(text).toContain('sku VARCHAR(255) NOT NULL UNIQUE');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// ── 8. DEFAULT value in SQL ────────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
test('DEFAULT value appears in SQL when set', () => {
|
|
202
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
203
|
+
const body = within(baseElement);
|
|
204
|
+
|
|
205
|
+
// Set table name
|
|
206
|
+
const tableNameInput = baseElement.querySelector('#tableName') as HTMLInputElement;
|
|
207
|
+
act(() => { fireEvent.change(tableNameInput, { target: { value: 'logs' } }); });
|
|
208
|
+
|
|
209
|
+
// Add a new column
|
|
210
|
+
act(() => { fireEvent.click(body.getByText('Add Column')); });
|
|
211
|
+
|
|
212
|
+
// Set column name
|
|
213
|
+
const colInputs = baseElement.querySelectorAll('input[placeholder="column_name"]');
|
|
214
|
+
act(() => { fireEvent.change(colInputs[1], { target: { value: 'level' } }); });
|
|
215
|
+
|
|
216
|
+
// The component doesn't have a visible default value input in the main column row,
|
|
217
|
+
// but the generateSQL function uses col.defaultValue. Since there's no UI for it
|
|
218
|
+
// in the current component (defaultValue is always '' in UI), we verify the SQL
|
|
219
|
+
// generation logic works by checking that an empty default produces no DEFAULT clause.
|
|
220
|
+
const text = baseElement.textContent || '';
|
|
221
|
+
expect(text).toContain('level VARCHAR(255)');
|
|
222
|
+
expect(text).not.toContain('DEFAULT');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// ── 9. Validate empty table name -> button disabled ─────────────────────────
|
|
226
|
+
|
|
227
|
+
test('CREATE TABLE button is disabled when table name is empty', () => {
|
|
228
|
+
const onTableCreated = mock(() => {});
|
|
229
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={onTableCreated} />);
|
|
230
|
+
const body = within(baseElement);
|
|
231
|
+
|
|
232
|
+
// Table name is empty by default
|
|
233
|
+
const createBtn = body.getByText('CREATE TABLE').closest('button') as HTMLButtonElement;
|
|
234
|
+
expect(createBtn.disabled).toBe(true);
|
|
235
|
+
|
|
236
|
+
// Click should not invoke onTableCreated
|
|
237
|
+
act(() => { fireEvent.click(createBtn); });
|
|
238
|
+
expect(onTableCreated).not.toHaveBeenCalled();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ── 10. Validate empty column names -> error toast ─────────────────────────
|
|
242
|
+
|
|
243
|
+
test('handleCreate shows error toast when a column has empty name', () => {
|
|
244
|
+
const onTableCreated = mock(() => {});
|
|
245
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={onTableCreated} />);
|
|
246
|
+
const body = within(baseElement);
|
|
247
|
+
|
|
248
|
+
// Set table name
|
|
249
|
+
const tableNameInput = baseElement.querySelector('#tableName') as HTMLInputElement;
|
|
250
|
+
act(() => { fireEvent.change(tableNameInput, { target: { value: 'test_table' } }); });
|
|
251
|
+
|
|
252
|
+
// Add a second column but leave its name empty
|
|
253
|
+
act(() => { fireEvent.click(body.getByText('Add Column')); });
|
|
254
|
+
|
|
255
|
+
// The new column has name='' by default
|
|
256
|
+
const createBtn = body.getByText('CREATE TABLE');
|
|
257
|
+
act(() => { fireEvent.click(createBtn); });
|
|
258
|
+
|
|
259
|
+
expect(mockToastError).toHaveBeenCalledWith('All columns must have a name');
|
|
260
|
+
expect(onTableCreated).not.toHaveBeenCalled();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// ── 11. handleCreate calls onTableCreated + onClose on success ─────────────
|
|
264
|
+
|
|
265
|
+
test('handleCreate calls onTableCreated and onClose on success', () => {
|
|
266
|
+
const onTableCreated = mock(() => {});
|
|
267
|
+
const onClose = mock(() => {});
|
|
268
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={onClose} onTableCreated={onTableCreated} />);
|
|
269
|
+
const body = within(baseElement);
|
|
270
|
+
|
|
271
|
+
// Set table name
|
|
272
|
+
const tableNameInput = baseElement.querySelector('#tableName') as HTMLInputElement;
|
|
273
|
+
act(() => { fireEvent.change(tableNameInput, { target: { value: 'customers' } }); });
|
|
274
|
+
|
|
275
|
+
// Default id column already has a name, so validation passes
|
|
276
|
+
const createBtn = body.getByText('CREATE TABLE');
|
|
277
|
+
act(() => { fireEvent.click(createBtn); });
|
|
278
|
+
|
|
279
|
+
expect(onTableCreated).toHaveBeenCalledTimes(1);
|
|
280
|
+
// Verify the SQL was passed
|
|
281
|
+
const sqlArg = (onTableCreated as ReturnType<typeof mock>).mock.calls[0][0] as string;
|
|
282
|
+
expect(sqlArg).toContain('CREATE TABLE customers');
|
|
283
|
+
expect(sqlArg).toContain('id SERIAL PRIMARY KEY');
|
|
284
|
+
|
|
285
|
+
expect(onClose).toHaveBeenCalledTimes(1);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// ── 12. State resets after creation ────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
test('state resets after successful creation', () => {
|
|
291
|
+
const onTableCreated = mock(() => {});
|
|
292
|
+
const onClose = mock(() => {});
|
|
293
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={onClose} onTableCreated={onTableCreated} />);
|
|
294
|
+
const body = within(baseElement);
|
|
295
|
+
|
|
296
|
+
// Set table name and add a column
|
|
297
|
+
const tableNameInput = baseElement.querySelector('#tableName') as HTMLInputElement;
|
|
298
|
+
act(() => { fireEvent.change(tableNameInput, { target: { value: 'temp_table' } }); });
|
|
299
|
+
act(() => { fireEvent.click(body.getByText('Add Column')); });
|
|
300
|
+
|
|
301
|
+
// Fill second column name
|
|
302
|
+
const colInputs = baseElement.querySelectorAll('input[placeholder="column_name"]');
|
|
303
|
+
act(() => { fireEvent.change(colInputs[1], { target: { value: 'name' } }); });
|
|
304
|
+
|
|
305
|
+
// We should have 2 columns now
|
|
306
|
+
expect(baseElement.querySelectorAll('input[placeholder="column_name"]').length).toBe(2);
|
|
307
|
+
|
|
308
|
+
// Create table
|
|
309
|
+
act(() => { fireEvent.click(body.getByText('CREATE TABLE')); });
|
|
310
|
+
|
|
311
|
+
// After creation, state resets: table name should be empty, columns back to default (1)
|
|
312
|
+
expect(tableNameInput.value).toBe('');
|
|
313
|
+
expect(baseElement.querySelectorAll('input[placeholder="column_name"]').length).toBe(1);
|
|
314
|
+
|
|
315
|
+
// The remaining column should be the default "id"
|
|
316
|
+
const resetColInput = baseElement.querySelector('input[placeholder="column_name"]') as HTMLInputElement;
|
|
317
|
+
expect(resetColInput.value).toBe('id');
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// ── 13. PK checkbox auto-unchecks Nullable ─────────────────────────────────
|
|
321
|
+
|
|
322
|
+
test('checking PK auto-unchecks Nullable', () => {
|
|
323
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
324
|
+
const body = within(baseElement);
|
|
325
|
+
|
|
326
|
+
// Add a new column (default: isPrimary=false, isNullable=true)
|
|
327
|
+
act(() => { fireEvent.click(body.getByText('Add Column')); });
|
|
328
|
+
|
|
329
|
+
const checkboxes = baseElement.querySelectorAll('button[role="checkbox"]');
|
|
330
|
+
// Row 1: PK=checkboxes[3], Null=checkboxes[4], Unq=checkboxes[5]
|
|
331
|
+
const pkCheckbox = checkboxes[3];
|
|
332
|
+
const nullCheckbox = checkboxes[4];
|
|
333
|
+
|
|
334
|
+
// Initially: PK unchecked, Null checked
|
|
335
|
+
expect(pkCheckbox.getAttribute('data-state')).toBe('unchecked');
|
|
336
|
+
expect(nullCheckbox.getAttribute('data-state')).toBe('checked');
|
|
337
|
+
|
|
338
|
+
// Check PK
|
|
339
|
+
act(() => { fireEvent.click(pkCheckbox); });
|
|
340
|
+
|
|
341
|
+
// PK should be checked, Null should be auto-unchecked
|
|
342
|
+
expect(pkCheckbox.getAttribute('data-state')).toBe('checked');
|
|
343
|
+
expect(nullCheckbox.getAttribute('data-state')).toBe('unchecked');
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// ── 14. Cancel button calls onClose ────────────────────────────────────────
|
|
347
|
+
|
|
348
|
+
test('Cancel button calls onClose', () => {
|
|
349
|
+
const onClose = mock(() => {});
|
|
350
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={onClose} onTableCreated={mock(() => {})} />);
|
|
351
|
+
const body = within(baseElement);
|
|
352
|
+
|
|
353
|
+
const cancelBtn = body.getByText('Cancel');
|
|
354
|
+
act(() => { fireEvent.click(cancelBtn); });
|
|
355
|
+
|
|
356
|
+
expect(onClose).toHaveBeenCalledTimes(1);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// ── 15. Column type selector changes SQL preview ───────────────────────────
|
|
360
|
+
|
|
361
|
+
test('default column type VARCHAR(255) appears in SQL for new columns', () => {
|
|
362
|
+
const { baseElement } = render(<CreateTableModal isOpen onClose={mock(() => {})} onTableCreated={mock(() => {})} />);
|
|
363
|
+
const body = within(baseElement);
|
|
364
|
+
|
|
365
|
+
// Set table name
|
|
366
|
+
const tableNameInput = baseElement.querySelector('#tableName') as HTMLInputElement;
|
|
367
|
+
act(() => { fireEvent.change(tableNameInput, { target: { value: 'items' } }); });
|
|
368
|
+
|
|
369
|
+
// Add a new column (defaults to VARCHAR(255))
|
|
370
|
+
act(() => { fireEvent.click(body.getByText('Add Column')); });
|
|
371
|
+
|
|
372
|
+
// Name the new column
|
|
373
|
+
const colInputs = baseElement.querySelectorAll('input[placeholder="column_name"]');
|
|
374
|
+
act(() => { fireEvent.change(colInputs[1], { target: { value: 'description' } }); });
|
|
375
|
+
|
|
376
|
+
// SQL preview should show the default type
|
|
377
|
+
const text = baseElement.textContent || '';
|
|
378
|
+
expect(text).toContain('description VARCHAR(255)');
|
|
379
|
+
|
|
380
|
+
// Also verify the default id column uses SERIAL
|
|
381
|
+
expect(text).toContain('id SERIAL PRIMARY KEY');
|
|
382
|
+
});
|
|
383
|
+
});
|