Editor settings, SSH/SSL runtime, data import + table-menu loose ends (#7)
* feat: editor settings model + typed clamped defaults (Task 1) * feat: carry SSH config + ssh_password to backend DbConfig (Task 2) * feat: Change enum bulk/drop/empty + type-specific change payload builder (Task 3) * feat: csv parser + shared export util (Task 4) * feat: rustls TLS connector factory with modes + client auth (Task 5) * feat: real SSH tunnel manager (testable backend) + pool eviction hook (Task 6) * feat: table DDL fetch (sqlite + pg_dump arg builder) (Task 7) * feat: keychain SSH secrets + connection delete purge + tunnel lifecycle (Task 8) * feat: real SSH tunnel + TLS connect path for postgres/mysql (Task 9) * feat: execute_change bulk/drop/empty + get_table_ddl command (Task 10) * feat: fetch SSH secrets into dbConnect + save on connection form (Task 11) * feat: Editor settings tab UI (Task 12) * feat: QueryEditor applies editor settings live (Task 13) * feat: ImportDialog with CSV/JSON preview + column mapping (Task 14) * feat: table-menu export/empty/delete/import + queue labels + payload builder (Task 15) * fix: error sanitization, encrypted-key guard, row-indexed import errors, caps (Task 16) * docs: mark Editor Settings, SSH/SSL runtime, Data Import, table-menu loose ends shipped (Task 17) * feat: auto-refresh schema tree after schema-modifying SQL (query + queue drop) * fix: use theme-consistent red classes for danger menu items (text-error was undefined) * feat: changes queue as tab-bar popover + amber pending border * feat: redesign changes popover (visual/SQL toggle, cards, footer actions, Cmd+S) * refactor: drop per-card status label from changes popover cards * feat: green completion indicator on committed cards + auto-close tabs of dropped tables * docs: changes queue popover UX + auto schema refresh statuses
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { TableOverflowMenu } from "./TableOverflowMenu";
|
||||
import { useDbViewerStore } from "../../stores/dbViewerStore";
|
||||
import { useUiStore } from "../../stores/uiStore";
|
||||
import * as commands from "../../lib/commands";
|
||||
import * as exportData from "../../lib/exportData";
|
||||
|
||||
describe("TableOverflowMenu", () => {
|
||||
beforeEach(() => {
|
||||
@@ -10,6 +14,9 @@ describe("TableOverflowMenu", () => {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
useDbViewerStore.getState().reset();
|
||||
useUiStore.setState({ activeConnectionId: "c1" });
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("renders menu trigger button", () => {
|
||||
@@ -34,4 +41,44 @@ describe("TableOverflowMenu", () => {
|
||||
await user.click(screen.getByText("Open in new tab"));
|
||||
expect(onOpenTab).toHaveBeenCalledWith("public", "users", true);
|
||||
});
|
||||
|
||||
it("Copy table schema calls getTableDdl and writes clipboard", async () => {
|
||||
vi.spyOn(commands, "getTableDdl").mockResolvedValue("CREATE TABLE t (id int)");
|
||||
const writeText = (navigator.clipboard as any).writeText as ReturnType<typeof vi.fn>;
|
||||
render(<TableOverflowMenu schema="public" table="t" onOpenTab={() => "tab-1"} />);
|
||||
fireEvent.click(screen.getByLabelText(/table options/i));
|
||||
fireEvent.click(screen.getByText(/copy table schema/i));
|
||||
await waitFor(() => expect(writeText).toHaveBeenCalledWith("CREATE TABLE t (id int)"));
|
||||
});
|
||||
|
||||
it("Empty Table opens confirm then stages an empty_table change", async () => {
|
||||
render(<TableOverflowMenu schema="public" table="t" onOpenTab={() => "tab-1"} />);
|
||||
fireEvent.click(screen.getByLabelText(/table options/i));
|
||||
fireEvent.click(screen.getByText(/empty table/i));
|
||||
fireEvent.click(screen.getByRole("button", { name: /empty table/i }));
|
||||
await waitFor(() => {
|
||||
const q = useDbViewerStore.getState().changesQueue;
|
||||
expect(q[q.length - 1]).toEqual(expect.objectContaining({ type: "empty_table", schema: "public", table: "t" }));
|
||||
});
|
||||
});
|
||||
|
||||
it("Delete Table opens confirm then stages a drop_table change", async () => {
|
||||
render(<TableOverflowMenu schema="public" table="t" onOpenTab={() => "tab-1"} />);
|
||||
fireEvent.click(screen.getByLabelText(/table options/i));
|
||||
fireEvent.click(screen.getByText(/delete table/i));
|
||||
fireEvent.click(screen.getByRole("button", { name: /delete table/i }));
|
||||
await waitFor(() => {
|
||||
const q = useDbViewerStore.getState().changesQueue;
|
||||
expect(q[q.length - 1]).toEqual(expect.objectContaining({ type: "drop_table", schema: "public", table: "t" }));
|
||||
});
|
||||
});
|
||||
|
||||
it("Export data calls exportData when rows and columns are provided", async () => {
|
||||
const spy = vi.spyOn(exportData, "exportData");
|
||||
const columns = [{ name: "id", data_type: "integer", is_nullable: false, is_pk: true, is_fk: false, fk_ref: null, default_value: null }];
|
||||
render(<TableOverflowMenu schema="public" table="t" onOpenTab={() => "tab-1"} columns={columns} rows={[[1]]} />);
|
||||
fireEvent.click(screen.getByLabelText(/table options/i));
|
||||
fireEvent.click(screen.getByText(/export data \(csv\)/i));
|
||||
await waitFor(() => expect(spy).toHaveBeenCalledWith([[1]], columns, "csv", "public.t"));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user