mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 14:23:13 +00:00
* 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>
118 lines
3.7 KiB
TypeScript
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);
|
|
});
|
|
});
|