From 46afd0dd5e41d0cc7f12c899237538324fe1acc2 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sat, 5 Sep 2026 11:36:31 +0800 Subject: [PATCH] fix(editor): take tab id first in toolbar context switching Closes #8218 --- apps/desktop/src/App.vue | 8 ++-- .../editorToolbarContextSwitching.test.ts | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 packages/app-tests/editorToolbarContextSwitching.test.ts diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 188917f181..fdfa911f09 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -2491,7 +2491,7 @@ function onQueryEditorObjectSourceSaved() { contentAreaRef.value?.refreshQueryEditorCompletionCache(); } -async function changeActiveConnection(connectionId: string, tabId?: string) { +async function changeActiveConnection(tabId: string, connectionId: string) { const tab = resolveToolbarTab(tabId); if (!tab) return; const connection = connectionStore.getConfig(connectionId); @@ -2530,7 +2530,7 @@ async function changeActiveConnection(connectionId: string, tabId?: string) { } } -function changeActiveDatabase(database: string, tabId?: string) { +function changeActiveDatabase(tabId: string, database: string) { const tab = resolveToolbarTab(tabId); if (tab) { queryStore.updateDatabase(tab.id, database); @@ -2541,7 +2541,7 @@ function changeActiveDatabase(database: string, tabId?: string) { } } -function changeActiveCatalog(catalog: string | undefined, database: string, tabId?: string) { +function changeActiveCatalog(tabId: string, catalog: string | undefined, database: string) { const tab = resolveToolbarTab(tabId); if (tab) { queryStore.updateCatalog(tab.id, catalog, database); @@ -2561,7 +2561,7 @@ async function clearActiveDefaultDatabase(tabId?: string) { await connectionStore.clearDefaultDatabase(tab.connectionId); } -function changeActiveSchema(schema: string | undefined, tabId?: string) { +function changeActiveSchema(tabId: string, schema: string | undefined) { const tab = resolveToolbarTab(tabId); if (!tab) return; queryStore.updateSchema(tab.id, schema); diff --git a/packages/app-tests/editorToolbarContextSwitching.test.ts b/packages/app-tests/editorToolbarContextSwitching.test.ts new file mode 100644 index 0000000000..2bb585735b --- /dev/null +++ b/packages/app-tests/editorToolbarContextSwitching.test.ts @@ -0,0 +1,46 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { test } from "vitest"; + +const appPath = "apps/desktop/src/App.vue"; +const contractPath = "apps/desktop/src/components/layout/editorToolbarActions.ts"; + +// App.vue 是根组件,测试无法挂载;而这族回归(#8218,同 #8216)恰恰是 +// 「契约 tabId-first、实现 value-first」的 string/string 参数错位,结构化 +// 类型检查对此不可见,所以这里用源码钉住契约两侧的参数顺序。 +function functionSource(name: string): string { + const source = readFileSync(appPath, "utf8"); + const start = source.indexOf(`function ${name}`); + assert.ok(start >= 0, `App.vue should define ${name}`); + const relativeEnd = source.slice(start).search(/\n(?:async\s+)?function\s+[A-Za-z]/); + return source.slice(start, relativeEnd > 0 ? start + relativeEnd : undefined); +} + +test("toolbar context switchers resolve the acting tab from their first parameter (#8218)", () => { + const signatures: Array<[name: string, expected: RegExp]> = [ + ["changeActiveConnection", /^function changeActiveConnection\(tabId: string, connectionId: string\)/], + ["changeActiveDatabase", /^function changeActiveDatabase\(tabId: string, database: string\)/], + ["changeActiveCatalog", /^function changeActiveCatalog\(tabId: string, catalog: string \| undefined, database: string\)/], + ["changeActiveSchema", /^function changeActiveSchema\(tabId: string, schema: string \| undefined\)/], + ]; + for (const [name, expected] of signatures) { + const body = functionSource(name); + assert.match(body, expected); + assert.match(body, /resolveToolbarTab\(tabId\)/); + } +}); + +test("App provides the switchers directly so the injected contract order reaches them", () => { + const appSource = readFileSync(appPath, "utf8"); + for (const name of ["changeConnection", "changeCatalog", "changeDatabase", "changeSchema"]) { + assert.match(appSource, new RegExp(`${name}: changeActive[A-Za-z]+,`)); + } +}); + +test("the injected toolbar action contract keeps tab id as the first parameter", () => { + const contract = readFileSync(contractPath, "utf8"); + assert.match(contract, /changeConnection\(tabId: string, connectionId: string\)/); + assert.match(contract, /changeCatalog\(tabId: string, catalog: string \| undefined, database: string\)/); + assert.match(contract, /changeDatabase\(tabId: string, database: string\)/); + assert.match(contract, /changeSchema\(tabId: string, schema: string \| undefined\)/); +});