Files
MyGoNavi/frontend/src/components/FindInDatabaseModal.test.tsx
tianqijiuyun-latiao 5fc29a6fd3 feat(i18n): 推进多语言剩余切片闭环
- 补齐 DataGrid、DataViewer、DefinitionViewer、JVM 等模块多语言文案与回归测试
- 收口 JVM 前后端展示、诊断、监控和资源呈现相关多语言路径
- 更新六语言共享词典并保留 raw 边界
2026-06-16 12:40:33 +08:00

206 lines
6.7 KiB
TypeScript

import React from "react";
import { readFileSync } from "node:fs";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { act, create, type ReactTestRenderer } from "react-test-renderer";
import { I18nProvider } from "../i18n/provider";
import FindInDatabaseModal from "./FindInDatabaseModal";
const mocks = vi.hoisted(() => ({
dbQuery: vi.fn(),
dbGetTables: vi.fn(),
dbGetAllColumns: vi.fn(),
message: {
warning: vi.fn(),
error: vi.fn(),
info: vi.fn(),
},
storeState: {
connections: [
{
id: "conn-1",
config: {
type: "mysql",
host: "localhost",
port: 3306,
user: "root",
password: "",
database: "app",
},
},
],
theme: "light",
},
}));
vi.mock("../store", () => ({
useStore: (selector: (state: typeof mocks.storeState) => unknown) => selector(mocks.storeState),
}));
vi.mock("../i18n/runtime", () => ({
applyDayjsLocale: vi.fn(),
syncLanguageRuntime: vi.fn(),
}));
vi.mock("../../wailsjs/go/app/App", () => ({
DBQuery: mocks.dbQuery,
DBGetTables: mocks.dbGetTables,
DBGetAllColumns: mocks.dbGetAllColumns,
}));
vi.mock("antd", async () => {
const React = await import("react");
return {
Modal: ({
children,
open,
title,
}: {
children?: React.ReactNode;
open?: boolean;
title?: React.ReactNode;
}) => (open ? React.createElement("section", null, title, children) : null),
Input: ({
placeholder,
value,
onChange,
}: {
placeholder?: string;
value?: string;
onChange?: (event: { target: { value: string } }) => void;
}) =>
React.createElement("input", {
placeholder,
value,
onChange: (event: any) => onChange?.({ target: { value: event.target.value } }),
}),
Button: ({
children,
disabled,
icon,
onClick,
}: {
children?: React.ReactNode;
disabled?: boolean;
icon?: React.ReactNode;
onClick?: () => void;
}) => React.createElement("button", { disabled, onClick }, icon, children),
Select: ({ options }: { options?: Array<{ label: React.ReactNode; value: string }> }) =>
React.createElement(
"div",
null,
options?.map((option) => React.createElement("span", { key: option.value }, option.label)),
),
Table: ({ columns, dataSource }: { columns?: any[]; dataSource?: any[] }) =>
React.createElement(
"div",
null,
columns?.map((column) => React.createElement("span", { key: column.key || column.dataIndex }, column.title)),
dataSource?.map((row, index) =>
React.createElement(
"div",
{ key: index },
Object.values(row).map((value, valueIndex) =>
React.createElement("span", { key: valueIndex }, String(value)),
),
),
),
),
Progress: ({ percent }: { percent: number }) => React.createElement("div", null, `${percent}%`),
Space: ({ children }: { children?: React.ReactNode }) => React.createElement("div", null, children),
Tag: ({ children }: { children?: React.ReactNode }) => React.createElement("span", null, children),
Tooltip: ({ children, title }: { children?: React.ReactNode; title?: React.ReactNode }) =>
React.createElement("span", { title }, children),
Empty: ({ description }: { description?: React.ReactNode }) => React.createElement("div", null, description),
message: mocks.message,
};
});
vi.mock("@ant-design/icons", async () => {
const React = await import("react");
const Icon = () => React.createElement("span", null);
return {
SearchOutlined: Icon,
StopOutlined: Icon,
EyeOutlined: Icon,
DatabaseOutlined: Icon,
};
});
const textContent = (node: any): string => {
if (node === null || node === undefined) return "";
if (typeof node === "string" || typeof node === "number") return String(node);
if (Array.isArray(node)) return node.map((item) => textContent(item)).join("");
return textContent(node.children || []);
};
const renderFindModal = () => {
let renderer!: ReactTestRenderer;
act(() => {
renderer = create(
<I18nProvider preference="en-US" onPreferenceChange={() => undefined}>
<FindInDatabaseModal open connectionId="conn-1" dbName="app_db" onClose={vi.fn()} />
</I18nProvider>,
);
});
return renderer;
};
describe("FindInDatabaseModal i18n", () => {
beforeEach(() => {
mocks.dbQuery.mockReset();
mocks.dbGetTables.mockReset();
mocks.dbGetAllColumns.mockReset();
mocks.message.warning.mockClear();
mocks.message.error.mockClear();
mocks.message.info.mockClear();
});
it("renders search chrome in the active language while preserving raw database name", () => {
const renderer = renderFindModal();
const renderedText = textContent(renderer.toJSON());
const input = renderer.root.findByType("input");
expect(renderedText).toContain("Search in database - app_db");
expect(input.props.placeholder).toBe("Enter the string to search for...");
expect(renderedText).toContain("Contains");
expect(renderedText).toContain("Exact match");
expect(renderedText).toContain("Search");
});
it("localizes controlled error wrappers while preserving backend detail", async () => {
mocks.dbGetTables.mockResolvedValue({ success: false, message: "driver raw detail" });
const renderer = renderFindModal();
const input = renderer.root.findByType("input");
await act(async () => {
input.props.onChange({ target: { value: "alice" } });
});
const searchButton = renderer.root.findAllByType("button").find((button) => textContent(button).includes("Search"));
expect(searchButton).toBeTruthy();
await act(async () => {
searchButton?.props.onClick();
await Promise.resolve();
});
expect(mocks.message.error).toHaveBeenCalledWith("Failed to get table list: driver raw detail");
});
it("does not keep migrated Chinese UI literals in FindInDatabaseModal source", () => {
const source = readFileSync(new URL("./FindInDatabaseModal.tsx", import.meta.url), "utf8");
expect(source).not.toContain("请输入搜索关键字");
expect(source).not.toContain("未找到连接配置");
expect(source).not.toContain("获取表列表失败");
expect(source).not.toContain("当前数据库没有表");
expect(source).not.toContain("未找到匹配的数据");
expect(source).not.toContain("搜索出错");
expect(source).not.toContain("在数据库中搜索");
expect(source).not.toContain("输入要搜索的字符串");
expect(source).not.toContain("精确匹配");
expect(source).not.toContain("匹配行详情");
});
});