Закрыты задачи бэклога по неочевидному поведению UI при ошибках отправки сообщений и блокировках со стороны Message-safety + добалено ограничение на размер сообщения
This commit is contained in:
@@ -23,6 +23,7 @@ test.beforeEach(async ({ page }) => {
|
||||
json: {
|
||||
auth: { phone_enabled: true, password_enabled: false },
|
||||
operator: { call_phone: "+74950000000" },
|
||||
messages: { max_text_length: 4000 },
|
||||
ux: { idle_timeout_minutes: 15 },
|
||||
attachments: { max_size_mb: 5, allowed_extensions: ["png", "pdf"], allowed_mime_types: ["image/png", "application/pdf"] },
|
||||
consents: {
|
||||
@@ -86,11 +87,11 @@ test("Enter отправляет введённое сообщение", async (
|
||||
await expect(page.getByRole("heading", { name: "Перед началом работы" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("сообщение ограничено 4000 символами", async ({ page }) => {
|
||||
test("счётчик показывает и ограничивает 4000 символов", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByLabel("Сообщение").fill("а".repeat(4001));
|
||||
await page.getByRole("button", { name: "Отправить" }).click();
|
||||
await expect(page.getByRole("alert")).toContainText("Максимум — 4000 символов");
|
||||
await expect(page.getByText("4001/4000")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Отправить" })).toBeDisabled();
|
||||
await expect(page.getByRole("heading", { name: "Перед началом работы" })).toHaveCount(0);
|
||||
});
|
||||
|
||||
|
||||
@@ -24,14 +24,21 @@ vi.mock("react-native", () => ({
|
||||
}));
|
||||
|
||||
import { buildOidcDeviceMetadata } from "../../src/oidc-device";
|
||||
import { ApiError, isMessageBlockedError } from "../../src/api";
|
||||
import { messageFitsLimit, normalizeMessageText } from "../../src/message-text";
|
||||
import { reconcileMessages } from "../../src/reconcile";
|
||||
import { sessionMemory } from "../../src/session";
|
||||
import { SingleFlight } from "../../src/single-flight";
|
||||
import { websocketJwtProtocol } from "../../src/realtime";
|
||||
import {
|
||||
clearPendingTextIntent,
|
||||
bindPendingFileIntent,
|
||||
bindPendingTextIntent,
|
||||
clearPendingFileIntent,
|
||||
createPendingTextIntent,
|
||||
loadPendingFileIntent,
|
||||
loadPendingTextIntent,
|
||||
savePendingFileIntent,
|
||||
savePendingTextIntent,
|
||||
} from "../../src/pending-intent";
|
||||
import type { Message } from "../../src/types";
|
||||
@@ -94,6 +101,33 @@ describe("pending message intent", () => {
|
||||
expect(loadPendingTextIntent()).toBeNull();
|
||||
clearPendingTextIntent();
|
||||
});
|
||||
|
||||
it("привязывает отложенный текст и файл к созданному диалогу", () => {
|
||||
const textIntent = createPendingTextIntent("После входа");
|
||||
bindPendingTextIntent(textIntent, "dialog-1");
|
||||
const file = new File(["test"], "test.txt", { type: "text/plain" });
|
||||
savePendingFileIntent({ file, dialogKey: "file-dialog-key", messageKey: "file-key" });
|
||||
bindPendingFileIntent("dialog-1");
|
||||
|
||||
expect(loadPendingTextIntent()?.dialogId).toBe("dialog-1");
|
||||
expect(loadPendingFileIntent("dialog-1")?.file).toBe(file);
|
||||
|
||||
clearPendingTextIntent();
|
||||
clearPendingFileIntent();
|
||||
});
|
||||
});
|
||||
|
||||
describe("message UX rules", () => {
|
||||
it("нормализует текст и проверяет динамический лимит", () => {
|
||||
expect(normalizeMessageText(" A ")).toBe("A");
|
||||
expect(messageFitsLimit("1234", 4)).toBe(true);
|
||||
expect(messageFitsLimit("12345", 4)).toBe(false);
|
||||
});
|
||||
|
||||
it("отличает блокировку message-safety от технической ошибки", () => {
|
||||
expect(isMessageBlockedError(new ApiError(422, "message_blocked", "blocked"))).toBe(true);
|
||||
expect(isMessageBlockedError(new ApiError(503, "dependency_unavailable", "failed"))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("SingleFlight", () => {
|
||||
|
||||
Reference in New Issue
Block a user