feat: enhance order asset selection and previews
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { test, expect } from "vitest";
|
||||
|
||||
import { OrderDetail } from "@/features/orders/order-detail";
|
||||
@@ -97,3 +97,140 @@ test("renders the business-empty final-result state when no final asset exists",
|
||||
expect(screen.getByText("最终图暂未生成")).toBeInTheDocument();
|
||||
expect(screen.getByText("当前订单还没有可展示的最终结果。")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders prepared-model input snapshots when asset metadata contains resource inputs", () => {
|
||||
render(
|
||||
<OrderDetail
|
||||
viewModel={{
|
||||
...BASE_ORDER_DETAIL,
|
||||
assets: [
|
||||
{
|
||||
id: 66,
|
||||
orderId: 101,
|
||||
type: "prepared_model",
|
||||
stepName: "prepare_model",
|
||||
parentAssetId: null,
|
||||
rootAssetId: null,
|
||||
versionNo: 0,
|
||||
isCurrentVersion: false,
|
||||
stepLabel: "模型准备",
|
||||
label: "模型准备产物",
|
||||
uri: "https://images.example.com/prepared-model.jpg",
|
||||
metadata: {
|
||||
model_input: {
|
||||
resource_id: 3,
|
||||
resource_name: "主模特",
|
||||
original_url: "https://images.example.com/model.jpg",
|
||||
},
|
||||
garment_input: {
|
||||
resource_id: 4,
|
||||
resource_name: "上衣",
|
||||
original_url: "https://images.example.com/garment.jpg",
|
||||
},
|
||||
scene_input: {
|
||||
resource_id: 5,
|
||||
resource_name: "白棚",
|
||||
original_url: "https://images.example.com/scene.jpg",
|
||||
},
|
||||
},
|
||||
createdAt: "2026-03-27T00:08:00Z",
|
||||
isMock: false,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("输入素材")).toBeInTheDocument();
|
||||
expect(screen.getByText("模特图")).toBeInTheDocument();
|
||||
expect(screen.getByText("服装图")).toBeInTheDocument();
|
||||
expect(screen.getByText("场景图")).toBeInTheDocument();
|
||||
expect(screen.getByText("主模特")).toBeInTheDocument();
|
||||
expect(screen.getByText("上衣")).toBeInTheDocument();
|
||||
expect(screen.getByText("白棚")).toBeInTheDocument();
|
||||
expect(screen.getByAltText("模型准备产物预览")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders image previews for non-mock final and process assets", () => {
|
||||
render(
|
||||
<OrderDetail
|
||||
viewModel={{
|
||||
...BASE_ORDER_DETAIL,
|
||||
hasMockAssets: false,
|
||||
finalAsset: {
|
||||
...BASE_ORDER_DETAIL.finalAsset!,
|
||||
uri: "https://images.example.com/final.jpg",
|
||||
isMock: false,
|
||||
},
|
||||
assets: [
|
||||
{
|
||||
id: 78,
|
||||
orderId: 101,
|
||||
type: "tryon",
|
||||
stepName: "tryon",
|
||||
parentAssetId: null,
|
||||
rootAssetId: null,
|
||||
versionNo: 0,
|
||||
isCurrentVersion: false,
|
||||
stepLabel: "试穿生成",
|
||||
label: "试穿生成产物",
|
||||
uri: "https://images.example.com/tryon.jpg",
|
||||
metadata: null,
|
||||
createdAt: "2026-03-27T00:09:00Z",
|
||||
isMock: false,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByAltText("最终图预览")).toBeInTheDocument();
|
||||
expect(screen.getByAltText("试穿生成产物预览")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("opens an asset preview dialog for real images and resets zoom state", () => {
|
||||
render(
|
||||
<OrderDetail
|
||||
viewModel={{
|
||||
...BASE_ORDER_DETAIL,
|
||||
hasMockAssets: false,
|
||||
finalAsset: {
|
||||
...BASE_ORDER_DETAIL.finalAsset!,
|
||||
uri: "https://images.example.com/final.jpg",
|
||||
isMock: false,
|
||||
},
|
||||
assets: [
|
||||
{
|
||||
id: 78,
|
||||
orderId: 101,
|
||||
type: "scene",
|
||||
stepName: "scene",
|
||||
parentAssetId: null,
|
||||
rootAssetId: null,
|
||||
versionNo: 0,
|
||||
isCurrentVersion: false,
|
||||
stepLabel: "场景处理",
|
||||
label: "场景处理产物",
|
||||
uri: "https://images.example.com/scene.jpg",
|
||||
metadata: null,
|
||||
createdAt: "2026-03-27T00:09:00Z",
|
||||
isMock: false,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "查看场景处理产物大图" }));
|
||||
|
||||
expect(screen.getByRole("dialog", { name: "场景处理产物预览" })).toBeInTheDocument();
|
||||
|
||||
const zoomableImage = screen.getByAltText("场景处理产物大图");
|
||||
fireEvent.wheel(zoomableImage, { deltaY: -120 });
|
||||
|
||||
expect(zoomableImage).toHaveStyle({ transform: "translate(0px, 0px) scale(1.12)" });
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "重置预览" }));
|
||||
|
||||
expect(zoomableImage).toHaveStyle({ transform: "translate(0px, 0px) scale(1)" });
|
||||
});
|
||||
|
||||
@@ -84,6 +84,19 @@ async function chooseSelectOption(label: string, optionName: string) {
|
||||
fireEvent.click(await screen.findByRole("option", { name: optionName }));
|
||||
}
|
||||
|
||||
async function chooseLibraryResource(label: string, resourceName: string) {
|
||||
fireEvent.click(screen.getByRole("button", { name: `选择${label}` }));
|
||||
|
||||
const dialog = await screen.findByRole("dialog", { name: `选择${label}` });
|
||||
fireEvent.click(within(dialog).getByRole("button", { name: resourceName }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByRole("dialog", { name: `选择${label}` }),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
pushMock.mockReset();
|
||||
});
|
||||
@@ -97,7 +110,7 @@ test("forces low customers to use auto_basic and mid customers to use semi_pro",
|
||||
|
||||
render(<SubmitWorkbench />);
|
||||
|
||||
await screen.findByText("Ava / Studio");
|
||||
await screen.findByRole("button", { name: "选择模特资源" });
|
||||
|
||||
await chooseSelectOption("客户层级", "低客单 low");
|
||||
|
||||
@@ -142,12 +155,12 @@ test("preserves selected values when order submission fails", async () => {
|
||||
|
||||
render(<SubmitWorkbench />);
|
||||
|
||||
await screen.findByText("Ava / Studio");
|
||||
await screen.findByRole("button", { name: "选择模特资源" });
|
||||
|
||||
await chooseSelectOption("客户层级", "低客单 low");
|
||||
await chooseSelectOption("模特资源", "Ava / Studio");
|
||||
await chooseSelectOption("场景资源", "Loft Window");
|
||||
await chooseSelectOption("服装资源", "Structured Coat 01");
|
||||
await chooseLibraryResource("模特资源", "Ava / Studio");
|
||||
await chooseLibraryResource("场景资源", "Loft Window");
|
||||
await chooseLibraryResource("服装资源", "Structured Coat 01");
|
||||
|
||||
const summaryCard = screen.getByRole("region", { name: "提单摘要" });
|
||||
expect(within(summaryCard).getByText("Ava / Studio")).toBeInTheDocument();
|
||||
@@ -165,15 +178,9 @@ test("preserves selected values when order submission fails", async () => {
|
||||
expect(screen.getByRole("combobox", { name: "服务模式" })).toHaveTextContent(
|
||||
"自动基础处理 auto_basic",
|
||||
);
|
||||
expect(screen.getByRole("combobox", { name: "模特资源" })).toHaveTextContent(
|
||||
"Ava / Studio",
|
||||
);
|
||||
expect(screen.getByRole("combobox", { name: "场景资源" })).toHaveTextContent(
|
||||
"Loft Window",
|
||||
);
|
||||
expect(screen.getByRole("combobox", { name: "服装资源" })).toHaveTextContent(
|
||||
"Structured Coat 01",
|
||||
);
|
||||
expect(screen.getAllByText("Ava / Studio").length).toBeGreaterThan(1);
|
||||
expect(screen.getAllByText("Loft Window").length).toBeGreaterThan(1);
|
||||
expect(screen.getAllByText("Structured Coat 01").length).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
test("submits the selected resources, surfaces returned ids, and redirects to the order detail page", async () => {
|
||||
@@ -198,12 +205,12 @@ test("submits the selected resources, surfaces returned ids, and redirects to th
|
||||
|
||||
render(<SubmitWorkbench />);
|
||||
|
||||
await screen.findByText("Ava / Studio");
|
||||
await screen.findByRole("button", { name: "选择模特资源" });
|
||||
|
||||
await chooseSelectOption("客户层级", "低客单 low");
|
||||
await chooseSelectOption("模特资源", "Ava / Studio");
|
||||
await chooseSelectOption("场景资源", "Loft Window");
|
||||
await chooseSelectOption("服装资源", "Structured Coat 01");
|
||||
await chooseLibraryResource("模特资源", "Ava / Studio");
|
||||
await chooseLibraryResource("场景资源", "Loft Window");
|
||||
await chooseLibraryResource("服装资源", "Structured Coat 01");
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "提交订单" }));
|
||||
|
||||
@@ -232,3 +239,78 @@ test("submits the selected resources, surfaces returned ids, and redirects to th
|
||||
expect(pushMock).toHaveBeenCalledWith("/orders/77");
|
||||
});
|
||||
});
|
||||
|
||||
test("allows submitting without selecting a scene resource", async () => {
|
||||
const fetchMock = createFetchMock({
|
||||
orderResponse: new Response(
|
||||
JSON.stringify({
|
||||
mode: "proxy",
|
||||
data: {
|
||||
orderId: 88,
|
||||
workflowId: "wf-88",
|
||||
status: "created",
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: 201,
|
||||
headers: { "content-type": "application/json" },
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
render(<SubmitWorkbench />);
|
||||
|
||||
await screen.findByRole("button", { name: "选择模特资源" });
|
||||
|
||||
await chooseSelectOption("客户层级", "低客单 low");
|
||||
await chooseLibraryResource("模特资源", "Ava / Studio");
|
||||
await chooseLibraryResource("服装资源", "Structured Coat 01");
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "提交订单" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
"/api/orders",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
customer_level: "low",
|
||||
service_mode: "auto_basic",
|
||||
model_id: 101,
|
||||
garment_asset_id: 303,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("opens a shared resource manager modal and picks a resource card for each library type", async () => {
|
||||
vi.stubGlobal("fetch", createFetchMock());
|
||||
|
||||
render(<SubmitWorkbench />);
|
||||
|
||||
await screen.findByRole("button", { name: "选择模特资源" });
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "选择模特资源" }));
|
||||
|
||||
const modelDialog = await screen.findByRole("dialog", { name: "选择模特资源" });
|
||||
expect(within(modelDialog).getByTestId("resource-picker-masonry").className).toContain(
|
||||
"columns-1",
|
||||
);
|
||||
expect(
|
||||
within(modelDialog).getByRole("button", { name: "Ava / Studio" }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(within(modelDialog).getByRole("button", { name: "Ava / Studio" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByRole("dialog", { name: "选择模特资源" }),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
const summaryCard = screen.getByRole("region", { name: "提单摘要" });
|
||||
expect(within(summaryCard).getByText("Ava / Studio")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user