نسخهٔ وب مستقل — پلتفرم نیروی کار هوش مصنوعی

مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت.
تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
This commit is contained in:
mjalmoudy94
2026-10-13 00:39:28 +03:30
commit 717e53a7ce
185 changed files with 39910 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { ApiError, api, apiRequest, getStoredToken, setStoredToken } from "@/lib/api";
describe("token storage", () => {
it("round-trips a token through localStorage", () => {
setStoredToken("abc123");
expect(getStoredToken()).toBe("abc123");
setStoredToken(null);
expect(getStoredToken()).toBeNull();
});
});
describe("ApiError", () => {
it("carries status, code and message", () => {
const error = new ApiError(404, "not_found", "سازمان یافت نشد.");
expect(error.status).toBe(404);
expect(error.code).toBe("not_found");
expect(error.message).toBe("سازمان یافت نشد.");
expect(error).toBeInstanceOf(Error);
});
it("carries the server's request id so a bug report is actionable", () => {
const error = new ApiError(500, "internal_error", "خطای داخلی", undefined, "req-42");
expect(error.requestId).toBe("req-42");
});
});
describe("error correlation", () => {
afterEach(() => {
vi.unstubAllGlobals();
});
it("reads the request id out of the error envelope", async () => {
vi.stubGlobal(
"fetch",
vi.fn(async () =>
new Response(
JSON.stringify({
error: { code: "internal_error", message: "خطا", request_id: "req-from-body" },
}),
{ status: 500, headers: { "Content-Type": "application/json" } },
),
),
);
await expect(apiRequest("/api/whatever")).rejects.toMatchObject({
requestId: "req-from-body",
status: 500,
});
});
it("falls back to the X-Request-ID header", async () => {
// A failure raised outside the error handlers — a CORS rejection or a proxy
// error — still carries the header, and the user should still get an id.
vi.stubGlobal(
"fetch",
vi.fn(async () =>
new Response(JSON.stringify({ detail: "Disallowed CORS origin" }), {
status: 400,
headers: { "X-Request-ID": "req-from-header" },
}),
),
);
await expect(apiRequest("/api/whatever")).rejects.toMatchObject({
requestId: "req-from-header",
});
});
});
describe("error console client", () => {
afterEach(() => {
vi.unstubAllGlobals();
});
it("omits unset filters from the query string", async () => {
const calls: string[] = [];
vi.stubGlobal(
"fetch",
vi.fn(async (url: string) => {
calls.push(url);
return new Response(JSON.stringify({ items: [], total: 0, limit: 50, offset: 0 }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}),
);
await api.adminErrors({ status: "open", severity: "critical" });
expect(calls[0]).toContain("status=open");
expect(calls[0]).toContain("severity=critical");
// An empty string means "all", and must not be sent as a filter value.
expect(calls[0]).not.toContain("source=");
expect(calls[0]).not.toContain("search=");
});
it("sends no query string at all when there are no filters", async () => {
const calls: string[] = [];
vi.stubGlobal(
"fetch",
vi.fn(async (url: string) => {
calls.push(url);
return new Response(JSON.stringify([]), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}),
);
await api.adminErrorGroups();
expect(calls[0]).not.toContain("?");
});
});
+148
View File
@@ -0,0 +1,148 @@
import { describe, expect, it, vi } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";
import {
ArtifactViewer,
Drawer,
Modal,
Table,
Tabs,
ToastStack,
type TableColumn,
} from "@agy/ui";
interface Row {
id: string;
name: string;
}
const columns: Array<TableColumn<Row>> = [
{ key: "name", header: "نام", render: (row) => row.name },
];
describe("Table", () => {
it("renders a row per record", () => {
render(
<Table
columns={columns}
rows={[{ id: "1", name: "کارمند الف" }, { id: "2", name: "کارمند ب" }]}
rowKey={(row) => row.id}
/>,
);
expect(screen.getByRole("columnheader", { name: "نام" })).toBeInTheDocument();
expect(screen.getAllByRole("row")).toHaveLength(3); // header + 2 body rows
});
it("shows the empty state instead of an empty table body", () => {
render(<Table columns={columns} rows={[]} rowKey={(row) => row.id} />);
expect(screen.getByText("موردی برای نمایش نیست.")).toBeInTheDocument();
expect(screen.queryByRole("table")).not.toBeInTheDocument();
});
it("prefers the loading state over rows", () => {
render(
<Table
columns={columns}
rows={[{ id: "1", name: "کارمند الف" }]}
rowKey={(row) => row.id}
loading
/>,
);
expect(screen.getByRole("status")).toBeInTheDocument();
expect(screen.queryByRole("table")).not.toBeInTheDocument();
});
it("prefers the error state over rows", () => {
render(
<Table
columns={columns}
rows={[{ id: "1", name: "کارمند الف" }]}
rowKey={(row) => row.id}
error="خطای شبکه"
/>,
);
expect(screen.getByRole("alert")).toBeInTheDocument();
expect(screen.queryByRole("table")).not.toBeInTheDocument();
});
});
describe("Tabs", () => {
it("marks the active tab as selected and reports clicks", () => {
const onChange = vi.fn();
render(
<Tabs
items={[
{ key: "overview", label: "نمای کلی" },
{ key: "git", label: "گیت" },
]}
active="overview"
onChange={onChange}
/>,
);
expect(screen.getByRole("tab", { name: "نمای کلی" })).toHaveAttribute(
"aria-selected",
"true",
);
fireEvent.click(screen.getByRole("tab", { name: "گیت" }));
expect(onChange).toHaveBeenCalledWith("git");
});
});
describe("Modal", () => {
it("renders nothing while closed", () => {
render(<Modal open={false} title="عنوان" onClose={() => undefined} />);
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
it("closes when the close button is pressed", () => {
const onClose = vi.fn();
render(<Modal open title="عنوان" onClose={onClose} />);
fireEvent.click(screen.getByRole("button", { name: "بستن" }));
expect(onClose).toHaveBeenCalled();
});
});
describe("Drawer", () => {
it("renders its title and children when open", () => {
render(
<Drawer open title="جزئیات" onClose={() => undefined}>
<p>محتوای کشو</p>
</Drawer>,
);
expect(screen.getByRole("dialog", { name: "جزئیات" })).toBeInTheDocument();
expect(screen.getByText("محتوای کشو")).toBeInTheDocument();
});
});
describe("ToastStack", () => {
it("renders nothing when there are no toasts", () => {
const { container } = render(<ToastStack toasts={[]} onDismiss={() => undefined} />);
expect(container).toBeEmptyDOMElement();
});
it("dismisses a toast by id", () => {
const onDismiss = vi.fn();
render(
<ToastStack
toasts={[{ id: "t1", title: "ذخیره شد", tone: "success" }]}
onDismiss={onDismiss}
/>,
);
expect(screen.getByText("ذخیره شد")).toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "بستن" }));
expect(onDismiss).toHaveBeenCalledWith("t1");
});
});
describe("ArtifactViewer", () => {
it("shows the artifact content and kind", () => {
render(<ArtifactViewer name="خروجی کار" kind="text" content="محتوای سند" />);
expect(screen.getByText("خروجی کار")).toBeInTheDocument();
expect(screen.getByText("text")).toBeInTheDocument();
expect(screen.getByText("محتوای سند")).toBeInTheDocument();
});
it("falls back to a placeholder for an empty artifact", () => {
render(<ArtifactViewer name="خالی" kind="text" content="" />);
expect(screen.getByText("محتوایی ثبت نشده است.")).toBeInTheDocument();
});
});
+1
View File
@@ -0,0 +1 @@
import "@testing-library/jest-dom/vitest";
+37
View File
@@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest";
import { render, screen } from "@testing-library/react";
import { Badge, Button, EmptyState, ErrorState, LoadingState } from "@agy/ui";
describe("design system", () => {
it("renders a button with its label", () => {
render(<Button>ورود</Button>);
expect(screen.getByRole("button", { name: "ورود" })).toBeInTheDocument();
});
it("disables the button while loading", () => {
render(<Button loading>ذخیره</Button>);
expect(screen.getByRole("button")).toBeDisabled();
});
it("announces the loading state to assistive tech", () => {
render(<LoadingState label="در حال بارگذاری…" />);
expect(screen.getByRole("status")).toBeInTheDocument();
});
it("renders an error state as an alert with a retry action", () => {
render(<ErrorState message="خطای شبکه" onRetry={() => undefined} />);
expect(screen.getByRole("alert")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "تلاش دوباره" })).toBeInTheDocument();
});
it("renders an empty state title and description", () => {
render(<EmptyState title="چیزی نیست" description="توضیح" />);
expect(screen.getByText("چیزی نیست")).toBeInTheDocument();
expect(screen.getByText("توضیح")).toBeInTheDocument();
});
it("renders a badge", () => {
render(<Badge tone="success">فعال</Badge>);
expect(screen.getByText("فعال")).toBeInTheDocument();
});
});