Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe("CloudDocsPage - 新建文档流程", () => {
setupFetchMock();
});

it("点击新建按钮应打开模板选择弹窗", async () => {
it("Clicking New button should open template selector modal", async () => {
render(<CloudDocsPage />);
await waitFor(() => {
expect(screen.queryByText("加载中...")).not.toBeInTheDocument();
Expand All @@ -88,14 +88,14 @@ describe("CloudDocsPage - 新建文档流程", () => {
// 弹窗默认关闭
expect(screen.queryByTestId("template-modal")).not.toBeInTheDocument();

// 点击新建
await userEvent.click(screen.getByText("新建"));
// Click New
await userEvent.click(screen.getByText("New"));

// 弹窗应打开
expect(screen.getByTestId("template-modal")).toBeInTheDocument();
});

it("选择空白文档应调用 /documents/create 并跳转", async () => {
it("Selecting blank document should call /documents/create and navigate", async () => {
const createdDoc = { id: 99 };
setupFetchMock({
"/documents/create": { ok: true, json: async () => createdDoc } as Response,
Expand All @@ -106,22 +106,22 @@ describe("CloudDocsPage - 新建文档流程", () => {
expect(screen.queryByText("加载中...")).not.toBeInTheDocument();
});

await userEvent.click(screen.getByText("新建"));
await userEvent.click(screen.getByText("New"));
await userEvent.click(screen.getByTestId("select-blank"));

await waitFor(() => {
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining("/documents/create"),
expect.objectContaining({
method: "POST",
body: JSON.stringify({ title: "未命名文档" }),
body: JSON.stringify({ title: "Untitled Document" }),
}),
);
expect(mockPush).toHaveBeenCalledWith("/home/cloud-docs/99");
});
});

it("选择模板应调用 /templates/:id/create-document 并跳转", async () => {
it("Selecting template should call /templates/:id/create-document and navigate", async () => {
const createdDoc = { id: 88 };
setupFetchMock({
"/templates/": { ok: true, json: async () => createdDoc } as Response,
Expand All @@ -132,7 +132,7 @@ describe("CloudDocsPage - 新建文档流程", () => {
expect(screen.queryByText("加载中...")).not.toBeInTheDocument();
});

await userEvent.click(screen.getByText("新建"));
await userEvent.click(screen.getByText("New"));
await userEvent.click(screen.getByTestId("select-template"));

await waitFor(() => {
Expand All @@ -147,7 +147,7 @@ describe("CloudDocsPage - 新建文档流程", () => {
});
});

it("创建文档失败应显示错误提示", async () => {
it("Creating document failure should show error message", async () => {
setupFetchMock({
"/documents/create": { ok: false, status: 500 } as Response,
});
Expand All @@ -157,11 +157,11 @@ describe("CloudDocsPage - 新建文档流程", () => {
expect(screen.queryByText("加载中...")).not.toBeInTheDocument();
});

await userEvent.click(screen.getByText("新建"));
await userEvent.click(screen.getByText("New"));
await userEvent.click(screen.getByTestId("select-blank"));

await waitFor(() => {
expect(screen.getByText("创建文档失败,请稍后重试")).toBeInTheDocument();
expect(screen.getByText("Failed to create document, please try again later")).toBeInTheDocument();
});
// 不应跳转
expect(mockPush).not.toHaveBeenCalled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default function TemplateSelectorModal({
void fetchTemplates();
}, [isOpen]);

/** 确认创建文档,selected 为 null 表示空白文档 */
/** 确认创建文档,selected 为 null 表示Blank Document */
const handleConfirm = async () => {
setCreating(true);
await onSelect(selected);
Expand Down Expand Up @@ -113,7 +113,7 @@ export default function TemplateSelectorModal({
<FiFileText className="h-6 w-6" />
</div>
<div>
<p className="font-medium text-slate-900">空白文档</p>
<p className="font-medium text-slate-900">Blank Document</p>
<p className="text-sm text-slate-500">从空白页面开始</p>
</div>
{selected === null && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ describe("TemplateSelectorModal", () => {
expect(container.innerHTML).toBe("");
});

it("打开时应渲染标题和空白文档选项", async () => {
it("Should render title and blank document option on open", async () => {
mockFetch.mockResolvedValueOnce({ ok: true, json: async () => [] });
render(<TemplateSelectorModal isOpen={true} onClose={onClose} onSelect={onSelect} />);
expect(screen.getByText("从模板创建")).toBeInTheDocument();
expect(screen.getByText("空白文档")).toBeInTheDocument();
expect(screen.getByText("Blank Document")).toBeInTheDocument();
expect(screen.getByText("创建文档")).toBeInTheDocument();
});

Expand All @@ -50,8 +50,8 @@ describe("TemplateSelectorModal", () => {
);
});

// ————— 核心场景:空白文档创建 —————
it("默认选中空白文档,点击创建应调用 onSelect(null)", async () => {
// /** Core scenario: blank document creation */
it("Default should be blank document, clicking create should call onSelect(null)", async () => {
mockFetch.mockResolvedValueOnce({ ok: true, json: async () => [] });
render(<TemplateSelectorModal isOpen={true} onClose={onClose} onSelect={onSelect} />);
await waitFor(() => {
Expand Down Expand Up @@ -81,16 +81,16 @@ describe("TemplateSelectorModal", () => {
);
});

it("选择模板后再切回空白文档,应调用 onSelect(null)", async () => {
it("Selecting template then switching back to blank should call onSelect(null)", async () => {
mockFetch.mockResolvedValueOnce({ ok: true, json: async () => fakeTemplates });
render(<TemplateSelectorModal isOpen={true} onClose={onClose} onSelect={onSelect} />);
await waitFor(() => {
expect(screen.getByText("周报模板")).toBeInTheDocument();
});

// 先选模板,再切回空白文档
// Select template then switch back to blank document
await userEvent.click(screen.getByText("周报模板"));
await userEvent.click(screen.getByText("空白文档"));
await userEvent.click(screen.getByText("Blank Document"));
await userEvent.click(screen.getByText("创建文档"));

expect(onSelect).toHaveBeenCalledWith(null);
Expand Down Expand Up @@ -129,7 +129,7 @@ describe("TemplateSelectorModal", () => {
});
});

it("模板加载失败时不崩溃,仍可创建空白文档", async () => {
it("Template load failure should not crash, blank document still creatable", async () => {
mockFetch.mockRejectedValueOnce(new Error("Network error"));
render(<TemplateSelectorModal isOpen={true} onClose={onClose} onSelect={onSelect} />);
await waitFor(() => {
Expand Down
30 changes: 15 additions & 15 deletions packages/frontend/app/home/cloud-docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function CloudDocsPage() {
setDocuments({ total: data.length, page: 1, pageSize: 50, items: data });
} catch (err) {
console.error(err);
setError("加载共享文档失败,请稍后重试");
setError("Failed to load shared documents, please try again later");
} finally {
setLoading(false);
}
Expand All @@ -90,7 +90,7 @@ export default function CloudDocsPage() {
setDocuments(data);
} catch (err) {
console.error(err);
setError("加载文档失败,请稍后重试");
setError("Failed to load documents, please try again later");
} finally {
setLoading(false);
}
Expand All @@ -112,7 +112,7 @@ export default function CloudDocsPage() {
try {
let res: Response;
if (template) {
// 基于模板创建文档
// Create document from template
res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001"}/templates/${template.id}/create-document`,
{
Expand All @@ -123,14 +123,14 @@ export default function CloudDocsPage() {
},
);
} else {
// 创建空白文档
// Create blank document
res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001"}/documents/create`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ title: "未命名文档" }),
body: JSON.stringify({ title: "Untitled Document" }),
},
);
}
Expand All @@ -139,7 +139,7 @@ export default function CloudDocsPage() {
router.push(`/home/cloud-docs/${doc.id}`);
} catch (err) {
console.error(err);
setError("创建文档失败,请稍后重试");
setError("Failed to create document, please try again later");
setCreatingDoc(false);
}
};
Expand All @@ -149,7 +149,7 @@ export default function CloudDocsPage() {
router.push(`/home/cloud-docs/${id}`);
}, [router]);

/** 删除文档 */
/** Delete document */
const handleDeleteDoc = useCallback(async (id: number) => {
if (deletingDocId) return;
setDeletingDocId(id);
Expand All @@ -167,7 +167,7 @@ export default function CloudDocsPage() {
);
} catch (err) {
console.error(err);
setError("删除文档失败,请稍后重试");
setError("Failed to delete document, please try again later");
} finally {
setDeletingDocId(null);
}
Expand Down Expand Up @@ -207,7 +207,7 @@ export default function CloudDocsPage() {
className="flex items-center gap-2 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-all duration-200 shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
>
<VscNewFile className="h-5 w-5" />
<span>新建</span>
<span>New</span>
</button>
<button className="flex items-center gap-2 px-6 py-3 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600 transition-all duration-200 shadow-md hover:shadow-lg transform hover:-translate-y-0.5">
<IoCloudUploadOutline className="h-5 w-5" />
Expand All @@ -233,7 +233,7 @@ export default function CloudDocsPage() {
: "text-gray-500 hover:text-gray-900"
}`}
>
<span>我的文档</span>
<span>My Documents</span>
</button>
<button
onClick={() => setActiveTab("shared")}
Expand All @@ -259,7 +259,7 @@ export default function CloudDocsPage() {
</div>

{loading ? (
<div className="text-center py-12 text-gray-500">加载中...</div>
<div className="text-center py-12 text-gray-500">Loading...</div>
) : error ? (
<div className="text-center py-12 text-red-500">{error}</div>
) : documents && documents.items.length > 0 ? (
Expand All @@ -283,7 +283,7 @@ export default function CloudDocsPage() {
<div>
<h3 className="font-medium text-gray-900">{doc.title}</h3>
<p className="text-sm text-gray-500 mt-0.5">
创建于 {formatDate(doc.createdAt)}
Created {formatDate(doc.createdAt)}
</p>
</div>
</div>
Expand All @@ -303,7 +303,7 @@ export default function CloudDocsPage() {
{activeTab !== "shared" && (
<button
type="button"
title="删除文档"
title="Delete document"
disabled={deletingDocId === doc.id}
onClick={(e) => {
e.stopPropagation();
Expand All @@ -328,7 +328,7 @@ export default function CloudDocsPage() {
</div>
) : (
<div className="text-center py-12 text-gray-400">
暂无文档,点击「新建」创建一个吧
No documents yet, click "New" to create one
</div>
)}

Expand All @@ -340,7 +340,7 @@ export default function CloudDocsPage() {
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
<p className="text-sm text-gray-500">正在创建文档...</p>
<p className="text-sm text-gray-500">Creating document...</p>
</div>
</div>
)}
Expand Down
Loading
Loading