@libredb/studio 0.9.7 → 0.9.12

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.
Files changed (688) hide show
  1. package/dist/chunk-34YQUUCM.mjs +319 -0
  2. package/dist/chunk-34YQUUCM.mjs.map +1 -0
  3. package/dist/chunk-4LVB3K53.mjs +37 -0
  4. package/dist/chunk-4LVB3K53.mjs.map +1 -0
  5. package/dist/chunk-6DRZXXNT.mjs +100 -0
  6. package/dist/chunk-6DRZXXNT.mjs.map +1 -0
  7. package/dist/chunk-CZVV3JJB.mjs +160 -0
  8. package/dist/chunk-CZVV3JJB.mjs.map +1 -0
  9. package/dist/chunk-D4WVWWWF.js +332 -0
  10. package/dist/chunk-D4WVWWWF.js.map +1 -0
  11. package/dist/chunk-DY3KXE44.mjs +3 -0
  12. package/dist/chunk-DY3KXE44.mjs.map +1 -0
  13. package/dist/chunk-DZ2UB3C6.mjs +6679 -0
  14. package/dist/chunk-DZ2UB3C6.mjs.map +1 -0
  15. package/dist/chunk-FYSE52VB.js +242 -0
  16. package/dist/chunk-FYSE52VB.js.map +1 -0
  17. package/dist/chunk-G4WYE6TI.js +4 -0
  18. package/dist/chunk-G4WYE6TI.js.map +1 -0
  19. package/dist/chunk-JOGLIOFO.js +1310 -0
  20. package/dist/chunk-JOGLIOFO.js.map +1 -0
  21. package/dist/chunk-JZO5KRZN.js +165 -0
  22. package/dist/chunk-JZO5KRZN.js.map +1 -0
  23. package/dist/chunk-KV356UXJ.js +253 -0
  24. package/dist/chunk-KV356UXJ.js.map +1 -0
  25. package/dist/chunk-PPODO6HX.mjs +237 -0
  26. package/dist/chunk-PPODO6HX.mjs.map +1 -0
  27. package/dist/chunk-PTIRB2JO.js +258 -0
  28. package/dist/chunk-PTIRB2JO.js.map +1 -0
  29. package/dist/chunk-Q6LRDBK7.js +42 -0
  30. package/dist/chunk-Q6LRDBK7.js.map +1 -0
  31. package/dist/chunk-QJP5FZRY.mjs +255 -0
  32. package/dist/chunk-QJP5FZRY.mjs.map +1 -0
  33. package/dist/chunk-R3POCJK6.mjs +248 -0
  34. package/dist/chunk-R3POCJK6.mjs.map +1 -0
  35. package/dist/chunk-RBVDMLFV.js +6747 -0
  36. package/dist/chunk-RBVDMLFV.js.map +1 -0
  37. package/dist/chunk-RCQB4FCE.js +186 -0
  38. package/dist/chunk-RCQB4FCE.js.map +1 -0
  39. package/dist/chunk-SR5DRGBX.mjs +174 -0
  40. package/dist/chunk-SR5DRGBX.mjs.map +1 -0
  41. package/dist/chunk-VLCRUZX7.js +102 -0
  42. package/dist/chunk-VLCRUZX7.js.map +1 -0
  43. package/dist/chunk-VWVRUCQO.mjs +1289 -0
  44. package/dist/chunk-VWVRUCQO.mjs.map +1 -0
  45. package/dist/components.d.mts +273 -0
  46. package/dist/components.d.ts +273 -0
  47. package/dist/components.js +59 -0
  48. package/dist/components.js.map +1 -0
  49. package/dist/components.mjs +6 -0
  50. package/dist/components.mjs.map +1 -0
  51. package/dist/custom-BNDOYC5P.js +134 -0
  52. package/dist/custom-BNDOYC5P.js.map +1 -0
  53. package/dist/custom-S2EKFMP3.mjs +132 -0
  54. package/dist/custom-S2EKFMP3.mjs.map +1 -0
  55. package/dist/gemini-4ASHNK4H.js +81 -0
  56. package/dist/gemini-4ASHNK4H.js.map +1 -0
  57. package/dist/gemini-C5RBLQEJ.mjs +79 -0
  58. package/dist/gemini-C5RBLQEJ.mjs.map +1 -0
  59. package/dist/index.d.mts +6 -0
  60. package/dist/index.d.ts +6 -0
  61. package/dist/index.js +95 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/index.mjs +10 -0
  64. package/dist/index.mjs.map +1 -0
  65. package/dist/mongodb-XMZEZA4A.mjs +748 -0
  66. package/dist/mongodb-XMZEZA4A.mjs.map +1 -0
  67. package/dist/mongodb-YQJJTLX3.js +750 -0
  68. package/dist/mongodb-YQJJTLX3.js.map +1 -0
  69. package/dist/mssql-PMOU4D36.js +916 -0
  70. package/dist/mssql-PMOU4D36.js.map +1 -0
  71. package/{src/lib/db/providers/sql/mssql.ts → dist/mssql-ZH5VP2C5.mjs} +268 -423
  72. package/dist/mssql-ZH5VP2C5.mjs.map +1 -0
  73. package/{src/lib/db/providers/sql/mysql.ts → dist/mysql-I3WJQXN2.mjs} +277 -428
  74. package/dist/mysql-I3WJQXN2.mjs.map +1 -0
  75. package/dist/mysql-Y3MSA5QY.js +833 -0
  76. package/dist/mysql-Y3MSA5QY.js.map +1 -0
  77. package/dist/ollama-26BYLVEV.mjs +115 -0
  78. package/dist/ollama-26BYLVEV.mjs.map +1 -0
  79. package/dist/ollama-HVWAGKQC.js +117 -0
  80. package/dist/ollama-HVWAGKQC.js.map +1 -0
  81. package/dist/openai-4U56KPG7.mjs +111 -0
  82. package/dist/openai-4U56KPG7.mjs.map +1 -0
  83. package/dist/openai-AK3R37BS.js +113 -0
  84. package/dist/openai-AK3R37BS.js.map +1 -0
  85. package/dist/oracle-L6VEAVXO.js +917 -0
  86. package/dist/oracle-L6VEAVXO.js.map +1 -0
  87. package/{src/lib/db/providers/sql/oracle.ts → dist/oracle-P2G7T4P4.mjs} +321 -454
  88. package/dist/oracle-P2G7T4P4.mjs.map +1 -0
  89. package/{src/lib/db/providers/sql/postgres.ts → dist/postgres-O5KOQUVP.mjs} +261 -471
  90. package/dist/postgres-O5KOQUVP.mjs.map +1 -0
  91. package/dist/postgres-RLCWNFFX.js +971 -0
  92. package/dist/postgres-RLCWNFFX.js.map +1 -0
  93. package/dist/providers.d.mts +149 -0
  94. package/dist/providers.d.ts +149 -0
  95. package/dist/providers.js +44 -0
  96. package/dist/providers.js.map +1 -0
  97. package/dist/providers.mjs +7 -0
  98. package/dist/providers.mjs.map +1 -0
  99. package/dist/redis-4WMQOVLX.mjs +435 -0
  100. package/dist/redis-4WMQOVLX.mjs.map +1 -0
  101. package/dist/redis-QVQ6YU62.js +441 -0
  102. package/dist/redis-QVQ6YU62.js.map +1 -0
  103. package/dist/sqlite-4I2P2OGQ.js +554 -0
  104. package/dist/sqlite-4I2P2OGQ.js.map +1 -0
  105. package/dist/sqlite-OA4YJX5S.mjs +531 -0
  106. package/dist/sqlite-OA4YJX5S.mjs.map +1 -0
  107. package/dist/types-BJvJfxSY.d.mts +141 -0
  108. package/dist/types-BJvJfxSY.d.ts +141 -0
  109. package/dist/types-ClAg_v5k.d.mts +343 -0
  110. package/dist/types-Der_X8E8.d.ts +343 -0
  111. package/dist/types.d.mts +2 -0
  112. package/dist/types.d.ts +2 -0
  113. package/dist/types.js +6 -0
  114. package/dist/types.js.map +1 -0
  115. package/dist/types.mjs +3 -0
  116. package/dist/types.mjs.map +1 -0
  117. package/dist/workspace.d.mts +80 -0
  118. package/dist/workspace.d.ts +80 -0
  119. package/dist/workspace.js +4182 -0
  120. package/dist/workspace.js.map +1 -0
  121. package/dist/workspace.mjs +4155 -0
  122. package/dist/workspace.mjs.map +1 -0
  123. package/package.json +60 -5
  124. package/.claude/settings.local.json +0 -127
  125. package/.cursorrules +0 -426
  126. package/.devin/wiki.json +0 -143
  127. package/.dockerignore +0 -80
  128. package/.env.example +0 -159
  129. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -49
  130. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -29
  131. package/.github/PULL_REQUEST_TEMPLATE.md +0 -57
  132. package/.github/workflows/ci.yml +0 -185
  133. package/.github/workflows/codeql.yml +0 -57
  134. package/.github/workflows/docker-build-push.yml +0 -118
  135. package/.github/workflows/helm-release.yml +0 -113
  136. package/CLAUDE.md +0 -265
  137. package/CODE_OF_CONDUCT.md +0 -124
  138. package/CONTRIBUTING.md +0 -154
  139. package/Dockerfile +0 -73
  140. package/SECURITY.md +0 -107
  141. package/artifacthub-repo.yml +0 -4
  142. package/bun.lock +0 -1714
  143. package/bunfig.toml +0 -3
  144. package/charts/libredb-studio/.helmignore +0 -11
  145. package/charts/libredb-studio/Chart.lock +0 -6
  146. package/charts/libredb-studio/Chart.yaml +0 -50
  147. package/charts/libredb-studio/README.md +0 -206
  148. package/charts/libredb-studio/templates/NOTES.txt +0 -59
  149. package/charts/libredb-studio/templates/_helpers.tpl +0 -135
  150. package/charts/libredb-studio/templates/configmap.yaml +0 -37
  151. package/charts/libredb-studio/templates/deployment.yaml +0 -184
  152. package/charts/libredb-studio/templates/hpa.yaml +0 -32
  153. package/charts/libredb-studio/templates/ingress.yaml +0 -41
  154. package/charts/libredb-studio/templates/networkpolicy.yaml +0 -50
  155. package/charts/libredb-studio/templates/pdb.yaml +0 -18
  156. package/charts/libredb-studio/templates/pvc.yaml +0 -23
  157. package/charts/libredb-studio/templates/secret.yaml +0 -30
  158. package/charts/libredb-studio/templates/seed-configmap.yaml +0 -11
  159. package/charts/libredb-studio/templates/service.yaml +0 -22
  160. package/charts/libredb-studio/templates/serviceaccount.yaml +0 -13
  161. package/charts/libredb-studio/values.schema.json +0 -246
  162. package/charts/libredb-studio/values.yaml +0 -286
  163. package/components.json +0 -22
  164. package/conductor/code_styleguides/typescript.md +0 -43
  165. package/conductor/product-guidelines.md +0 -43
  166. package/conductor/product.md +0 -3
  167. package/conductor/setup_state.json +0 -1
  168. package/conductor/tech-stack.md +0 -39
  169. package/conductor/tracks/enhance_postgres_monitoring_20251227/metadata.json +0 -8
  170. package/conductor/tracks/enhance_postgres_monitoring_20251227/plan.md +0 -44
  171. package/conductor/tracks/enhance_postgres_monitoring_20251227/spec.md +0 -31
  172. package/conductor/tracks.md +0 -8
  173. package/conductor/workflow.md +0 -333
  174. package/database-compose.yml +0 -55
  175. package/docker/postgres-init/01-extensions.sql +0 -10
  176. package/docker/postgres-init/02-sample-data.sql +0 -585
  177. package/docker/postgres.yml +0 -68
  178. package/docker-compose.yml +0 -38
  179. package/docs/AI_PLAN.md +0 -74
  180. package/docs/API_DOCS.md +0 -875
  181. package/docs/ARCHITECTURE.md +0 -218
  182. package/docs/DATABASE_PROVIDERS.md +0 -358
  183. package/docs/FEATURES.md +0 -116
  184. package/docs/HELM_CHART.md +0 -252
  185. package/docs/LOGIN_PAGE.md +0 -178
  186. package/docs/MONACO_EDITOR_PERFORMANCE.md +0 -315
  187. package/docs/OIDC_ARCH.md +0 -681
  188. package/docs/OIDC_SETUP.md +0 -322
  189. package/docs/POSTGRES_METRICS.md +0 -516
  190. package/docs/QUERY_OPTIMIZATION.md +0 -370
  191. package/docs/SEED_CONNECTIONS.md +0 -468
  192. package/docs/SQL_ALIAS_COMPLETION.md +0 -190
  193. package/docs/STORAGE_ARCHITECTURE.md +0 -565
  194. package/docs/STORAGE_QUICK_SETUP.md +0 -419
  195. package/docs/TECHNICAL_PLAN.md +0 -36
  196. package/docs/THEMING.md +0 -345
  197. package/docs/adding-a-new-database-provider.md +0 -642
  198. package/docs/backlogs/000-PLATFORM_DATA_SYNC_DATABASE.md +0 -360
  199. package/docs/backlogs/001-INLINE_DATA_EDITING.md +0 -118
  200. package/docs/backlogs/002-DATA_IMPORT.md +0 -215
  201. package/docs/backlogs/003-QUERY_TIME_MACHINE.md +0 -183
  202. package/docs/backlogs/004-AI_DATA_STORYTELLER.md +0 -292
  203. package/docs/backlogs/005-QUERY_PLAYGROUND.md +0 -352
  204. package/docs/backlogs/006-DATA_MASKING.md +0 -418
  205. package/docs/enterprise-features.md +0 -718
  206. package/docs/kubernetes-helm-chart-artifacthub-plan.md +0 -803
  207. package/docs/medium-koyeb-article-en.md +0 -215
  208. package/docs/plans/test-plans.md +0 -445
  209. package/docs/releases/RELEASE.V0.3.0.md +0 -22
  210. package/docs/releases/RELEASE.V0.4.0.md +0 -154
  211. package/docs/releases/RELEASE.V0.5.0.md +0 -252
  212. package/docs/releases/RELEASE_v0.5.6.md +0 -145
  213. package/docs/releases/RELEASE_v0.6.1.md +0 -303
  214. package/docs/releases/RELEASE_v0.6.7.md +0 -292
  215. package/docs/releases/RELEASE_v0.7.0.md +0 -332
  216. package/docs/releases/RELEASE_v0.8.0.md +0 -521
  217. package/docs/sampledb/titanic.sql +0 -1379
  218. package/docs/superpowers/plans/2026-03-25-seed-connections.md +0 -1362
  219. package/docs/superpowers/specs/2026-03-25-seed-connections-design.md +0 -590
  220. package/e2e/admin-dashboard.spec.ts +0 -64
  221. package/e2e/connection-management.spec.ts +0 -58
  222. package/e2e/export.spec.ts +0 -34
  223. package/e2e/login.spec.ts +0 -85
  224. package/e2e/query-execution.spec.ts +0 -35
  225. package/e2e/tab-management.spec.ts +0 -64
  226. package/eslint.config.mjs +0 -28
  227. package/fly.toml +0 -43
  228. package/next.config.ts +0 -32
  229. package/playwright.config.ts +0 -34
  230. package/postcss.config.mjs +0 -7
  231. package/public/favicon-32x32.png +0 -0
  232. package/public/favicon.ico +0 -0
  233. package/public/file.svg +0 -1
  234. package/public/globe.svg +0 -1
  235. package/public/logo.svg +0 -32
  236. package/public/next.svg +0 -1
  237. package/public/screenshots/code-generator.png +0 -0
  238. package/public/screenshots/connection-modal.png +0 -0
  239. package/public/screenshots/data-profiler.png +0 -0
  240. package/public/screenshots/erd-diagram.png +0 -0
  241. package/public/screenshots/hero-editor.png +0 -0
  242. package/public/screenshots/nl2sql.png +0 -0
  243. package/public/vercel.svg +0 -1
  244. package/public/window.svg +0 -1
  245. package/render.yaml +0 -58
  246. package/scripts/merge-lcov.mjs +0 -239
  247. package/sonar-project.properties +0 -16
  248. package/src/app/admin/error.tsx +0 -46
  249. package/src/app/admin/page.tsx +0 -10
  250. package/src/app/api/admin/audit/route.ts +0 -52
  251. package/src/app/api/admin/fleet-health/route.ts +0 -81
  252. package/src/app/api/ai/autopilot/route.ts +0 -105
  253. package/src/app/api/ai/chat/route.ts +0 -132
  254. package/src/app/api/ai/describe-schema/route.ts +0 -52
  255. package/src/app/api/ai/explain/route.ts +0 -86
  256. package/src/app/api/ai/impact/route.ts +0 -97
  257. package/src/app/api/ai/index-advisor/route.ts +0 -98
  258. package/src/app/api/ai/nl2sql/route.ts +0 -87
  259. package/src/app/api/ai/query-safety/route.ts +0 -87
  260. package/src/app/api/auth/login/route.ts +0 -62
  261. package/src/app/api/auth/logout/route.ts +0 -25
  262. package/src/app/api/auth/me/route.ts +0 -10
  263. package/src/app/api/auth/oidc/callback/route.ts +0 -82
  264. package/src/app/api/auth/oidc/login/route.ts +0 -43
  265. package/src/app/api/connections/managed/route.ts +0 -35
  266. package/src/app/api/db/cancel/route.ts +0 -42
  267. package/src/app/api/db/disconnect/route.ts +0 -28
  268. package/src/app/api/db/health/route.ts +0 -49
  269. package/src/app/api/db/maintenance/route.ts +0 -72
  270. package/src/app/api/db/monitoring/route.ts +0 -62
  271. package/src/app/api/db/multi-query/route.ts +0 -116
  272. package/src/app/api/db/pool-stats/route.ts +0 -37
  273. package/src/app/api/db/profile/route.ts +0 -144
  274. package/src/app/api/db/provider-meta/route.ts +0 -49
  275. package/src/app/api/db/query/route.ts +0 -50
  276. package/src/app/api/db/schema/route.ts +0 -47
  277. package/src/app/api/db/schema-snapshot/route.ts +0 -42
  278. package/src/app/api/db/test-connection/route.ts +0 -55
  279. package/src/app/api/db/transaction/route.ts +0 -111
  280. package/src/app/api/storage/[collection]/route.ts +0 -67
  281. package/src/app/api/storage/config/route.ts +0 -17
  282. package/src/app/api/storage/migrate/route.ts +0 -45
  283. package/src/app/api/storage/route.ts +0 -32
  284. package/src/app/error.tsx +0 -49
  285. package/src/app/global-error.tsx +0 -55
  286. package/src/app/globals.css +0 -146
  287. package/src/app/icon.svg +0 -42
  288. package/src/app/layout.tsx +0 -34
  289. package/src/app/login/login-form.tsx +0 -301
  290. package/src/app/login/page.tsx +0 -11
  291. package/src/app/monitoring/page.tsx +0 -8
  292. package/src/app/not-found.tsx +0 -29
  293. package/src/app/page.tsx +0 -5
  294. package/src/components/AIAutopilotPanel.tsx +0 -238
  295. package/src/components/CodeGenerator.tsx +0 -271
  296. package/src/components/CommandPalette.tsx +0 -227
  297. package/src/components/ConnectionModal.tsx +0 -759
  298. package/src/components/CreateTableModal.tsx +0 -281
  299. package/src/components/DataCharts.tsx +0 -962
  300. package/src/components/DataImportModal.tsx +0 -582
  301. package/src/components/DataProfiler.tsx +0 -335
  302. package/src/components/DatabaseDocs.tsx +0 -251
  303. package/src/components/MaskingSettings.tsx +0 -414
  304. package/src/components/MobileNav.tsx +0 -50
  305. package/src/components/NL2SQLPanel.tsx +0 -281
  306. package/src/components/PivotTable.tsx +0 -257
  307. package/src/components/QueryEditor.tsx +0 -760
  308. package/src/components/QueryHistory.tsx +0 -344
  309. package/src/components/QuerySafetyDialog.tsx +0 -290
  310. package/src/components/ResultsGrid.tsx +0 -644
  311. package/src/components/SaveQueryModal.tsx +0 -104
  312. package/src/components/SavedQueries.tsx +0 -128
  313. package/src/components/SchemaDiagram.tsx +0 -473
  314. package/src/components/SchemaDiff.tsx +0 -473
  315. package/src/components/SnapshotTimeline.tsx +0 -116
  316. package/src/components/Studio.tsx +0 -639
  317. package/src/components/TestDataGenerator.tsx +0 -261
  318. package/src/components/VisualExplain.tsx +0 -820
  319. package/src/components/admin/AdminDashboard.tsx +0 -163
  320. package/src/components/admin/tabs/AuditTab.tsx +0 -531
  321. package/src/components/admin/tabs/MonitoringEmbed.tsx +0 -11
  322. package/src/components/admin/tabs/OperationsTab.tsx +0 -646
  323. package/src/components/admin/tabs/OverviewTab.tsx +0 -1328
  324. package/src/components/admin/tabs/SecurityTab.tsx +0 -284
  325. package/src/components/community-section.tsx +0 -92
  326. package/src/components/icons/db-icons.tsx +0 -84
  327. package/src/components/libredb-logo.tsx +0 -61
  328. package/src/components/monitoring/MonitoringDashboard.tsx +0 -345
  329. package/src/components/monitoring/tabs/MetricChart.tsx +0 -82
  330. package/src/components/monitoring/tabs/OverviewTab.tsx +0 -263
  331. package/src/components/monitoring/tabs/PerformanceTab.tsx +0 -254
  332. package/src/components/monitoring/tabs/PoolTab.tsx +0 -174
  333. package/src/components/monitoring/tabs/QueriesTab.tsx +0 -287
  334. package/src/components/monitoring/tabs/SessionsTab.tsx +0 -316
  335. package/src/components/monitoring/tabs/StorageTab.tsx +0 -335
  336. package/src/components/monitoring/tabs/TablesTab.tsx +0 -300
  337. package/src/components/results-grid/ResultCard.tsx +0 -111
  338. package/src/components/results-grid/RowDetailSheet.tsx +0 -178
  339. package/src/components/results-grid/StatsBar.tsx +0 -201
  340. package/src/components/results-grid/index.ts +0 -1
  341. package/src/components/results-grid/utils.ts +0 -23
  342. package/src/components/schema-explorer/ColumnList.tsx +0 -53
  343. package/src/components/schema-explorer/SchemaExplorer.tsx +0 -182
  344. package/src/components/schema-explorer/TableItem.tsx +0 -210
  345. package/src/components/schema-explorer/index.ts +0 -1
  346. package/src/components/sidebar/ConnectionItem.tsx +0 -105
  347. package/src/components/sidebar/ConnectionsList.tsx +0 -62
  348. package/src/components/sidebar/Sidebar.tsx +0 -130
  349. package/src/components/sidebar/index.ts +0 -2
  350. package/src/components/studio/BottomPanel.tsx +0 -286
  351. package/src/components/studio/QueryToolbar.tsx +0 -180
  352. package/src/components/studio/StudioDesktopHeader.tsx +0 -114
  353. package/src/components/studio/StudioMobileHeader.tsx +0 -340
  354. package/src/components/studio/StudioTabBar.tsx +0 -82
  355. package/src/components/studio/index.ts +0 -5
  356. package/src/components/ui/accordion.tsx +0 -66
  357. package/src/components/ui/alert-dialog.tsx +0 -157
  358. package/src/components/ui/alert.tsx +0 -66
  359. package/src/components/ui/aspect-ratio.tsx +0 -11
  360. package/src/components/ui/avatar.tsx +0 -53
  361. package/src/components/ui/badge.tsx +0 -46
  362. package/src/components/ui/breadcrumb.tsx +0 -109
  363. package/src/components/ui/button-group.tsx +0 -83
  364. package/src/components/ui/button.tsx +0 -60
  365. package/src/components/ui/calendar.tsx +0 -216
  366. package/src/components/ui/card.tsx +0 -92
  367. package/src/components/ui/carousel.tsx +0 -241
  368. package/src/components/ui/chart.tsx +0 -357
  369. package/src/components/ui/checkbox.tsx +0 -32
  370. package/src/components/ui/collapsible.tsx +0 -33
  371. package/src/components/ui/command.tsx +0 -184
  372. package/src/components/ui/context-menu.tsx +0 -252
  373. package/src/components/ui/dialog.tsx +0 -143
  374. package/src/components/ui/drawer.tsx +0 -135
  375. package/src/components/ui/dropdown-menu.tsx +0 -257
  376. package/src/components/ui/empty.tsx +0 -104
  377. package/src/components/ui/field.tsx +0 -248
  378. package/src/components/ui/form.tsx +0 -167
  379. package/src/components/ui/hover-card.tsx +0 -44
  380. package/src/components/ui/input-group.tsx +0 -170
  381. package/src/components/ui/input-otp.tsx +0 -77
  382. package/src/components/ui/input.tsx +0 -21
  383. package/src/components/ui/item.tsx +0 -193
  384. package/src/components/ui/kbd.tsx +0 -28
  385. package/src/components/ui/label.tsx +0 -24
  386. package/src/components/ui/menubar.tsx +0 -276
  387. package/src/components/ui/navigation-menu.tsx +0 -168
  388. package/src/components/ui/pagination.tsx +0 -127
  389. package/src/components/ui/popover.tsx +0 -48
  390. package/src/components/ui/progress.tsx +0 -31
  391. package/src/components/ui/radio-group.tsx +0 -45
  392. package/src/components/ui/resizable.tsx +0 -56
  393. package/src/components/ui/scroll-area.tsx +0 -58
  394. package/src/components/ui/select.tsx +0 -187
  395. package/src/components/ui/separator.tsx +0 -28
  396. package/src/components/ui/sheet.tsx +0 -139
  397. package/src/components/ui/sidebar.tsx +0 -726
  398. package/src/components/ui/skeleton.tsx +0 -13
  399. package/src/components/ui/slider.tsx +0 -63
  400. package/src/components/ui/sonner.tsx +0 -40
  401. package/src/components/ui/spinner.tsx +0 -16
  402. package/src/components/ui/switch.tsx +0 -31
  403. package/src/components/ui/table.tsx +0 -116
  404. package/src/components/ui/tabs.tsx +0 -66
  405. package/src/components/ui/textarea.tsx +0 -18
  406. package/src/components/ui/toggle-group.tsx +0 -83
  407. package/src/components/ui/toggle.tsx +0 -47
  408. package/src/components/ui/tooltip.tsx +0 -61
  409. package/src/exports/components.ts +0 -15
  410. package/src/exports/index.ts +0 -4
  411. package/src/exports/providers.ts +0 -4
  412. package/src/exports/types.ts +0 -26
  413. package/src/hooks/use-ai-chat.ts +0 -182
  414. package/src/hooks/use-all-connections.ts +0 -66
  415. package/src/hooks/use-api-call.ts +0 -71
  416. package/src/hooks/use-auth.ts +0 -51
  417. package/src/hooks/use-connection-form.ts +0 -349
  418. package/src/hooks/use-connection-manager.ts +0 -169
  419. package/src/hooks/use-connection-payload.ts +0 -15
  420. package/src/hooks/use-inline-editing.ts +0 -109
  421. package/src/hooks/use-mobile.ts +0 -20
  422. package/src/hooks/use-monitoring-data.ts +0 -270
  423. package/src/hooks/use-provider-metadata.ts +0 -62
  424. package/src/hooks/use-query-execution.ts +0 -478
  425. package/src/hooks/use-storage-sync.ts +0 -259
  426. package/src/hooks/use-tab-manager.ts +0 -231
  427. package/src/hooks/use-toast.ts +0 -20
  428. package/src/hooks/use-transaction-control.ts +0 -64
  429. package/src/lib/api/error-codes.ts +0 -30
  430. package/src/lib/api/errors.ts +0 -236
  431. package/src/lib/api/with-error-handler.ts +0 -41
  432. package/src/lib/audit.ts +0 -105
  433. package/src/lib/auth.ts +0 -87
  434. package/src/lib/connection-string-parser.ts +0 -172
  435. package/src/lib/data-masking.ts +0 -385
  436. package/src/lib/db/base-provider.ts +0 -325
  437. package/src/lib/db/errors.ts +0 -317
  438. package/src/lib/db/factory.ts +0 -324
  439. package/src/lib/db/index.ts +0 -123
  440. package/src/lib/db/providers/document/index.ts +0 -6
  441. package/src/lib/db/providers/document/mongodb.ts +0 -992
  442. package/src/lib/db/providers/keyvalue/redis.ts +0 -554
  443. package/src/lib/db/providers/sql/index.ts +0 -11
  444. package/src/lib/db/providers/sql/sql-base.ts +0 -174
  445. package/src/lib/db/providers/sql/sqlite.ts +0 -721
  446. package/src/lib/db/types.ts +0 -437
  447. package/src/lib/db/utils/pool-manager.ts +0 -287
  448. package/src/lib/db/utils/query-limiter.ts +0 -239
  449. package/src/lib/db-ui-config.ts +0 -86
  450. package/src/lib/editor/mongodb-completions.ts +0 -172
  451. package/src/lib/editor/sql-completions.ts +0 -280
  452. package/src/lib/llm/base-provider.ts +0 -117
  453. package/src/lib/llm/factory.ts +0 -102
  454. package/src/lib/llm/index.ts +0 -90
  455. package/src/lib/llm/providers/custom.ts +0 -181
  456. package/src/lib/llm/providers/gemini.ts +0 -126
  457. package/src/lib/llm/providers/ollama.ts +0 -154
  458. package/src/lib/llm/providers/openai.ts +0 -146
  459. package/src/lib/llm/types.ts +0 -173
  460. package/src/lib/llm/utils/config.ts +0 -187
  461. package/src/lib/llm/utils/retry.ts +0 -119
  462. package/src/lib/llm/utils/streaming.ts +0 -202
  463. package/src/lib/logger.ts +0 -127
  464. package/src/lib/monitoring-thresholds.ts +0 -44
  465. package/src/lib/oidc.ts +0 -262
  466. package/src/lib/query-generators.ts +0 -61
  467. package/src/lib/schema-diff/diff-engine.ts +0 -273
  468. package/src/lib/schema-diff/migration-generator.ts +0 -208
  469. package/src/lib/schema-diff/types.ts +0 -55
  470. package/src/lib/seed/config-loader.ts +0 -79
  471. package/src/lib/seed/connection-filter.ts +0 -49
  472. package/src/lib/seed/credential-resolver.ts +0 -62
  473. package/src/lib/seed/index.ts +0 -40
  474. package/src/lib/seed/resolve-connection.ts +0 -57
  475. package/src/lib/seed/types.ts +0 -69
  476. package/src/lib/sql/alias-extractor.ts +0 -267
  477. package/src/lib/sql/index.ts +0 -8
  478. package/src/lib/sql/statement-splitter.ts +0 -167
  479. package/src/lib/sql/types.ts +0 -40
  480. package/src/lib/ssh/tunnel.ts +0 -142
  481. package/src/lib/storage/factory.ts +0 -84
  482. package/src/lib/storage/index.ts +0 -14
  483. package/src/lib/storage/local-storage.ts +0 -99
  484. package/src/lib/storage/providers/postgres.ts +0 -225
  485. package/src/lib/storage/providers/sqlite.ts +0 -153
  486. package/src/lib/storage/storage-facade.ts +0 -272
  487. package/src/lib/storage/types.ts +0 -75
  488. package/src/lib/time-series-buffer.ts +0 -58
  489. package/src/lib/types.ts +0 -173
  490. package/src/lib/utils.ts +0 -6
  491. package/src/proxy.ts +0 -104
  492. package/src/types/db-drivers.d.ts +0 -23
  493. package/src/types/html2canvas.d.ts +0 -9
  494. package/tests/api/admin/audit.test.ts +0 -178
  495. package/tests/api/admin/fleet-health.test.ts +0 -183
  496. package/tests/api/ai/autopilot.test.ts +0 -174
  497. package/tests/api/ai/chat.test.ts +0 -250
  498. package/tests/api/ai/describe-schema.test.ts +0 -266
  499. package/tests/api/ai/explain.test.ts +0 -199
  500. package/tests/api/ai/impact.test.ts +0 -168
  501. package/tests/api/ai/index-advisor.test.ts +0 -171
  502. package/tests/api/ai/nl2sql.test.ts +0 -202
  503. package/tests/api/ai/query-safety.test.ts +0 -196
  504. package/tests/api/auth/login.test.ts +0 -170
  505. package/tests/api/auth/logout.test.ts +0 -140
  506. package/tests/api/auth/me.test.ts +0 -73
  507. package/tests/api/auth/oidc-callback.test.ts +0 -215
  508. package/tests/api/auth/oidc-login.test.ts +0 -127
  509. package/tests/api/db/cancel.test.ts +0 -198
  510. package/tests/api/db/disconnect.test.ts +0 -124
  511. package/tests/api/db/health.test.ts +0 -222
  512. package/tests/api/db/maintenance.test.ts +0 -263
  513. package/tests/api/db/monitoring.test.ts +0 -221
  514. package/tests/api/db/multi-query.test.ts +0 -316
  515. package/tests/api/db/pool-stats.test.ts +0 -135
  516. package/tests/api/db/profile.test.ts +0 -330
  517. package/tests/api/db/provider-meta.test.ts +0 -193
  518. package/tests/api/db/query.test.ts +0 -314
  519. package/tests/api/db/schema-snapshot.test.ts +0 -170
  520. package/tests/api/db/schema.test.ts +0 -191
  521. package/tests/api/db/test-connection.test.ts +0 -185
  522. package/tests/api/db/transaction.test.ts +0 -314
  523. package/tests/api/proxy.test.ts +0 -191
  524. package/tests/api/seed/managed-route.test.ts +0 -113
  525. package/tests/api/storage/config.test.ts +0 -42
  526. package/tests/api/storage/storage-routes.test.ts +0 -309
  527. package/tests/components/AIAutopilotPanel.test.tsx +0 -756
  528. package/tests/components/AdminPage.test.tsx +0 -33
  529. package/tests/components/CodeGenerator.test.tsx +0 -182
  530. package/tests/components/CommandPalette.test.tsx +0 -428
  531. package/tests/components/CommunitySection.test.tsx +0 -91
  532. package/tests/components/ConnectionModal.mobile.test.tsx +0 -284
  533. package/tests/components/ConnectionModal.test.tsx +0 -570
  534. package/tests/components/CreateTableModal.test.tsx +0 -383
  535. package/tests/components/DataCharts.test.tsx +0 -739
  536. package/tests/components/DataImportModal.test.tsx +0 -751
  537. package/tests/components/DataProfiler.test.tsx +0 -589
  538. package/tests/components/DatabaseDocs.test.tsx +0 -353
  539. package/tests/components/LoginPage.test.tsx +0 -163
  540. package/tests/components/LoginPageOIDC.test.tsx +0 -92
  541. package/tests/components/MaskingSettings.test.tsx +0 -498
  542. package/tests/components/MobileNav.test.tsx +0 -30
  543. package/tests/components/MonitoringPage.test.tsx +0 -32
  544. package/tests/components/NL2SQLPanel.test.tsx +0 -621
  545. package/tests/components/Page.test.tsx +0 -33
  546. package/tests/components/PivotTable.test.tsx +0 -350
  547. package/tests/components/QueryEditor.test.tsx +0 -1730
  548. package/tests/components/QueryHistory.test.tsx +0 -572
  549. package/tests/components/QuerySafetyDialog.test.tsx +0 -586
  550. package/tests/components/ResultsGrid.test.tsx +0 -804
  551. package/tests/components/RootLayout.test.tsx +0 -83
  552. package/tests/components/SaveQueryModal.test.tsx +0 -25
  553. package/tests/components/SavedQueries.test.tsx +0 -43
  554. package/tests/components/SchemaDiagram.test.tsx +0 -1034
  555. package/tests/components/SchemaDiff.test.tsx +0 -906
  556. package/tests/components/SnapshotTimeline.test.tsx +0 -174
  557. package/tests/components/Studio.test.tsx +0 -1030
  558. package/tests/components/TestDataGenerator.test.tsx +0 -291
  559. package/tests/components/VisualExplain.test.tsx +0 -704
  560. package/tests/components/admin/AdminDashboard.test.tsx +0 -205
  561. package/tests/components/admin/AuditTab.test.tsx +0 -220
  562. package/tests/components/admin/MonitoringEmbed.test.tsx +0 -58
  563. package/tests/components/admin/OperationsTab.test.tsx +0 -975
  564. package/tests/components/admin/OverviewTab.test.tsx +0 -254
  565. package/tests/components/admin/SecurityTab.test.tsx +0 -467
  566. package/tests/components/monitoring/MetricChart.test.tsx +0 -111
  567. package/tests/components/monitoring/MonitoringDashboard.test.tsx +0 -259
  568. package/tests/components/monitoring/OverviewTab.test.tsx +0 -78
  569. package/tests/components/monitoring/PerformanceTab.test.tsx +0 -87
  570. package/tests/components/monitoring/PoolTab.test.tsx +0 -42
  571. package/tests/components/monitoring/QueriesTab.test.tsx +0 -80
  572. package/tests/components/monitoring/SessionsTab.test.tsx +0 -154
  573. package/tests/components/monitoring/StorageTab.test.tsx +0 -127
  574. package/tests/components/monitoring/TablesTab.test.tsx +0 -153
  575. package/tests/components/results-grid/ResultCard.test.tsx +0 -105
  576. package/tests/components/results-grid/RowDetailSheet.test.tsx +0 -308
  577. package/tests/components/results-grid/StatsBar.test.tsx +0 -162
  578. package/tests/components/schema-explorer/ColumnList.test.tsx +0 -151
  579. package/tests/components/schema-explorer/SchemaExplorer.test.tsx +0 -461
  580. package/tests/components/schema-explorer/TableItem.test.tsx +0 -415
  581. package/tests/components/sidebar/ConnectionItem.test.tsx +0 -201
  582. package/tests/components/sidebar/ConnectionsList.test.tsx +0 -176
  583. package/tests/components/sidebar/Sidebar.test.tsx +0 -187
  584. package/tests/components/studio/BottomPanel.test.tsx +0 -383
  585. package/tests/components/studio/QueryToolbar.test.tsx +0 -321
  586. package/tests/components/studio/StudioDesktopHeader.test.tsx +0 -377
  587. package/tests/components/studio/StudioMobileHeader.test.tsx +0 -198
  588. package/tests/components/studio/StudioTabBar.test.tsx +0 -331
  589. package/tests/fixtures/connections.ts +0 -96
  590. package/tests/fixtures/masking-configs.ts +0 -86
  591. package/tests/fixtures/query-results.ts +0 -71
  592. package/tests/fixtures/schemas.ts +0 -64
  593. package/tests/fixtures/seed-connections/invalid-config.yaml +0 -7
  594. package/tests/fixtures/seed-connections/minimal-config.yaml +0 -8
  595. package/tests/fixtures/seed-connections/mixed-credentials.yaml +0 -23
  596. package/tests/fixtures/seed-connections/multi-role-config.yaml +0 -30
  597. package/tests/fixtures/seed-connections/valid-config.json +0 -15
  598. package/tests/fixtures/seed-connections/valid-config.yaml +0 -51
  599. package/tests/helpers/mock-fetch.ts +0 -59
  600. package/tests/helpers/mock-monaco.ts +0 -112
  601. package/tests/helpers/mock-navigation.ts +0 -28
  602. package/tests/helpers/mock-next.ts +0 -80
  603. package/tests/helpers/mock-provider.ts +0 -133
  604. package/tests/helpers/mock-sonner.ts +0 -29
  605. package/tests/helpers/render-with-providers.tsx +0 -19
  606. package/tests/hooks/use-ai-chat.test.ts +0 -600
  607. package/tests/hooks/use-auth.test.ts +0 -371
  608. package/tests/hooks/use-connection-form.test.ts +0 -743
  609. package/tests/hooks/use-connection-manager.test.ts +0 -466
  610. package/tests/hooks/use-inline-editing.test.ts +0 -321
  611. package/tests/hooks/use-mobile.test.ts +0 -177
  612. package/tests/hooks/use-monitoring-data.test.ts +0 -819
  613. package/tests/hooks/use-provider-metadata.test.ts +0 -228
  614. package/tests/hooks/use-query-execution.test.ts +0 -1212
  615. package/tests/hooks/use-tab-manager.test.ts +0 -756
  616. package/tests/hooks/use-toast.test.ts +0 -74
  617. package/tests/hooks/use-transaction-control.test.ts +0 -211
  618. package/tests/integration/db/mongodb-provider.test.ts +0 -698
  619. package/tests/integration/db/mssql-provider.test.ts +0 -840
  620. package/tests/integration/db/mysql-provider.test.ts +0 -872
  621. package/tests/integration/db/oracle-provider.test.ts +0 -843
  622. package/tests/integration/db/postgres-provider.test.ts +0 -1382
  623. package/tests/integration/db/redis-provider.test.ts +0 -526
  624. package/tests/integration/db/sqlite-provider.test.ts +0 -480
  625. package/tests/integration/seed/seed-pipeline.test.ts +0 -102
  626. package/tests/isolated/factory-singleton.test.ts +0 -150
  627. package/tests/isolated/use-storage-sync.test.ts +0 -389
  628. package/tests/run-components.sh +0 -196
  629. package/tests/setup-dom.ts +0 -58
  630. package/tests/setup.ts +0 -40
  631. package/tests/unit/api-errors.test.ts +0 -210
  632. package/tests/unit/code-generator-functions.test.ts +0 -271
  633. package/tests/unit/components/column-list.test.tsx +0 -190
  634. package/tests/unit/components/data-import-modal.test.tsx +0 -441
  635. package/tests/unit/components/studio-mobile-header.test.tsx +0 -327
  636. package/tests/unit/data-charts-functions.test.ts +0 -496
  637. package/tests/unit/data-import-functions.test.ts +0 -320
  638. package/tests/unit/data-import-utils.test.ts +0 -125
  639. package/tests/unit/db/base-provider.test.ts +0 -517
  640. package/tests/unit/db/errors.test.ts +0 -403
  641. package/tests/unit/db/factory.test.ts +0 -436
  642. package/tests/unit/db/pool-manager.test.ts +0 -440
  643. package/tests/unit/db/query-limiter.test.ts +0 -387
  644. package/tests/unit/db/sql-base.test.ts +0 -438
  645. package/tests/unit/lib/api/error-codes.test.ts +0 -39
  646. package/tests/unit/lib/audit.test.ts +0 -326
  647. package/tests/unit/lib/auth.test.ts +0 -146
  648. package/tests/unit/lib/connection-string-parser.test.ts +0 -424
  649. package/tests/unit/lib/data-masking.test.ts +0 -583
  650. package/tests/unit/lib/db-icons.test.tsx +0 -41
  651. package/tests/unit/lib/monitoring-thresholds.test.ts +0 -133
  652. package/tests/unit/lib/oidc.test.ts +0 -509
  653. package/tests/unit/lib/query-generators.test.ts +0 -127
  654. package/tests/unit/lib/storage/factory.test.ts +0 -71
  655. package/tests/unit/lib/storage/local-storage.test.ts +0 -114
  656. package/tests/unit/lib/storage/providers/postgres.test.ts +0 -312
  657. package/tests/unit/lib/storage/providers/sqlite.test.ts +0 -232
  658. package/tests/unit/lib/storage/storage-facade-extended.test.ts +0 -331
  659. package/tests/unit/lib/storage/storage-facade.test.ts +0 -184
  660. package/tests/unit/lib/storage.test.ts +0 -317
  661. package/tests/unit/lib/time-series-buffer.test.ts +0 -212
  662. package/tests/unit/lib/utils.test.ts +0 -24
  663. package/tests/unit/llm/base-provider.test.ts +0 -238
  664. package/tests/unit/llm/config.test.ts +0 -262
  665. package/tests/unit/llm/custom-provider.test.ts +0 -281
  666. package/tests/unit/llm/gemini-provider.test.ts +0 -248
  667. package/tests/unit/llm/llm-factory.test.ts +0 -155
  668. package/tests/unit/llm/ollama-provider.test.ts +0 -288
  669. package/tests/unit/llm/openai-provider.test.ts +0 -324
  670. package/tests/unit/llm/retry.test.ts +0 -180
  671. package/tests/unit/llm/streaming.test.ts +0 -355
  672. package/tests/unit/logger.test.ts +0 -198
  673. package/tests/unit/mongodb-completions.test.ts +0 -516
  674. package/tests/unit/pivot-table-functions.test.ts +0 -76
  675. package/tests/unit/query-cancelled-error.test.ts +0 -81
  676. package/tests/unit/schema-diff/diff-engine.test.ts +0 -367
  677. package/tests/unit/schema-diff/migration-generator.test.ts +0 -513
  678. package/tests/unit/seed/config-loader.test.ts +0 -73
  679. package/tests/unit/seed/connection-filter.test.ts +0 -91
  680. package/tests/unit/seed/credential-resolver.test.ts +0 -85
  681. package/tests/unit/seed/index.test.ts +0 -72
  682. package/tests/unit/seed/resolve-connection.test.ts +0 -74
  683. package/tests/unit/seed/types.test.ts +0 -129
  684. package/tests/unit/sql/alias-extractor.test.ts +0 -444
  685. package/tests/unit/sql/statement-splitter.test.ts +0 -348
  686. package/tests/unit/sql-completions.test.ts +0 -463
  687. package/tests/unit/ssh-tunnel.test.ts +0 -465
  688. package/tsconfig.json +0 -42
