修复工单操作权限判断并补充路由回归
This commit is contained in:
1 parent
42dfead9cd
commit
1fe68cc8dd
2 files changed
+140
-20
No files matched your search
@@ -186,6 +186,7 @@ test(
|
||||
attachment: [],
|
||||
replies: [],
|
||||
status: "待回复",
|
||||
can_operate: 1,
|
||||
create_time: 1730000000,
|
||||
username: "audit-user",
|
||||
client_id: 1,
|
||||
@@ -240,7 +241,9 @@ test(
|
||||
return fulfillApi(route, {});
|
||||
}
|
||||
if (request.method() === "PUT" && match[2] === "close") {
|
||||
ticket.status = "已关闭";
|
||||
// 展示状态允许后台自定义,操作权限由独立字段收回。
|
||||
ticket.status = "处理完毕";
|
||||
ticket.can_operate = 0;
|
||||
return fulfillApi(route, {});
|
||||
}
|
||||
return route.fallback();
|
||||
@@ -286,6 +289,22 @@ test(
|
||||
await page
|
||||
.getByRole("button", { name: "关闭工单", exact: true })
|
||||
.waitFor({ state: "hidden" });
|
||||
assert.equal(await editor.getAttribute("contenteditable"), "false");
|
||||
assert.equal(await page.locator("#reply-files").isDisabled(), true);
|
||||
assert.equal(
|
||||
await page
|
||||
.getByRole("button", { name: "提交回复", exact: true })
|
||||
.isDisabled(),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
await page.getByRole("button", { name: "引用", exact: true }).count(),
|
||||
0,
|
||||
);
|
||||
assert.equal(
|
||||
await page.getByRole("button", { name: "催单", exact: true }).count(),
|
||||
0,
|
||||
);
|
||||
const writes = requests.filter((request) => request.method !== "GET");
|
||||
assert.deepEqual(
|
||||
writes.map(({ method, path }) => ({ method, path })),
|
||||
@@ -310,6 +329,109 @@ test(
|
||||
previousDraftDiscarded: true,
|
||||
writes: writes.map(({ method, path }) => ({ method, path })),
|
||||
previousTicketUnchanged: true,
|
||||
permissionRevocationDisablesActions: true,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
// 补齐既有工单场景的权限空隙:只读工单在动态详情和旧菜单路由中都不能从展示文案推断可操作。
|
||||
for (const [routePath, status] of [
|
||||
["/tickets/2", "处理中"],
|
||||
["/tickets/ticket?id=2", "服务已结束"],
|
||||
]) {
|
||||
test(
|
||||
`ticket can_operate denies writes at ${routePath} with custom display status`,
|
||||
{ timeout: 25000 },
|
||||
(t) =>
|
||||
scenario(t, { loginMode: "password-only" }, async (page) => {
|
||||
const writes = [];
|
||||
const ticket = {
|
||||
id: 2,
|
||||
ticket_num: "AUDIT-2",
|
||||
title: "只读工单",
|
||||
content: "<p>历史工单仍可阅读</p>",
|
||||
status,
|
||||
can_operate: 0,
|
||||
create_time: 1730000000,
|
||||
username: "audit-user",
|
||||
client_id: 1,
|
||||
color: "#777",
|
||||
attachment: [],
|
||||
replies: [
|
||||
{
|
||||
id: 10,
|
||||
content: "<p>历史客服回复</p>",
|
||||
attachment: [],
|
||||
create_time: 1730000001,
|
||||
type: "Admin",
|
||||
admin_name: "客服",
|
||||
quote_reply_id: 0,
|
||||
quote_info: null,
|
||||
},
|
||||
],
|
||||
};
|
||||
await page.route(
|
||||
(url) =>
|
||||
url.origin === backendOrigin &&
|
||||
(url.pathname.startsWith("/console/v1/ticket/") ||
|
||||
url.pathname === "/console/v1/upload"),
|
||||
async (route) => {
|
||||
const request = route.request();
|
||||
const path = new URL(request.url()).pathname.replace(
|
||||
"/console/v1",
|
||||
"",
|
||||
);
|
||||
if (path === "/ticket/config")
|
||||
return fulfillApi(route, { ticket_notice_open: 0 });
|
||||
if (request.method() === "GET" && path === "/ticket/2")
|
||||
return fulfillApi(route, { ticket });
|
||||
if (["POST", "PUT"].includes(request.method())) {
|
||||
writes.push({ method: request.method(), path });
|
||||
return fulfillApi(route, { save_name: "unused-attachment.txt" });
|
||||
}
|
||||
return route.fallback();
|
||||
},
|
||||
);
|
||||
await login(page, routePath);
|
||||
await page
|
||||
.getByRole("heading", { name: ticket.title, exact: true })
|
||||
.waitFor();
|
||||
assert.equal(
|
||||
await page.getByText("历史客服回复", { exact: true }).isVisible(),
|
||||
true,
|
||||
);
|
||||
const editor = page.getByRole("textbox", {
|
||||
name: "回复内容",
|
||||
exact: true,
|
||||
});
|
||||
assert.equal(await editor.getAttribute("contenteditable"), "false");
|
||||
assert.equal(await page.locator("#reply-files").isDisabled(), true);
|
||||
assert.equal(
|
||||
await page
|
||||
.getByRole("button", { name: "提交回复", exact: true })
|
||||
.isDisabled(),
|
||||
true,
|
||||
);
|
||||
for (const name of ["引用", "催单", "关闭工单"])
|
||||
assert.equal(
|
||||
await page.getByRole("button", { name, exact: true }).count(),
|
||||
0,
|
||||
);
|
||||
// 文件输入收到浏览器 change 事件时,处理函数也必须遵守权限;仅修改 HTTP 外部边界。
|
||||
await page.locator("#reply-files").setInputFiles({
|
||||
name: "read-only-ticket.txt",
|
||||
mimeType: "text/plain",
|
||||
buffer: Buffer.from("local permission regression"),
|
||||
});
|
||||
assert.deepEqual(writes, []);
|
||||
return {
|
||||
routePath,
|
||||
displayStatus: status,
|
||||
serverCanOperate: ticket.can_operate,
|
||||
historicalRepliesReadable: true,
|
||||
editingAndWritesBlocked: true,
|
||||
writes,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user