shoc-frontend-new/src/test/domain/work-orders/utils/comment-mentions.test.ts
Arthur Bassi f35fce9bd5
feat(work-orders): comment @mentions autocomplete (SH-217) (#91)
* docs(work-orders): add pending BE contract checklist for design parity gaps

* feat(work-orders): add mentions in work-order comments composer

* docs(work-orders): align pr description with jira and qa residuals

* refactor(workorders): split work order view page into view components

* fix(work-orders): export comment assignment vendor input types for view hook

* fix(work-orders): drop preferredContact from vendor update payload

Align with WorkOrderVendorUpdatePayload on current dev so the mentions branch typechecks.

* fix(work-orders): remove Preferred Contact from WO vendor editor (SH-180)

EOF

* fix(work-orders): harden comment mentions and clear draft on success

* fix(work-orders): harden mention highlight boundary and submit catch [SH-217]

* fix(work-orders): keep mention cursor sync on current value [SH-217]

* fix(work-orders): keep mention menu open after typing @

Pass live text into syncCursor instead of a render-time valueRef.
Add a composer interaction regression for @ autocomplete visibility.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
2026-08-11 14:07:02 -03:00

118 lines
3.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
buildCommentBodySegments,
encodeMentionToken,
extractMentionIds,
filterMentionCandidates,
findActiveMentionQuery,
insertMentionToken,
toDisplayText,
} from "@/domain/work-orders/utils/comment-mentions";
describe("comment-mentions", () => {
it("encodes and extracts mention ids from tokens", () => {
const token = encodeMentionToken("user-1", "Jane Doe");
const text = `Please review ${token} today`;
expect(text).toBe("Please review @[user-1:Jane Doe] today");
expect(extractMentionIds(text)).toEqual(["user-1"]);
expect(toDisplayText(text)).toBe("Please review @Jane Doe today");
});
it("builds highlight segments from encoded tokens", () => {
const text = `Hi ${encodeMentionToken("42", "Alex Smith")}!`;
expect(buildCommentBodySegments(text)).toEqual([
{ type: "text", value: "Hi " },
{ type: "mention", userId: "42", label: "Alex Smith" },
{ type: "text", value: "!" },
]);
});
it("highlights plain @names when response mentions are provided", () => {
const text = "Ping @Jane Doe about this";
expect(
buildCommentBodySegments(text, ["user-1"], [{ id: "user-1", name: "Jane Doe" }]),
).toEqual([
{ type: "text", value: "Ping " },
{ type: "mention", userId: "Jane Doe", label: "Jane Doe" },
{ type: "text", value: " about this" },
]);
});
it("does not highlight casual @names from the full user roster without mentions", () => {
const text = "See @Jane Doe later";
expect(buildCommentBodySegments(text, undefined, [{ id: "user-1", name: "Jane Doe" }])).toEqual(
[{ type: "text", value: "See @Jane Doe later" }],
);
});
it("highlights plain @names before encoded tokens when mentioned", () => {
const encoded = encodeMentionToken("2", "Bob");
const text = `Hi @Jane Doe and ${encoded}`;
expect(
buildCommentBodySegments(
text,
["user-1", "2"],
[
{ id: "user-1", name: "Jane Doe" },
{ id: "2", name: "Bob" },
],
),
).toEqual([
{ type: "text", value: "Hi " },
{ type: "mention", userId: "Jane Doe", label: "Jane Doe" },
{ type: "text", value: " and " },
{ type: "mention", userId: "2", label: "Bob" },
]);
});
it("does not highlight shorter mention names as prefixes of longer @words", () => {
const text = "Check @Annual maintenance with @Ann";
expect(buildCommentBodySegments(text, ["user-ann"], [{ id: "user-ann", name: "Ann" }])).toEqual(
[
{ type: "text", value: "Check @Annual maintenance with " },
{ type: "mention", userId: "Ann", label: "Ann" },
],
);
});
it("finds active mention query at cursor", () => {
const text = "Hello @ja";
const query = findActiveMentionQuery(text, text.length);
expect(query).toEqual({ start: 6, query: "ja" });
expect(findActiveMentionQuery("email@test.com", 13)).toBeNull();
});
it("filters mention candidates by query", () => {
const candidates = [
{ id: "1", name: "Jane Doe" },
{ id: "2", name: "John Smith" },
];
expect(filterMentionCandidates(candidates, "jo")).toEqual([{ id: "2", name: "John Smith" }]);
});
it("inserts encoded mention token replacing active query", () => {
const text = "Hello @ja";
const activeQuery = findActiveMentionQuery(text, text.length);
expect(activeQuery).not.toBeNull();
if (!activeQuery) {
return;
}
const { nextText, nextCursor } = insertMentionToken(text, activeQuery.start, text.length, {
id: "1",
name: "Jane Doe",
});
expect(nextText).toBe("Hello @[1:Jane Doe] ");
expect(nextCursor).toBe(nextText.length);
});
});