@@ -1,1034 +0,0 @@
1
- import '../setup-dom';
2
- import '../helpers/mock-sonner';
3
- import '../helpers/mock-navigation';
4
-
5
- import { mock } from 'bun:test';
6
- import { setupFramerMotionMock } from '../helpers/mock-monaco';
7
-
8
- // Enhanced XYFlow mock that renders nodes via nodeTypes
9
- mock.module('@xyflow/react', () => {
10
- // eslint-disable-next-line @typescript-eslint/no-require-imports
11
- const React = require('react');
12
- return {
13
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
- ReactFlow: ({ children, nodes = [], nodeTypes = {}, onNodeClick, onPaneClick }: Record<string, any>) => {
15
- const renderedNodes = nodes.map((node: { id: string; type: string; data: Record<string, unknown> }) => {
16
- const NodeComp = nodeTypes[node.type];
17
- if (!NodeComp) return null;
18
- return React.createElement('div', {
19
- key: node.id,
20
- 'data-testid': `node-${node.id}`,
21
- 'data-node-id': node.id,
22
- onClick: (e: React.MouseEvent) => { e.stopPropagation(); onNodeClick?.(e, node); },
23
- }, React.createElement(NodeComp, { id: node.id, data: node.data, type: node.type }));
24
- });
25
- // Wrap nodes in a keyed container to avoid reconciliation issues
26
- // when the number of nodes changes (e.g. during search filtering)
27
- return React.createElement('div', {
28
- 'data-testid': 'mock-react-flow',
29
- className: 'react-flow',
30
- onClick: (e: React.MouseEvent) => { if (e.target === e.currentTarget) onPaneClick?.(); },
31
- },
32
- React.createElement('div', { key: '__nodes__', 'data-testid': 'nodes-container' }, renderedNodes),
33
- React.createElement('svg', { key: '__svg__' }),
34
- React.createElement(React.Fragment, { key: '__children__' }, children),
35
- );
36
- },
37
- ReactFlowProvider: ({ children }: { children: unknown }) => children,
38
- MiniMap: () => React.createElement('div', { 'data-testid': 'mock-minimap' }),
39
- Controls: () => null,
40
- Background: () => null,
41
- Handle: () => null,
42
- useNodesState: () => [[], mock(() => {}), mock(() => {})],
43
- useEdgesState: () => [[], mock(() => {}), mock(() => {})],
44
- useReactFlow: () => ({ fitView: mock(() => {}), getNodes: mock(() => []), getEdges: mock(() => []) }),
45
- Position: { Top: 'top', Bottom: 'bottom', Left: 'left', Right: 'right' },
46
- MarkerType: { ArrowClosed: 'arrowclosed' },
47
- Panel: ({ children, position }: { children: unknown; position?: string }) =>
48
- React.createElement('div', { 'data-testid': `mock-panel-${position || 'default'}` }, children),
49
- };
50
- });
51
-
52
- setupFramerMotionMock();
53
-
54
- // Mock elkjs
55
- mock.module('elkjs/lib/elk.bundled.js', () => ({
56
- default: class MockELK {
57
- layout(graph: unknown) {
58
- return Promise.resolve(graph);
59
- }
60
- },
61
- }));
62
-
63
- // Track html2canvas calls
64
- const mockHtml2canvas = mock(() => Promise.resolve({
65
- toDataURL: () => 'data:image/png;base64,mock',
66
- }));
67
-
68
- mock.module('html2canvas', () => ({
69
- default: mockHtml2canvas,
70
- }));
71
-
72
- import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
73
- import { render, fireEvent, within, cleanup, act } from '@testing-library/react';
74
- import React from 'react';
75
-
76
- import { SchemaDiagram } from '@/components/SchemaDiagram';
77
- import { mockSchema, emptySchema } from '../fixtures/schemas';
78
- import type { TableSchema } from '@/lib/types';
79
-
80
- // =============================================================================
81
- // Test Data
82
- // =============================================================================
83
-
84
- // Schema with NO foreign keys at all (triggers heuristic fallback)
85
- const schemaNoFK: TableSchema[] = [
86
- {
87
- name: 'users',
88
- columns: [
89
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
90
- { name: 'name', type: 'varchar(255)', nullable: false, isPrimary: false },
91
- ],
92
- indexes: [],
93
- foreignKeys: [],
94
- rowCount: 100,
95
- },
96
- {
97
- name: 'posts',
98
- columns: [
99
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
100
- { name: 'title', type: 'text', nullable: false, isPrimary: false },
101
- ],
102
- indexes: [],
103
- foreignKeys: [],
104
- rowCount: 50,
105
- },
106
- ];
107
-
108
- // Schema with heuristic _id column (no FK data, but column ends with _id)
109
- const schemaHeuristic: TableSchema[] = [
110
- {
111
- name: 'users',
112
- columns: [
113
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
114
- { name: 'email', type: 'varchar', nullable: true, isPrimary: false },
115
- ],
116
- indexes: [],
117
- foreignKeys: [],
118
- rowCount: 10,
119
- },
120
- {
121
- name: 'comments',
122
- columns: [
123
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
124
- { name: 'user_id', type: 'integer', nullable: false, isPrimary: false },
125
- { name: 'body', type: 'text', nullable: false, isPrimary: false },
126
- ],
127
- indexes: [],
128
- foreignKeys: [],
129
- rowCount: 200,
130
- },
131
- ];
132
-
133
- // Schema with heuristic _id column matching singular table name (no plural 's')
134
- const schemaHeuristicSingular: TableSchema[] = [
135
- {
136
- name: 'author',
137
- columns: [
138
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
139
- { name: 'name', type: 'varchar(255)', nullable: false, isPrimary: false },
140
- ],
141
- indexes: [],
142
- foreignKeys: [],
143
- rowCount: 10,
144
- },
145
- {
146
- name: 'books',
147
- columns: [
148
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
149
- { name: 'author_id', type: 'integer', nullable: false, isPrimary: false },
150
- { name: 'title', type: 'text', nullable: false, isPrimary: false },
151
- ],
152
- indexes: [],
153
- foreignKeys: [],
154
- rowCount: 50,
155
- },
156
- ];
157
-
158
- // Schema with foreignKeys field omitted (tests `|| []` guards)
159
- const schemaUndefinedFK: TableSchema[] = [
160
- {
161
- name: 'items',
162
- columns: [
163
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
164
- { name: 'label', type: 'text', nullable: true, isPrimary: false },
165
- ],
166
- indexes: [],
167
- rowCount: 20,
168
- } as TableSchema,
169
- ];
170
-
171
- // Multi-FK schema for highlighting tests
172
- const schemaMultiFK: TableSchema[] = [
173
- {
174
- name: 'users',
175
- columns: [
176
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
177
- { name: 'name', type: 'varchar(255)', nullable: false, isPrimary: false },
178
- ],
179
- indexes: [],
180
- foreignKeys: [],
181
- rowCount: 100,
182
- },
183
- {
184
- name: 'orders',
185
- columns: [
186
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
187
- { name: 'user_id', type: 'integer', nullable: false, isPrimary: false },
188
- { name: 'total', type: 'numeric(10,2)', nullable: false, isPrimary: false },
189
- ],
190
- indexes: [],
191
- foreignKeys: [
192
- { columnName: 'user_id', referencedTable: 'users', referencedColumn: 'id' },
193
- ],
194
- rowCount: 500,
195
- },
196
- {
197
- name: 'items',
198
- columns: [
199
- { name: 'id', type: 'integer', nullable: false, isPrimary: true },
200
- { name: 'order_id', type: 'integer', nullable: false, isPrimary: false },
201
- { name: 'product', type: 'varchar(255)', nullable: false, isPrimary: false },
202
- ],
203
- indexes: [],
204
- foreignKeys: [
205
- { columnName: 'order_id', referencedTable: 'orders', referencedColumn: 'id' },
206
- ],
207
- rowCount: 1000,
208
- },
209
- ];
210
-
211
- // Single table schema
212
- const singleTableSchema: TableSchema[] = [
213
- {
214
- name: 'settings',
215
- columns: [
216
- { name: 'key', type: 'text', nullable: false, isPrimary: true },
217
- { name: 'value', type: 'text', nullable: true, isPrimary: false },
218
- ],
219
- indexes: [],
220
- foreignKeys: [],
221
- rowCount: 5,
222
- },
223
- ];
224
-
225
- // =============================================================================
226
- // Helpers
227
- // =============================================================================
228
-
229
- function createDefaultProps(overrides: Partial<Parameters<typeof SchemaDiagram>[0]> = {}) {
230
- return {
231
- schema: mockSchema,
232
- onClose: mock(() => {}),
233
- ...overrides,
234
- };
235
- }
236
-
237
- // =============================================================================
238
- // SchemaDiagram Tests
239
- // =============================================================================
240
-
241
- describe('SchemaDiagram', () => {
242
- afterEach(() => {
243
- cleanup();
244
- });
245
-
246
- beforeEach(() => {
247
- mockHtml2canvas.mockClear();
248
- });
249
-
250
- // ── Rendering ───────────────────────────────────────────────────────────
251
-
252
- test('renders ReactFlow container', () => {
253
- const props = createDefaultProps();
254
- const { container } = render(<SchemaDiagram {...props} />);
255
-
256
- expect(container.querySelector('[data-testid="mock-react-flow"]')).not.toBeNull();
257
- });
258
-
259
- test('shows top-right panel with buttons', () => {
260
- const props = createDefaultProps();
261
- const { container } = render(<SchemaDiagram {...props} />);
262
-
263
- expect(container.querySelector('[data-testid="mock-panel-top-right"]')).not.toBeNull();
264
- });
265
-
266
- test('shows top-left info panel', () => {
267
- const props = createDefaultProps();
268
- const { container } = render(<SchemaDiagram {...props} />);
269
-
270
- expect(container.querySelector('[data-testid="mock-panel-top-left"]')).not.toBeNull();
271
- });
272
-
273
- test('renders ERD Visualizer heading', () => {
274
- const props = createDefaultProps();
275
- const { container } = render(<SchemaDiagram {...props} />);
276
- const view = within(container);
277
-
278
- expect(view.queryByText('ERD Visualizer')).not.toBeNull();
279
- });
280
-
281
- // ── Close button ────────────────────────────────────────────────────────
282
-
283
- test('onClose fires when close button clicked', () => {
284
- const onClose = mock(() => {});
285
- const props = createDefaultProps({ onClose });
286
- const { container } = render(<SchemaDiagram {...props} />);
287
-
288
- const closeButton = Array.from(container.querySelectorAll('button')).find(btn =>
289
- btn.className.includes('rounded-full')
290
- );
291
- expect(closeButton).not.toBeNull();
292
-
293
- fireEvent.click(closeButton!);
294
- expect(onClose).toHaveBeenCalledTimes(1);
295
- });
296
-
297
- // ── Table count and relationships ───────────────────────────────────────
298
-
299
- test('shows table info from schema in ERD panel', () => {
300
- const props = createDefaultProps();
301
- const { container } = render(<SchemaDiagram {...props} />);
302
- const view = within(container);
303
-
304
- expect(view.queryByText('3 tables')).not.toBeNull();
305
- });
306
-
307
- test('shows relationship count', () => {
308
- const props = createDefaultProps();
309
- const { container } = render(<SchemaDiagram {...props} />);
310
- const view = within(container);
311
-
312
- // mockSchema has orders → users FK, so 1 relationship
313
- expect(view.queryByText('1 relationships')).not.toBeNull();
314
- });
315
-
316
- test('shows 0 relationships for schema without FKs', () => {
317
- const props = createDefaultProps({ schema: schemaNoFK });
318
- const { container } = render(<SchemaDiagram {...props} />);
319
- const view = within(container);
320
-
321
- expect(view.queryByText('0 relationships')).not.toBeNull();
322
- });
323
-
324
- test('shows heuristic relationships count for _id columns', () => {
325
- const props = createDefaultProps({ schema: schemaHeuristic });
326
- const { container } = render(<SchemaDiagram {...props} />);
327
- const view = within(container);
328
-
329
- // comments.user_id → users heuristic edge
330
- expect(view.queryByText('1 relationships')).not.toBeNull();
331
- });
332
-
333
- test('shows single table count', () => {
334
- const props = createDefaultProps({ schema: singleTableSchema });
335
- const { container } = render(<SchemaDiagram {...props} />);
336
- const view = within(container);
337
-
338
- expect(view.queryByText('1 tables')).not.toBeNull();
339
- });
340
-
341
- // ── Export buttons ──────────────────────────────────────────────────────
342
-
343
- test('export buttons present (PNG, SVG)', () => {
344
- const props = createDefaultProps();
345
- const { container } = render(<SchemaDiagram {...props} />);
346
- const view = within(container);
347
-
348
- expect(view.queryByText('PNG')).not.toBeNull();
349
- expect(view.queryByText('SVG')).not.toBeNull();
350
- });
351
-
352
- test('PNG export button click does not crash', () => {
353
- const props = createDefaultProps();
354
- const { container } = render(<SchemaDiagram {...props} />);
355
- const view = within(container);
356
-
357
- const pngButton = view.getByText('PNG').closest('button')!;
358
- fireEvent.click(pngButton);
359
- // Should not throw
360
- });
361
-
362
- test('SVG export button click does not crash', () => {
363
- const props = createDefaultProps();
364
- const { container } = render(<SchemaDiagram {...props} />);
365
- const view = within(container);
366
-
367
- const svgButton = view.getByText('SVG').closest('button')!;
368
- fireEvent.click(svgButton);
369
- // Should not throw
370
- });
371
-
372
- // ── Search input ────────────────────────────────────────────────────────
373
-
374
- test('search input present with placeholder', () => {
375
- const props = createDefaultProps();
376
- const { container } = render(<SchemaDiagram {...props} />);
377
- const view = within(container);
378
-
379
- expect(view.queryByPlaceholderText('Filter tables...')).not.toBeNull();
380
- });
381
-
382
- test('search filters tables and updates count', () => {
383
- const props = createDefaultProps();
384
- const { container } = render(<SchemaDiagram {...props} />);
385
- const view = within(container);
386
-
387
- // Initially 3 tables
388
- expect(view.queryByText('3 tables')).not.toBeNull();
389
-
390
- const searchInput = view.getByPlaceholderText('Filter tables...');
391
- fireEvent.change(searchInput, { target: { value: 'users' } });
392
-
393
- // After filtering, only 1 table matches
394
- expect(view.queryByText('1 tables')).not.toBeNull();
395
- expect(view.queryByText('3 tables')).toBeNull();
396
- });
397
-
398
- test('search is case-insensitive', () => {
399
- const props = createDefaultProps();
400
- const { container } = render(<SchemaDiagram {...props} />);
401
- const view = within(container);
402
-
403
- const searchInput = view.getByPlaceholderText('Filter tables...');
404
- fireEvent.change(searchInput, { target: { value: 'ORDERS' } });
405
-
406
- expect(view.queryByText('1 tables')).not.toBeNull();
407
- });
408
-
409
- test('search with no matches shows 0 tables', () => {
410
- const props = createDefaultProps();
411
- const { container } = render(<SchemaDiagram {...props} />);
412
- const view = within(container);
413
-
414
- const searchInput = view.getByPlaceholderText('Filter tables...');
415
- fireEvent.change(searchInput, { target: { value: 'nonexistent' } });
416
-
417
- expect(view.queryByText('0 tables')).not.toBeNull();
418
- });
419
-
420
- test('clearing search restores all tables', () => {
421
- const props = createDefaultProps();
422
- const { container } = render(<SchemaDiagram {...props} />);
423
- const view = within(container);
424
-
425
- const searchInput = view.getByPlaceholderText('Filter tables...');
426
-
427
- // Type to filter
428
- fireEvent.change(searchInput, { target: { value: 'users' } });
429
- expect(view.queryByText('1 tables')).not.toBeNull();
430
-
431
- // Clear the search
432
- fireEvent.change(searchInput, { target: { value: '' } });
433
- expect(view.queryByText('3 tables')).not.toBeNull();
434
- });
435
-
436
- // ── Compact mode toggle ─────────────────────────────────────────────────
437
-
438
- test('compact mode toggle present showing "Compact" initially', () => {
439
- const props = createDefaultProps();
440
- const { container } = render(<SchemaDiagram {...props} />);
441
- const view = within(container);
442
-
443
- expect(view.queryByText('Compact')).not.toBeNull();
444
- expect(view.queryByText('Detail')).toBeNull();
445
- });
446
-
447
- test('clicking Compact toggles to Detail', () => {
448
- const props = createDefaultProps();
449
- const { container } = render(<SchemaDiagram {...props} />);
450
- const view = within(container);
451
-
452
- const compactButton = view.getByText('Compact').closest('button')!;
453
- fireEvent.click(compactButton);
454
-
455
- expect(view.queryByText('Detail')).not.toBeNull();
456
- expect(view.queryByText('Compact')).toBeNull();
457
- });
458
-
459
- test('clicking Detail toggles back to Compact', () => {
460
- const props = createDefaultProps();
461
- const { container } = render(<SchemaDiagram {...props} />);
462
- const view = within(container);
463
-
464
- // Toggle to compact
465
- const compactButton = view.getByText('Compact').closest('button')!;
466
- fireEvent.click(compactButton);
467
- expect(view.queryByText('Detail')).not.toBeNull();
468
-
469
- // Toggle back to detail
470
- const detailButton = view.getByText('Detail').closest('button')!;
471
- fireEvent.click(detailButton);
472
- expect(view.queryByText('Compact')).not.toBeNull();
473
- });
474
-
475
- test('compact button has blue text class when compact mode is active', () => {
476
- const props = createDefaultProps();
477
- const { container } = render(<SchemaDiagram {...props} />);
478
- const view = within(container);
479
-
480
- const compactButton = view.getByText('Compact').closest('button')!;
481
- expect(compactButton.className).not.toContain('text-blue-400');
482
-
483
- fireEvent.click(compactButton);
484
- const detailButton = view.getByText('Detail').closest('button')!;
485
- expect(detailButton.className).toContain('text-blue-400');
486
- });
487
-
488
- // ── No FK warning ───────────────────────────────────────────────────────
489
-
490
- test('shows no-FK warning when schema has no foreign keys', () => {
491
- const props = createDefaultProps({ schema: schemaNoFK });
492
- const { container } = render(<SchemaDiagram {...props} />);
493
- const view = within(container);
494
-
495
- expect(view.queryByText(/No FK data available/)).not.toBeNull();
496
- expect(view.queryByText(/heuristic relationships/)).not.toBeNull();
497
- });
498
-
499
- test('does not show no-FK warning when schema has foreign keys', () => {
500
- const props = createDefaultProps(); // mockSchema has FK on orders
501
- const { container } = render(<SchemaDiagram {...props} />);
502
- const view = within(container);
503
-
504
- expect(view.queryByText(/No FK data available/)).toBeNull();
505
- });
506
-
507
- // ── Selected node info ──────────────────────────────────────────────────
508
-
509
- test('does not show selected node info by default', () => {
510
- const props = createDefaultProps();
511
- const { container } = render(<SchemaDiagram {...props} />);
512
- const view = within(container);
513
-
514
- expect(view.queryByText('Selected:')).toBeNull();
515
- expect(view.queryByText('clear')).toBeNull();
516
- });
517
-
518
- // ── Empty schema / loading state ────────────────────────────────────────
519
-
520
- test('empty schema shows loading/generating state', () => {
521
- const props = createDefaultProps({ schema: emptySchema });
522
- const { container } = render(<SchemaDiagram {...props} />);
523
- const view = within(container);
524
-
525
- expect(view.queryByText('Generating ERD Diagram...')).not.toBeNull();
526
- });
527
-
528
- test('empty schema does not render ReactFlow', () => {
529
- const props = createDefaultProps({ schema: emptySchema });
530
- const { container } = render(<SchemaDiagram {...props} />);
531
-
532
- expect(container.querySelector('[data-testid="mock-react-flow"]')).toBeNull();
533
- });
534
-
535
- test('empty schema does not render panels', () => {
536
- const props = createDefaultProps({ schema: emptySchema });
537
- const { container } = render(<SchemaDiagram {...props} />);
538
- const view = within(container);
539
-
540
- expect(view.queryByText('ERD Visualizer')).toBeNull();
541
- expect(view.queryByText('PNG')).toBeNull();
542
- expect(view.queryByText('Compact')).toBeNull();
543
- expect(view.queryByPlaceholderText('Filter tables...')).toBeNull();
544
- });
545
-
546
- // ── Search affects edge count ───────────────────────────────────────────
547
-
548
- test('filtering to table with FK shows its relationships', () => {
549
- const props = createDefaultProps();
550
- const { container } = render(<SchemaDiagram {...props} />);
551
- const view = within(container);
552
-
553
- // Search for "orders" — has FK to users, but users is filtered out
554
- const searchInput = view.getByPlaceholderText('Filter tables...');
555
- fireEvent.change(searchInput, { target: { value: 'orders' } });
556
-
557
- // Only orders table visible, users is filtered out → FK edge excluded (target not in set)
558
- expect(view.queryByText('1 tables')).not.toBeNull();
559
- expect(view.queryByText('0 relationships')).not.toBeNull();
560
- });
561
-
562
- // ── Heuristic edge detection ────────────────────────────────────────────
563
-
564
- test('heuristic edges are created for _id columns when no FK data', () => {
565
- const props = createDefaultProps({ schema: schemaHeuristic });
566
- const { container } = render(<SchemaDiagram {...props} />);
567
- const view = within(container);
568
-
569
- // comments has user_id → should heuristically link to users
570
- expect(view.queryByText('1 relationships')).not.toBeNull();
571
- });
572
-
573
- test('heuristic edges not created when real FK data exists', () => {
574
- // mockSchema has real FK on orders.user_id → users.id
575
- // so heuristic fallback should NOT run
576
- const props = createDefaultProps();
577
- const { container } = render(<SchemaDiagram {...props} />);
578
- const view = within(container);
579
-
580
- // Only 1 real FK edge, no extra heuristic
581
- expect(view.queryByText('1 relationships')).not.toBeNull();
582
- });
583
-
584
- // ── Multiple re-renders don't crash ─────────────────────────────────────
585
-
586
- test('re-rendering with different schema does not crash', () => {
587
- const onClose = mock(() => {});
588
- const { container, rerender } = render(
589
- <SchemaDiagram schema={mockSchema} onClose={onClose} />
590
- );
591
- const view = within(container);
592
- expect(view.queryByText('3 tables')).not.toBeNull();
593
-
594
- rerender(<SchemaDiagram schema={singleTableSchema} onClose={onClose} />);
595
- expect(view.queryByText('1 tables')).not.toBeNull();
596
- });
597
-
598
- // ── Panel buttons ─────────────────────────────────────────────────────
599
-
600
- test('top-right panel has PNG, SVG, Compact, and close buttons', () => {
601
- const props = createDefaultProps();
602
- const { container } = render(<SchemaDiagram {...props} />);
603
- const view = within(container);
604
- expect(view.queryByText('PNG')).not.toBeNull();
605
- expect(view.queryByText('SVG')).not.toBeNull();
606
- expect(view.queryByText('Compact')).not.toBeNull();
607
- // Close button (X icon)
608
- const closeBtn = Array.from(container.querySelectorAll('button')).find(btn =>
609
- btn.className.includes('rounded-full')
610
- );
611
- expect(closeBtn).not.toBeNull();
612
- });
613
-
614
- // ── MiniMap rendered ─────────────────────────────────────────────────
615
-
616
- test('MiniMap component renders', () => {
617
- const props = createDefaultProps();
618
- const { container } = render(<SchemaDiagram {...props} />);
619
- expect(container.querySelector('[data-testid="mock-minimap"]')).not.toBeNull();
620
- });
621
-
622
- // ── Schema with many tables ─────────────────────────────────────────
623
-
624
- test('schema with many tables renders correct count', () => {
625
- const manyTables: TableSchema[] = Array.from({ length: 10 }, (_, i) => ({
626
- name: `table_${i}`,
627
- columns: [{ name: 'id', type: 'integer', nullable: false, isPrimary: true }],
628
- indexes: [],
629
- foreignKeys: [],
630
- rowCount: i * 10,
631
- }));
632
- const props = createDefaultProps({ schema: manyTables });
633
- const { container } = render(<SchemaDiagram {...props} />);
634
- const view = within(container);
635
- expect(view.queryByText('10 tables')).not.toBeNull();
636
- expect(view.queryByText('0 relationships')).not.toBeNull();
637
- });
638
-
639
- // ── Search with partial match ───────────────────────────────────────
640
-
641
- test('search with partial match filters correctly', () => {
642
- const props = createDefaultProps();
643
- const { container } = render(<SchemaDiagram {...props} />);
644
- const view = within(container);
645
-
646
- const searchInput = view.getByPlaceholderText('Filter tables...');
647
- fireEvent.change(searchInput, { target: { value: 'ord' } });
648
- // 'orders' matches 'ord'
649
- expect(view.queryByText('1 tables')).not.toBeNull();
650
- });
651
-
652
- // ═══════════════════════════════════════════════════════════════════════
653
- // NEW: TableNode Rendering Tests
654
- // ═══════════════════════════════════════════════════════════════════════
655
-
656
- describe('TableNode rendering', () => {
657
- test('renders table name in header', () => {
658
- const props = createDefaultProps();
659
- const { container } = render(<SchemaDiagram {...props} />);
660
- const view = within(container);
661
-
662
- // Each table name should appear in uppercase in the header
663
- expect(view.queryByText('users')).not.toBeNull();
664
- expect(view.queryByText('orders')).not.toBeNull();
665
- expect(view.queryByText('products')).not.toBeNull();
666
- });
667
-
668
- test('shows column count badge', () => {
669
- const props = createDefaultProps();
670
- const { container } = render(<SchemaDiagram {...props} />);
671
- const view = within(container);
672
-
673
- // users has 6 columns, orders has 5, products has 4
674
- expect(view.queryByText('6 cols')).not.toBeNull();
675
- expect(view.queryByText('5 cols')).not.toBeNull();
676
- expect(view.queryByText('4 cols')).not.toBeNull();
677
- });
678
-
679
- test('displays column names', () => {
680
- const props = createDefaultProps({ schema: singleTableSchema });
681
- const { container } = render(<SchemaDiagram {...props} />);
682
- const view = within(container);
683
-
684
- expect(view.queryByText('key')).not.toBeNull();
685
- expect(view.queryByText('value')).not.toBeNull();
686
- });
687
-
688
- test('displays column type text', () => {
689
- const props = createDefaultProps({ schema: singleTableSchema });
690
- const { container } = render(<SchemaDiagram {...props} />);
691
-
692
- // Column types should be rendered in uppercase
693
- const texts = Array.from(container.querySelectorAll('.font-mono'));
694
- const typeTexts = texts.map(el => el.textContent);
695
- expect(typeTexts).toContain('text');
696
- });
697
-
698
- test('shows NN for NOT NULL columns', () => {
699
- const props = createDefaultProps({ schema: singleTableSchema });
700
- const { container } = render(<SchemaDiagram {...props} />);
701
-
702
- // 'key' column has nullable: false
703
- const nnElements = container.querySelectorAll('span');
704
- const nnTexts = Array.from(nnElements).map(el => el.textContent);
705
- expect(nnTexts).toContain('NN');
706
- });
707
-
708
- test('compact mode hides column details', () => {
709
- const props = createDefaultProps({ schema: singleTableSchema });
710
- const { container } = render(<SchemaDiagram {...props} />);
711
- const view = within(container);
712
-
713
- // Before compact — columns visible
714
- expect(view.queryByText('key')).not.toBeNull();
715
- expect(view.queryByText('value')).not.toBeNull();
716
-
717
- // Toggle compact mode
718
- const compactButton = view.getByText('Compact').closest('button')!;
719
- fireEvent.click(compactButton);
720
-
721
- // In compact mode, columns should be hidden (only header visible)
722
- // The header still shows settings and "2 cols"
723
- expect(view.queryByText('settings')).not.toBeNull();
724
- expect(view.queryByText('2 cols')).not.toBeNull();
725
- // Column names should not appear as separate elements in the columns list
726
- // key/value are column names, but the column list section is hidden in compact
727
- const nodeEl = container.querySelector('[data-node-id="settings"]');
728
- expect(nodeEl).not.toBeNull();
729
- // In compact mode, the p-1 div with columns is not rendered
730
- // We check that column type badges disappear
731
- const fontMonoElements = nodeEl!.querySelectorAll('.font-mono');
732
- expect(fontMonoElements.length).toBe(0);
733
- });
734
-
735
- test('renders node for each table in schema', () => {
736
- const props = createDefaultProps();
737
- const { container } = render(<SchemaDiagram {...props} />);
738
-
739
- expect(container.querySelector('[data-node-id="users"]')).not.toBeNull();
740
- expect(container.querySelector('[data-node-id="orders"]')).not.toBeNull();
741
- expect(container.querySelector('[data-node-id="products"]')).not.toBeNull();
742
- });
743
-
744
- test('node with empty/null data returns nothing', () => {
745
- // Schema with a valid table ensures at least one node renders
746
- // The guard `if (!data) return null; if (!table) return null;` is tested
747
- // by the fact that the enhanced mock passes correct data through
748
- const props = createDefaultProps({ schema: singleTableSchema });
749
- const { container } = render(<SchemaDiagram {...props} />);
750
-
751
- const nodeEl = container.querySelector('[data-node-id="settings"]');
752
- expect(nodeEl).not.toBeNull();
753
- // The node should have content (table header)
754
- expect(nodeEl!.textContent).toContain('settings');
755
- });
756
- });
757
-
758
- // ═══════════════════════════════════════════════════════════════════════
759
- // NEW: Node Selection Tests
760
- // ═══════════════════════════════════════════════════════════════════════
761
-
762
- describe('Node selection', () => {
763
- test('clicking a node shows "Selected:" info panel', () => {
764
- const props = createDefaultProps();
765
- const { container } = render(<SchemaDiagram {...props} />);
766
- const view = within(container);
767
-
768
- // Initially no selection
769
- expect(view.queryByText('Selected:')).toBeNull();
770
-
771
- // Click the users node
772
- const usersNode = container.querySelector('[data-node-id="users"]')!;
773
- fireEvent.click(usersNode);
774
-
775
- // Selection should appear with selected node name and clear button
776
- expect(view.queryByText('Selected:')).not.toBeNull();
777
- // The selected table name appears in a font-mono span
778
- const selectedSpan = container.querySelector('.font-mono.font-bold');
779
- expect(selectedSpan).not.toBeNull();
780
- expect(selectedSpan!.textContent).toBe('users');
781
- expect(view.queryByText('clear')).not.toBeNull();
782
- });
783
-
784
- test('clicking the same node again deselects (toggle)', () => {
785
- const props = createDefaultProps();
786
- const { container } = render(<SchemaDiagram {...props} />);
787
- const view = within(container);
788
-
789
- const usersNode = container.querySelector('[data-node-id="users"]')!;
790
-
791
- // Select
792
- fireEvent.click(usersNode);
793
- expect(view.queryByText('Selected:')).not.toBeNull();
794
-
795
- // Deselect
796
- fireEvent.click(usersNode);
797
- expect(view.queryByText('Selected:')).toBeNull();
798
- });
799
-
800
- test('clicking "clear" button clears selection', () => {
801
- const props = createDefaultProps();
802
- const { container } = render(<SchemaDiagram {...props} />);
803
- const view = within(container);
804
-
805
- // Select a node
806
- const usersNode = container.querySelector('[data-node-id="users"]')!;
807
- fireEvent.click(usersNode);
808
- expect(view.queryByText('Selected:')).not.toBeNull();
809
-
810
- // Click clear
811
- const clearButton = view.getByText('clear');
812
- fireEvent.click(clearButton);
813
- expect(view.queryByText('Selected:')).toBeNull();
814
- });
815
-
816
- test('clicking pane background clears selection', () => {
817
- const props = createDefaultProps();
818
- const { container } = render(<SchemaDiagram {...props} />);
819
- const view = within(container);
820
-
821
- // Select a node
822
- const usersNode = container.querySelector('[data-node-id="users"]')!;
823
- fireEvent.click(usersNode);
824
- expect(view.queryByText('Selected:')).not.toBeNull();
825
-
826
- // Click the pane background (the react-flow container itself)
827
- const reactFlowContainer = container.querySelector('[data-testid="mock-react-flow"]')!;
828
- // Fire click directly on the container element (target === currentTarget)
829
- fireEvent.click(reactFlowContainer);
830
- expect(view.queryByText('Selected:')).toBeNull();
831
- });
832
- });
833
-
834
- // ═══════════════════════════════════════════════════════════════════════
835
- // NEW: Node/Edge Highlighting Tests
836
- // ═══════════════════════════════════════════════════════════════════════
837
-
838
- describe('Node/Edge highlighting', () => {
839
- test('selected node gets highlighted (blue border)', () => {
840
- const props = createDefaultProps();
841
- const { container } = render(<SchemaDiagram {...props} />);
842
-
843
- // Click users node
844
- const usersNode = container.querySelector('[data-node-id="users"]')!;
845
- fireEvent.click(usersNode);
846
-
847
- // The TableNode's root div inside the data-node-id div should have blue border
848
- const innerDiv = usersNode.querySelector('.border-blue-500\\/60');
849
- expect(innerDiv).not.toBeNull();
850
- });
851
-
852
- test('FK target of selected node is highlighted', () => {
853
- const props = createDefaultProps();
854
- const { container } = render(<SchemaDiagram {...props} />);
855
-
856
- // Select 'orders' which has FK to 'users'
857
- const ordersNode = container.querySelector('[data-node-id="orders"]')!;
858
- fireEvent.click(ordersNode);
859
-
860
- // The 'users' table should also be highlighted (FK target)
861
- const usersNode = container.querySelector('[data-node-id="users"]')!;
862
- const usersInner = usersNode.querySelector('.border-blue-500\\/60');
863
- expect(usersInner).not.toBeNull();
864
- });
865
-
866
- test('FK source of selected node is highlighted', () => {
867
- const props = createDefaultProps();
868
- const { container } = render(<SchemaDiagram {...props} />);
869
-
870
- // Select 'users' — orders has FK pointing to users
871
- const usersNode = container.querySelector('[data-node-id="users"]')!;
872
- fireEvent.click(usersNode);
873
-
874
- // The 'orders' table should be highlighted (it references users via FK)
875
- const ordersNode = container.querySelector('[data-node-id="orders"]')!;
876
- const ordersInner = ordersNode.querySelector('.border-blue-500\\/60');
877
- expect(ordersInner).not.toBeNull();
878
- });
879
-
880
- test('non-related node is NOT highlighted when another is selected', () => {
881
- const props = createDefaultProps();
882
- const { container } = render(<SchemaDiagram {...props} />);
883
-
884
- // Select 'orders' (related to users via FK, not related to products)
885
- const ordersNode = container.querySelector('[data-node-id="orders"]')!;
886
- fireEvent.click(ordersNode);
887
-
888
- // Products should NOT be highlighted
889
- const productsNode = container.querySelector('[data-node-id="products"]')!;
890
- const productsInner = productsNode.querySelector('.border-blue-500\\/60');
891
- expect(productsInner).toBeNull();
892
- // Products should have default border
893
- const productsDefault = productsNode.querySelector('.border-white\\/10');
894
- expect(productsDefault).not.toBeNull();
895
- });
896
- });
897
-
898
- // ═══════════════════════════════════════════════════════════════════════
899
- // NEW: Export Internals Tests
900
- // ═══════════════════════════════════════════════════════════════════════
901
-
902
- describe('Export functionality', () => {
903
- test('PNG export calls html2canvas and creates download link', async () => {
904
- const clickMock = mock(() => {});
905
- const originalCreateElement = document.createElement.bind(document);
906
- const createElementSpy = mock((tag: string) => {
907
- const el = originalCreateElement(tag);
908
- if (tag === 'a') {
909
- Object.defineProperty(el, 'click', { value: clickMock });
910
- }
911
- return el;
912
- });
913
- document.createElement = createElementSpy as unknown as typeof document.createElement;
914
-
915
- const props = createDefaultProps();
916
- const { container } = render(<SchemaDiagram {...props} />);
917
- const view = within(container);
918
-
919
- const pngButton = view.getByText('PNG').closest('button')!;
920
- await act(async () => {
921
- fireEvent.click(pngButton);
922
- });
923
-
924
- // html2canvas should have been called
925
- expect(mockHtml2canvas).toHaveBeenCalledTimes(1);
926
-
927
- // Wait for the async chain
928
- await act(async () => {
929
- await new Promise(r => setTimeout(r, 10));
930
- });
931
-
932
- expect(clickMock).toHaveBeenCalled();
933
-
934
- // Restore
935
- document.createElement = originalCreateElement;
936
- });
937
-
938
- test('SVG export uses XMLSerializer and creates download link', async () => {
939
- const clickMock = mock(() => {});
940
- const revokeObjectURLMock = mock(() => {});
941
- const originalCreateElement = document.createElement.bind(document);
942
- const createElementSpy = mock((tag: string) => {
943
- const el = originalCreateElement(tag);
944
- if (tag === 'a') {
945
- Object.defineProperty(el, 'click', { value: clickMock });
946
- }
947
- return el;
948
- });
949
- document.createElement = createElementSpy as unknown as typeof document.createElement;
950
-
951
- const originalRevokeObjectURL = URL.revokeObjectURL;
952
- URL.revokeObjectURL = revokeObjectURLMock;
953
-
954
- const props = createDefaultProps();
955
- const { container } = render(<SchemaDiagram {...props} />);
956
- const view = within(container);
957
-
958
- const svgButton = view.getByText('SVG').closest('button')!;
959
- await act(async () => {
960
- fireEvent.click(svgButton);
961
- });
962
-
963
- // Wait for async chain
964
- await act(async () => {
965
- await new Promise(r => setTimeout(r, 10));
966
- });
967
-
968
- expect(clickMock).toHaveBeenCalled();
969
- expect(revokeObjectURLMock).toHaveBeenCalled();
970
-
971
- // Restore
972
- document.createElement = originalCreateElement;
973
- URL.revokeObjectURL = originalRevokeObjectURL;
974
- });
975
- });
976
-
977
- // ═══════════════════════════════════════════════════════════════════════
978
- // NEW: Edge Construction & Misc Tests
979
- // ═══════════════════════════════════════════════════════════════════════
980
-
981
- describe('Edge construction and misc', () => {
982
- test('heuristic matches singular table name (author_id → author)', () => {
983
- const props = createDefaultProps({ schema: schemaHeuristicSingular });
984
- const { container } = render(<SchemaDiagram {...props} />);
985
- const view = within(container);
986
-
987
- // books.author_id → author (singular match, not authors)
988
- expect(view.queryByText('1 relationships')).not.toBeNull();
989
- });
990
-
991
- test('schema with undefined foreignKeys does not crash', () => {
992
- const props = createDefaultProps({ schema: schemaUndefinedFK });
993
- const { container } = render(<SchemaDiagram {...props} />);
994
- const view = within(container);
995
-
996
- expect(view.queryByText('1 tables')).not.toBeNull();
997
- expect(view.queryByText('0 relationships')).not.toBeNull();
998
- });
999
-
1000
- test('multi-FK schema shows correct relationship count', () => {
1001
- const props = createDefaultProps({ schema: schemaMultiFK });
1002
- const { container } = render(<SchemaDiagram {...props} />);
1003
- const view = within(container);
1004
-
1005
- // orders→users + items→orders = 2 relationships
1006
- expect(view.queryByText('2 relationships')).not.toBeNull();
1007
- });
1008
-
1009
- test('multi-FK: selecting middle node highlights both connected nodes', () => {
1010
- const props = createDefaultProps({ schema: schemaMultiFK });
1011
- const { container } = render(<SchemaDiagram {...props} />);
1012
-
1013
- // Select 'orders' which is FK target of 'items' and FK source pointing to 'users'
1014
- const ordersNode = container.querySelector('[data-node-id="orders"]')!;
1015
- fireEvent.click(ordersNode);
1016
-
1017
- // 'users' should be highlighted (orders has FK to users)
1018
- const usersNode = container.querySelector('[data-node-id="users"]')!;
1019
- expect(usersNode.querySelector('.border-blue-500\\/60')).not.toBeNull();
1020
-
1021
- // 'items' should be highlighted (items has FK to orders)
1022
- const itemsNode = container.querySelector('[data-node-id="items"]')!;
1023
- expect(itemsNode.querySelector('.border-blue-500\\/60')).not.toBeNull();
1024
- });
1025
-
1026
- test('no-FK warning shown for schema with undefined foreignKeys', () => {
1027
- const props = createDefaultProps({ schema: schemaUndefinedFK });
1028
- const { container } = render(<SchemaDiagram {...props} />);
1029
- const view = within(container);
1030
-
1031
- expect(view.queryByText(/No FK data available/)).not.toBeNull();
1032
- });
1033
- });
1034
- });