fix(oceanbase): localize explain plan estimated time label

This commit is contained in:
efeire authored and GitHub committed 2026-09-20 23:00:59 +08:00
1 parent ce2d270132
commit 98b7a9ed75
18 files changed
+119 -16

No files matched your search

@@ -4,6 +4,7 @@ import { useI18n } from "vue-i18n";
import { ArrowDownWideNarrow, CircleHelp, GitMerge, KeyRound, Layers, ListTree, Maximize2, MonitorCheck, Pencil, Sigma, Split, Table2, ZoomIn, ZoomOut } from "@lucide/vue";
import { Button } from "@/components/ui/button";
import type { ExplainPlanNode } from "@/lib/diagram/explainPlan";
import { formatExplainPlanDetails } from "@/lib/diagram/explainPlan";
import type { PlanCanvasCategory, PlanCanvasNode } from "@/lib/diagram/planCanvas";
import { buildPlanCanvas, edgeStrokeWidth, formatPlanRows, heatLevel, PLAN_CANVAS_GAP_X, PLAN_CANVAS_NODE_H, PLAN_CANVAS_NODE_W } from "@/lib/diagram/planCanvas";
@@ -110,7 +111,7 @@ interface DetailEntry {
}
const selectedDetails = computed<DetailEntry[]>(() =>
(selected.value?.node.details ?? [])
formatExplainPlanDetails(selected.value?.node, t("explain.estimatedTime"))
.map((detail): DetailEntry => {
const separatorIndex = detail.indexOf(":");
if (separatorIndex === -1) return { value: detail.trim() };
@@ -5,6 +5,7 @@ import { useI18n } from "vue-i18n";
import { Button } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import type { ExplainPlanNode } from "@/lib/diagram/explainPlan";
import { formatExplainPlanDetails } from "@/lib/diagram/explainPlan";
const props = defineProps<{
node: ExplainPlanNode;
@@ -20,7 +21,7 @@ interface ExplainPlanDetailEntry {
}
const detailEntries = computed<ExplainPlanDetailEntry[]>(() =>
props.node.details
formatExplainPlanDetails(props.node, t("explain.estimatedTime"))
.map((detail): ExplainPlanDetailEntry => {
const separatorIndex = detail.indexOf(":");
if (separatorIndex === -1) return { value: detail.trim() };
@@ -3,7 +3,7 @@ import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { AlertCircle, Braces, GitBranch, Table2, FileText, Workflow } from "@lucide/vue";
import type { ParsedExplainPlan, ExplainPlanNode } from "@/lib/diagram/explainPlan";
import { flattenExplainPlanNodes } from "@/lib/diagram/explainPlan";
import { flattenExplainPlanNodes, formatExplainPlanDetails } from "@/lib/diagram/explainPlan";
import { extractActualRows } from "@/lib/diagram/planCanvas";
import { Button } from "@/components/ui/button";
import type { QueryResult } from "@/types/database";
@@ -185,7 +185,7 @@ function tableCellText(value: unknown): string {
<td class="px-2 py-1.5 tabular-nums">{{ row.node.cost || "-" }}</td>
<td class="px-2 py-1.5 tabular-nums">{{ row.node.rows || "-" }}</td>
<td class="px-2 py-1.5 text-muted-foreground">
{{ row.node.details.join("; ") || "-" }}
{{ formatExplainPlanDetails(row.node, t("explain.estimatedTime")).join("; ") || "-" }}
</td>
</tr>
</tbody>
@@ -0,0 +1,61 @@
// @vitest-environment happy-dom
import { afterEach, describe, expect, it } from "vitest";
import { createApp, nextTick, type App } from "vue";
import { createI18n } from "vue-i18n";
import en from "@/i18n/locales/en";
import zhCN from "@/i18n/locales/zh-CN";
import { parseExplainResult } from "@/lib/diagram/explainPlan";
import ExplainPlanViewer from "@/components/explain/ExplainPlanViewer.vue";
let app: App | undefined;
afterEach(() => {
app?.unmount();
app = undefined;
document.body.innerHTML = "";
});
describe("OceanBase estimated time localization", () => {
it.each(["canvas", "tree", "summary"] as const)("updates the existing %s view when the locale changes", async (view) => {
const plan = parseExplainResult("oceanbase-oracle", {
columns: ["Query Plan"],
rows: [[JSON.stringify({ ID: 0, OPERATOR: "TABLE FULL SCAN", NAME: "EXAMPLE_TABLE", "EST.TIME(us)": 0, filter: "C1 > 4" })]],
affected_rows: 0,
execution_time_ms: 1,
});
const originalPlan = JSON.stringify(plan);
const i18n = createI18n({ legacy: false, locale: "en", messages: { en, "zh-CN": zhCN } });
const container = document.createElement("div");
document.body.append(container);
app = createApp(ExplainPlanViewer, { plan });
app.use(i18n);
app.mount(container);
await nextTick();
if (view !== "canvas") {
const label = en.explain[view];
const button = [...container.querySelectorAll("button")].find((item) => item.textContent?.trim() === label);
expect(button).toBeDefined();
button!.click();
await nextTick();
}
expect(container.textContent).toContain("Estimated time");
expect(container.textContent).toContain("0 µs");
expect(container.textContent).toContain("C1 > 4");
i18n.global.locale.value = "zh-CN";
await nextTick();
expect(container.textContent).toContain("预计耗时");
expect(container.textContent).not.toContain("Estimated time");
expect(container.textContent).toContain("0 µs");
expect(container.textContent).toContain("C1 > 4");
i18n.global.locale.value = "en";
await nextTick();
expect(container.textContent).toContain("Estimated time");
expect(container.textContent).not.toContain("预计耗时");
expect(JSON.stringify(plan)).toBe(originalPlan);
});
});