mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 10:28:44 +00:00
fix(tabs): allow duplicating data tabs with fresh results
This commit is contained in:
+14
@@ -167,6 +167,20 @@ describe("EditorGroupTabBar behavior", () => {
|
||||
setActivePinia(pinia);
|
||||
});
|
||||
|
||||
it("offers duplicate without rename on a data tab", async () => {
|
||||
const store = useQueryStore();
|
||||
const id = store.createTab("pg-1", "app", "users", "data", "public");
|
||||
const { app, host } = mountBar(store.groups[0].id, [id], id, pinia);
|
||||
await settle();
|
||||
|
||||
const menu = JSON.parse(host.querySelector<HTMLElement>("[data-menu-items]")!.dataset.menuItems!);
|
||||
expect(menu).toContainEqual(expect.objectContaining({ label: "Duplicate", visible: true }));
|
||||
expect(menu).not.toContainEqual(expect.objectContaining({ label: "Rename" }));
|
||||
|
||||
app.unmount();
|
||||
host.remove();
|
||||
});
|
||||
|
||||
it("activates a tab on plain click", async () => {
|
||||
const store = useQueryStore();
|
||||
const firstId = store.createTab("pg-1", "app", "Query 1", "query");
|
||||
|
||||
+19
-1
@@ -4,7 +4,7 @@ import { createCloseAllTabMenuItem, createCloseLeftTabMenuItem, createCloseOther
|
||||
const t = (key: string) => key;
|
||||
|
||||
describe("shared tab menu helpers", () => {
|
||||
it("builds rename and duplicate items only when renaming is allowed", () => {
|
||||
it("builds rename and duplicate items for query tabs", () => {
|
||||
const items = createRenameDuplicateTabItems({
|
||||
tab: { id: "t1" } as never,
|
||||
t,
|
||||
@@ -29,6 +29,24 @@ describe("shared tab menu helpers", () => {
|
||||
expect(queryOnly[0]?.visible).toBe(false);
|
||||
});
|
||||
|
||||
it("allows duplicating data tabs without allowing rename", () => {
|
||||
let duplicated = false;
|
||||
const items = createRenameDuplicateTabItems({
|
||||
tab: { id: "data", mode: "data" } as never,
|
||||
t,
|
||||
canRename: false,
|
||||
onRename: () => undefined,
|
||||
onDuplicate: () => {
|
||||
duplicated = true;
|
||||
},
|
||||
});
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.label).toBe("contextMenu.duplicateTab");
|
||||
expect(items[0]?.visible).toBe(true);
|
||||
items[0]?.action?.();
|
||||
expect(duplicated).toBe(true);
|
||||
});
|
||||
|
||||
it("builds locate and pin menu items with the expected actions", () => {
|
||||
let located = false;
|
||||
let toggled = false;
|
||||
|
||||
@@ -17,7 +17,7 @@ export function createRenameDuplicateTabItems(options: { tab: QueryTab; t: (key:
|
||||
label: options.t("contextMenu.duplicateTab"),
|
||||
action: options.onDuplicate,
|
||||
icon: Copy,
|
||||
visible: options.canRename,
|
||||
visible: options.canRename || options.tab.mode === "data",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -77,6 +77,53 @@ describe("queryStore table data refresh", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("loads a duplicated data tab with its filter, database sort and page without sharing results", async () => {
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
const id = store.createTab("pg-1", "app", "users", "data", "public");
|
||||
store.setTableMeta(id, {
|
||||
database: "app",
|
||||
schema: "public",
|
||||
tableName: "users",
|
||||
tableType: "TABLE",
|
||||
columns: [{ name: "id", data_type: "integer", is_nullable: false, column_default: null, is_primary_key: true, extra: null }],
|
||||
primaryKeys: ["id"],
|
||||
});
|
||||
const original = store.tabs.find((tab) => tab.id === id)!;
|
||||
original.whereInput = "status = 'ACTIVE'";
|
||||
original.orderByInput = "id DESC";
|
||||
original.resultSortColumn = "id";
|
||||
original.resultSortDirection = "desc";
|
||||
original.resultSortMode = "database";
|
||||
original.resultPageLimit = 25;
|
||||
original.resultPageOffset = 50;
|
||||
original.result = { columns: ["id"], rows: [[99]], affected_rows: 0, execution_time_ms: 1 };
|
||||
mocks.executeMulti.mockResolvedValue([{ columns: ["id"], rows: [[42]], affected_rows: 0, execution_time_ms: 1 }]);
|
||||
|
||||
store.duplicateTab(id);
|
||||
const copy = store.tabs[1];
|
||||
await vi.waitFor(() => expect(copy.result?.rows).toEqual([[42]]));
|
||||
|
||||
expect(copy.id).not.toBe(id);
|
||||
expect(copy.mode).toBe("data");
|
||||
expect(copy.connectionId).toBe("pg-1");
|
||||
expect(copy.database).toBe("app");
|
||||
expect(copy.schema).toBe("public");
|
||||
expect(store.activeTabId).toBe(copy.id);
|
||||
expect(store.groups[0].tabIds).toEqual([id, copy.id]);
|
||||
expect(mocks.buildTableSelectSql).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
whereInput: "status = 'ACTIVE'",
|
||||
orderBy: "id DESC",
|
||||
limit: 25,
|
||||
offset: 50,
|
||||
}),
|
||||
);
|
||||
expect(mocks.executeMulti).toHaveBeenCalledTimes(1);
|
||||
expect(original.result?.rows).toEqual([[99]]);
|
||||
expect(copy.result).not.toBe(original.result);
|
||||
});
|
||||
|
||||
it("refreshes only matching data tabs after a table mutation", async () => {
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
|
||||
@@ -4645,18 +4645,18 @@ export const useQueryStore = defineStore("query", () => {
|
||||
lastExecutedSql: undefined,
|
||||
resultBaseSql: original.resultBaseSql,
|
||||
resultSortedSql: undefined,
|
||||
resultSortColumn: undefined,
|
||||
resultSortColumnIndex: undefined,
|
||||
resultSortDirection: undefined,
|
||||
resultSortMode: undefined,
|
||||
resultSortColumn: original.mode === "data" && original.resultSortMode === "database" ? original.resultSortColumn : undefined,
|
||||
resultSortColumnIndex: original.mode === "data" && original.resultSortMode === "database" ? original.resultSortColumnIndex : undefined,
|
||||
resultSortDirection: original.mode === "data" && original.resultSortMode === "database" ? original.resultSortDirection : undefined,
|
||||
resultSortMode: original.mode === "data" && original.resultSortMode === "database" ? original.resultSortMode : undefined,
|
||||
resultLocalSortOriginalRows: undefined,
|
||||
resultLocalSortOriginalLargeValueCells: undefined,
|
||||
resultLocalSortOriginalMongoDocuments: undefined,
|
||||
resultLocalSortOriginalMongoCopyDocuments: undefined,
|
||||
orderByInput: undefined,
|
||||
orderByInput: original.mode === "data" ? original.orderByInput : undefined,
|
||||
resultPageSql: undefined,
|
||||
resultPageLimit: undefined,
|
||||
resultPageOffset: undefined,
|
||||
resultPageLimit: original.mode === "data" ? original.resultPageLimit : undefined,
|
||||
resultPageOffset: original.mode === "data" ? original.resultPageOffset : undefined,
|
||||
resultExecutedPageLimit: undefined,
|
||||
resultExecutedPageOffset: undefined,
|
||||
resultCountSql: undefined,
|
||||
@@ -4696,6 +4696,8 @@ export const useQueryStore = defineStore("query", () => {
|
||||
objectSource: original.objectSource ? { ...original.objectSource } : undefined,
|
||||
sourceView: original.sourceView,
|
||||
tableMeta: original.tableMeta ? { ...original.tableMeta, columns: [...original.tableMeta.columns], primaryKeys: [...original.tableMeta.primaryKeys] } : undefined,
|
||||
tableMetaGeneration: original.mode === "data" ? original.tableMetaGeneration : undefined,
|
||||
tableMetaUpdatedAt: original.mode === "data" ? original.tableMetaUpdatedAt : undefined,
|
||||
queryAnalysis: original.queryAnalysis ? { ...original.queryAnalysis, sources: original.queryAnalysis.sources?.map((source) => ({ ...source })), columns: original.queryAnalysis.columns.map((c) => ({ ...c })) } : undefined,
|
||||
querySourceColumns: original.querySourceColumns ? [...original.querySourceColumns] : undefined,
|
||||
queryWriteTargets: original.queryWriteTargets?.map((target) => ({ ...target, sourceColumns: [...target.sourceColumns] })),
|
||||
@@ -4717,6 +4719,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
focusedGroupId.value = owner.id;
|
||||
}
|
||||
activeTabId.value = newId;
|
||||
if (newTab.mode === "data") void refreshDataTab(newId);
|
||||
}
|
||||
|
||||
function closeTabsWhere(predicate: (tab: QueryTab) => boolean) {
|
||||
|
||||
Reference in New Issue
Block a user