Skip to content

Commit 3313568

Browse files
authored
fix(tests): replace DOM highlight polling with PM state checks to fix WebKit flakes (#2367)
Comment highlight tests were flaky on WebKit CI because assertCommentHighlightExists polls for .superdoc-comment-highlight DOM elements, which depend on the full render pipeline completing within 20-25s. Replaced with assertMarksAtPos checks against ProseMirror state, which is immediate and engine-independent.
1 parent 72077b2 commit 3313568

2 files changed

Lines changed: 43 additions & 57 deletions

File tree

tests/behavior/tests/comments/comment-mark-non-inclusive.spec.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ test.describe('comment mark non-inclusive boundary', () => {
1313
await superdoc.assertTextContains('Commented');
1414

1515
// 2. Add a comment on the word "Commented"
16-
const commentId = await addCommentByText(superdoc.page, {
16+
await addCommentByText(superdoc.page, {
1717
pattern: 'Commented',
1818
text: 'Test comment',
1919
});
2020
await superdoc.waitForStable();
21-
await superdoc.assertCommentHighlightExists({ text: 'Commented', commentId, timeoutMs: 20_000 });
2221

23-
// 3. Place cursor right after the commented text and type new text
22+
// Verify comment mark exists in PM state (avoids slow DOM highlight polling on WebKit)
2423
const pos = await superdoc.findTextPos('Commented');
24+
await superdoc.assertMarksAtPos(pos, ['commentMark']);
25+
26+
// 3. Place cursor right after the commented text and type new text
2527
await superdoc.setTextSelection(pos + 'Commented'.length);
2628
await superdoc.waitForStable();
2729

@@ -52,15 +54,17 @@ test.describe('comment mark non-inclusive boundary', () => {
5254
await superdoc.waitForStable();
5355

5456
// 2. Add a comment on the word "Commented"
55-
const commentId = await addCommentByText(superdoc.page, {
57+
await addCommentByText(superdoc.page, {
5658
pattern: 'Commented',
5759
text: 'Test comment',
5860
});
5961
await superdoc.waitForStable();
60-
await superdoc.assertCommentHighlightExists({ text: 'Commented', commentId, timeoutMs: 20_000 });
6162

62-
// 3. Place cursor right before the commented text and type new text
63+
// Verify comment mark exists in PM state (avoids slow DOM highlight polling on WebKit)
6364
const pos = await superdoc.findTextPos('Commented');
65+
await superdoc.assertMarksAtPos(pos, ['commentMark']);
66+
67+
// 3. Place cursor right before the commented text and type new text
6468
await superdoc.setTextSelection(pos);
6569
await superdoc.waitForStable();
6670

tests/behavior/tests/formatting/decoration-survives-mark-change.spec.ts

Lines changed: 33 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { test, expect } from '../../fixtures/superdoc.js';
2-
import { addCommentByText, assertDocumentApiReady, listComments } from '../../helpers/document-api.js';
2+
import { addCommentByText, assertDocumentApiReady } from '../../helpers/document-api.js';
33

44
/**
55
* SD-1963: Decoration range incorrectly expands to run boundaries;
@@ -22,29 +22,25 @@ test.describe('comment highlight survives mark changes', () => {
2222
await superdoc.waitForStable();
2323

2424
// Add a comment on "brown fox"
25-
const commentId = await addCommentByText(superdoc.page, {
25+
await addCommentByText(superdoc.page, {
2626
pattern: 'brown fox',
2727
text: 'Comment on brown fox',
2828
});
2929
await superdoc.waitForStable();
3030

31-
// Verify comment highlight exists
32-
await superdoc.assertCommentHighlightExists({
33-
text: 'brown fox',
34-
commentId,
35-
timeoutMs: 20_000,
36-
});
31+
// Verify comment mark exists in PM state (avoids slow DOM highlight polling on WebKit)
32+
let pos = await superdoc.findTextPos('brown fox');
33+
await superdoc.assertMarksAtPos(pos, ['commentMark']);
3734

3835
// Select "brown fox" and apply bold
39-
const pos = await superdoc.findTextPos('brown fox');
36+
pos = await superdoc.findTextPos('brown fox');
4037
await superdoc.setTextSelection(pos, pos + 'brown fox'.length);
4138
await superdoc.bold();
4239
await superdoc.waitForStable();
4340

44-
// Comment highlight must still be present
45-
await superdoc.assertCommentHighlightExists({
46-
commentId,
47-
});
41+
// Comment mark must still be present after applying bold
42+
pos = await superdoc.findTextPos('brown fox');
43+
await superdoc.assertMarksAtPos(pos, ['commentMark']);
4844

4945
// Bold must have been applied
5046
await superdoc.assertTextHasMarks('brown fox', ['bold']);
@@ -59,48 +55,28 @@ test.describe('comment highlight survives mark changes', () => {
5955
await superdoc.waitForStable();
6056

6157
// Comment spans "quick brown fox"
62-
const commentId = await addCommentByText(superdoc.page, {
58+
await addCommentByText(superdoc.page, {
6359
pattern: 'quick brown fox',
6460
text: 'Partial italic test',
6561
});
6662
await superdoc.waitForStable();
67-
await superdoc.assertCommentHighlightExists({ text: 'quick brown fox', commentId, timeoutMs: 20_000 });
63+
64+
// Verify comment mark exists in PM state (avoids slow DOM highlight polling on WebKit)
65+
let pos = await superdoc.findTextPos('quick brown fox');
66+
await superdoc.assertMarksAtPos(pos, ['commentMark']);
6867

6968
// Apply italic to only "brown" (middle of the commented range)
70-
const pos = await superdoc.findTextPos('brown');
69+
pos = await superdoc.findTextPos('brown');
7170
await superdoc.setTextSelection(pos, pos + 'brown'.length);
7271
await superdoc.italic();
7372
await superdoc.waitForStable();
7473

75-
// Comment highlight must still exist — after the run split the highlight may
76-
// span multiple elements, so check by commentId AND all text segments in a
77-
// single poll to avoid multiplicative timeouts in slower engines (webkit).
78-
await expect
79-
.poll(
80-
() =>
81-
superdoc.page.evaluate(
82-
({ cId }) => {
83-
const normalize = (v: string) => v.replace(/\s+/g, ' ').trim();
84-
const highlights = Array.from(document.querySelectorAll('.superdoc-comment-highlight'));
85-
if (highlights.length === 0) return 'no highlights';
86-
const hasId = highlights.some((el) =>
87-
(el.getAttribute('data-comment-ids') ?? '')
88-
.split(/[\s,]+/)
89-
.filter(Boolean)
90-
.includes(cId),
91-
);
92-
if (!hasId) return 'missing commentId';
93-
const texts = highlights.map((el) => normalize(el.textContent ?? ''));
94-
for (const word of ['quick', 'brown', 'fox']) {
95-
if (!texts.some((t) => t.includes(word))) return `missing "${word}"`;
96-
}
97-
return 'ok';
98-
},
99-
{ cId: commentId },
100-
),
101-
{ timeout: 25_000 },
102-
)
103-
.toBe('ok');
74+
// Comment mark must still exist on all segments after the run split.
75+
// After italic splits the range, check each word's PM node still has commentMark.
76+
for (const word of ['quick', 'brown', 'fox']) {
77+
const wordPos = await superdoc.findTextPos(word);
78+
await superdoc.assertMarksAtPos(wordPos, ['commentMark']);
79+
}
10480

10581
// Italic applied to "brown"
10682
await superdoc.assertTextHasMarks('brown', ['italic']);
@@ -114,33 +90,39 @@ test.describe('comment highlight survives mark changes', () => {
11490
await superdoc.type('Decoration resilience test sentence');
11591
await superdoc.waitForStable();
11692

117-
const commentId = await addCommentByText(superdoc.page, {
93+
await addCommentByText(superdoc.page, {
11894
pattern: 'resilience test',
11995
text: 'Multi-mark test',
12096
});
12197
await superdoc.waitForStable();
122-
await superdoc.assertCommentHighlightExists({ text: 'resilience test', commentId, timeoutMs: 20_000 });
98+
99+
// Verify comment mark exists in PM state (avoids slow DOM highlight polling on WebKit)
100+
let pos = await superdoc.findTextPos('resilience test');
101+
await superdoc.assertMarksAtPos(pos, ['commentMark']);
123102

124103
// Apply bold, then italic, then underline to the same range.
125104
// Re-select between each mark because WebKit can disrupt DOM selection
126105
// after Cmd+B/I/U shortcuts, and PM may re-index positions after marks.
127-
let pos = await superdoc.findTextPos('resilience test');
106+
pos = await superdoc.findTextPos('resilience test');
128107
await superdoc.setTextSelection(pos, pos + 'resilience test'.length);
129108
await superdoc.bold();
130109
await superdoc.waitForStable();
131-
await superdoc.assertCommentHighlightExists({ commentId });
110+
pos = await superdoc.findTextPos('resilience test');
111+
await superdoc.assertMarksAtPos(pos, ['commentMark']);
132112

133113
pos = await superdoc.findTextPos('resilience test');
134114
await superdoc.setTextSelection(pos, pos + 'resilience test'.length);
135115
await superdoc.italic();
136116
await superdoc.waitForStable();
137-
await superdoc.assertCommentHighlightExists({ commentId });
117+
pos = await superdoc.findTextPos('resilience test');
118+
await superdoc.assertMarksAtPos(pos, ['commentMark']);
138119

139120
pos = await superdoc.findTextPos('resilience test');
140121
await superdoc.setTextSelection(pos, pos + 'resilience test'.length);
141122
await superdoc.underline();
142123
await superdoc.waitForStable();
143-
await superdoc.assertCommentHighlightExists({ commentId });
124+
pos = await superdoc.findTextPos('resilience test');
125+
await superdoc.assertMarksAtPos(pos, ['commentMark']);
144126

145127
// All three marks should be present
146128
await superdoc.assertTextHasMarks('resilience test', ['bold', 'italic', 'underline']);

0 commit comments

Comments
 (0)