Skip to content

Commit 4b16545

Browse files
tommoorclaude
andauthored
Fix Comment.toPlainText using wrong schema for mention nodes (outline#11889)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 27dc02a commit 4b16545

File tree

2 files changed

+191
-2
lines changed

2 files changed

+191
-2
lines changed

server/models/Comment.test.ts

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { v4 as uuidv4 } from "uuid";
2+
import { MentionType } from "@shared/types";
3+
import { buildComment, buildDocument, buildUser } from "@server/test/factories";
4+
import Comment from "./Comment";
5+
6+
describe("Comment", () => {
7+
describe("toPlainText", () => {
8+
it("should convert simple text to plain text", async () => {
9+
const user = await buildUser();
10+
const document = await buildDocument({
11+
userId: user.id,
12+
teamId: user.teamId,
13+
});
14+
const comment = await buildComment({
15+
userId: user.id,
16+
documentId: document.id,
17+
});
18+
19+
const text = comment.toPlainText();
20+
expect(text).toBe("test");
21+
});
22+
23+
it("should convert comment with mention to plain text", async () => {
24+
const user = await buildUser();
25+
const document = await buildDocument({
26+
userId: user.id,
27+
teamId: user.teamId,
28+
});
29+
const comment = await Comment.create({
30+
documentId: document.id,
31+
createdById: user.id,
32+
data: {
33+
type: "doc",
34+
content: [
35+
{
36+
type: "paragraph",
37+
content: [
38+
{
39+
type: "text",
40+
text: "Hello ",
41+
},
42+
{
43+
type: "mention",
44+
attrs: {
45+
type: MentionType.User,
46+
label: "Jane",
47+
modelId: uuidv4(),
48+
id: uuidv4(),
49+
},
50+
},
51+
],
52+
},
53+
],
54+
},
55+
});
56+
57+
const text = comment.toPlainText();
58+
expect(text).toBe("Hello @Jane");
59+
});
60+
61+
it("should convert comment with document mention to plain text", async () => {
62+
const user = await buildUser();
63+
const document = await buildDocument({
64+
userId: user.id,
65+
teamId: user.teamId,
66+
});
67+
const comment = await Comment.create({
68+
documentId: document.id,
69+
createdById: user.id,
70+
data: {
71+
type: "doc",
72+
content: [
73+
{
74+
type: "paragraph",
75+
content: [
76+
{
77+
type: "text",
78+
text: "See ",
79+
},
80+
{
81+
type: "mention",
82+
attrs: {
83+
type: MentionType.Document,
84+
label: "My Document",
85+
modelId: uuidv4(),
86+
id: uuidv4(),
87+
},
88+
},
89+
],
90+
},
91+
],
92+
},
93+
});
94+
95+
const text = comment.toPlainText();
96+
expect(text).toBe("See My Document");
97+
});
98+
});
99+
100+
describe("resolve", () => {
101+
it("should resolve the comment", async () => {
102+
const user = await buildUser();
103+
const document = await buildDocument({
104+
userId: user.id,
105+
teamId: user.teamId,
106+
});
107+
const comment = await buildComment({
108+
userId: user.id,
109+
documentId: document.id,
110+
});
111+
112+
comment.resolve(user);
113+
114+
expect(comment.isResolved).toBe(true);
115+
expect(comment.resolvedById).toBe(user.id);
116+
expect(comment.resolvedAt).toBeTruthy();
117+
});
118+
119+
it("should throw if already resolved", async () => {
120+
const user = await buildUser();
121+
const document = await buildDocument({
122+
userId: user.id,
123+
teamId: user.teamId,
124+
});
125+
const comment = await buildComment({
126+
userId: user.id,
127+
documentId: document.id,
128+
});
129+
130+
comment.resolve(user);
131+
132+
expect(() => comment.resolve(user)).toThrow();
133+
});
134+
135+
it("should throw if comment is a reply", async () => {
136+
const user = await buildUser();
137+
const document = await buildDocument({
138+
userId: user.id,
139+
teamId: user.teamId,
140+
});
141+
const parent = await buildComment({
142+
userId: user.id,
143+
documentId: document.id,
144+
});
145+
const reply = await buildComment({
146+
userId: user.id,
147+
documentId: document.id,
148+
parentCommentId: parent.id,
149+
});
150+
151+
expect(() => reply.resolve(user)).toThrow();
152+
});
153+
});
154+
155+
describe("unresolve", () => {
156+
it("should unresolve the comment", async () => {
157+
const user = await buildUser();
158+
const document = await buildDocument({
159+
userId: user.id,
160+
teamId: user.teamId,
161+
});
162+
const comment = await buildComment({
163+
userId: user.id,
164+
documentId: document.id,
165+
});
166+
167+
comment.resolve(user);
168+
comment.unresolve();
169+
170+
expect(comment.isResolved).toBe(false);
171+
expect(comment.resolvedById).toBeNull();
172+
expect(comment.resolvedAt).toBeNull();
173+
});
174+
175+
it("should throw if not resolved", async () => {
176+
const user = await buildUser();
177+
const document = await buildDocument({
178+
userId: user.id,
179+
teamId: user.teamId,
180+
});
181+
const comment = await buildComment({
182+
userId: user.id,
183+
documentId: document.id,
184+
});
185+
186+
expect(() => comment.unresolve()).toThrow();
187+
});
188+
});
189+
});

server/models/Comment.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
import type { ProsemirrorData, ReactionSummary } from "@shared/types";
1414
import { ProsemirrorHelper } from "@shared/utils/ProsemirrorHelper";
1515
import { CommentValidation } from "@shared/validations";
16-
import { basicSchema } from "@server/editor";
16+
import { commentSchema } from "@server/editor";
1717
import { ValidationError } from "@server/errors";
1818
import Document from "./Document";
1919
import User from "./User";
@@ -137,7 +137,7 @@ class Comment extends ParanoidModel<
137137
* @returns The plain text representation of the comment data
138138
*/
139139
public toPlainText() {
140-
const node = Node.fromJSON(basicSchema, this.data);
140+
const node = Node.fromJSON(commentSchema, this.data);
141141
return ProsemirrorHelper.toPlainText(node);
142142
}
143143

0 commit comments

Comments
 (0)