ep_comments_page 11.0.25 → 11.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/locales/be-tarask.json +23 -0
- package/locales/fr.json +2 -0
- package/locales/nl.json +1 -0
- package/locales/zh-hans.json +2 -0
- package/package.json +1 -1
- package/static/tests/frontend-new/helper/comments.ts +188 -0
- package/static/tests/frontend-new/specs/commentDelete.spec.ts +57 -0
- package/static/tests/frontend-new/specs/commentEdit.spec.ts +113 -0
- package/static/tests/frontend-new/specs/commentIcons.spec.ts +131 -0
- package/static/tests/frontend-new/specs/commentReply.spec.ts +92 -0
- package/static/tests/frontend-new/specs/commentSuggestion.spec.ts +97 -0
- package/static/tests/frontend-new/specs/comment_l10n.spec.ts +63 -0
- package/static/tests/frontend-new/specs/comment_settings.spec.ts +47 -0
- package/static/tests/frontend-new/specs/newComment.spec.ts +25 -0
- package/static/tests/frontend-new/specs/preCommentMark.spec.ts +123 -0
- package/static/tests/frontend-new/specs/timeFormat.spec.ts +244 -0
- package/static/tests/frontend-new/specs/xcommentCopyPaste.spec.ts +29 -0
- package/static/tests/frontend/specs/commentDelete.js +0 -146
- package/static/tests/frontend/specs/commentEdit.js +0 -291
- package/static/tests/frontend/specs/commentIcons.js +0 -298
- package/static/tests/frontend/specs/commentReply.js +0 -200
- package/static/tests/frontend/specs/commentSuggestion.js +0 -100
- package/static/tests/frontend/specs/comment_l10n.js +0 -141
- package/static/tests/frontend/specs/comment_settings.js +0 -132
- package/static/tests/frontend/specs/newComment.js +0 -18
- package/static/tests/frontend/specs/preCommentMark.js +0 -241
- package/static/tests/frontend/specs/timeFormat.js +0 -266
- package/static/tests/frontend/specs/xcommentCopyPaste.js +0 -480
- package/static/tests/frontend/utils.js +0 -14
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@metadata": {
|
|
3
|
+
"authors": [
|
|
4
|
+
"Ucukor"
|
|
5
|
+
]
|
|
6
|
+
},
|
|
7
|
+
"ep_comments_page.comment": "Камэнтар",
|
|
8
|
+
"ep_comments_page.comments": "Камэнтары",
|
|
9
|
+
"ep_comments_page.delete_comment.title": "Выдаліць гэты камэнтар",
|
|
10
|
+
"ep_comments_page.edit_comment.title": "Рэдагаваць гэты камэнтар",
|
|
11
|
+
"ep_comments_page.show_comments": "Паказаць камэнтары",
|
|
12
|
+
"ep_comments_page.comments_template.suggested_change": "Прапанаваная зьмена",
|
|
13
|
+
"ep_comments_page.comments_template.from": "Ад",
|
|
14
|
+
"ep_comments_page.comments_template.accept_change.value": "Прыняць зьмены",
|
|
15
|
+
"ep_comments_page.comments_template.revert_change.value": "Адкаціць зьмены",
|
|
16
|
+
"ep_comments_page.comments_template.to": "Да",
|
|
17
|
+
"ep_comments_page.comments_template.comment.value": "Камэнтар",
|
|
18
|
+
"ep_comments_page.comments_template.cancel.value": "Скасаваць",
|
|
19
|
+
"ep_comments_page.comments_template.reply.value": "Адказаць",
|
|
20
|
+
"ep_comments_page.comments_template.reply.placeholder": "Адказаць",
|
|
21
|
+
"ep_comments_page.comments_template.edit_comment.save": "захаваць",
|
|
22
|
+
"ep_comments_page.comments_template.edit_comment.cancel": "скасаваць"
|
|
23
|
+
}
|
package/locales/fr.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@metadata": {
|
|
3
3
|
"authors": [
|
|
4
|
+
"Chpol",
|
|
4
5
|
"Meaz",
|
|
5
6
|
"Verdy p"
|
|
6
7
|
]
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
"ep_comments_page.comments_template.accept_change.value": "Appliquer la proposition",
|
|
19
20
|
"ep_comments_page.comments_template.revert_change.value": "Annuler la proposition",
|
|
20
21
|
"ep_comments_page.comments_template.suggested_change_from": "Propose de remplacer « {{changeFrom}} » par « {{changeTo}} »",
|
|
22
|
+
"ep_comments_page.comments_template.suggested_change_from_label": "Modification suggérée par",
|
|
21
23
|
"ep_comments_page.comments_template.suggest_change_from": "Remplacer « {{changeFrom}} » par",
|
|
22
24
|
"ep_comments_page.comments_template.to": "Par",
|
|
23
25
|
"ep_comments_page.comments_template.include_suggestion": "Inclure la modification suggérée",
|
package/locales/nl.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"ep_comments_page.comments_template.accept_change.value": "Accepteer wijziging",
|
|
19
19
|
"ep_comments_page.comments_template.revert_change.value": "Draai wijziging terug",
|
|
20
20
|
"ep_comments_page.comments_template.suggested_change_from": "Voorgestelde wijziging van \"{{changeFrom}}\" in \"{{changeTo}}\"",
|
|
21
|
+
"ep_comments_page.comments_template.suggested_change_from_label": "Voorgestelde wijziging van",
|
|
21
22
|
"ep_comments_page.comments_template.suggest_change_from": "Voorgestelde wijziging van \"{{changeFrom}}\" in",
|
|
22
23
|
"ep_comments_page.comments_template.to": "Naar",
|
|
23
24
|
"ep_comments_page.comments_template.include_suggestion": "Voeg voorgestelde wijziging toe",
|
package/locales/zh-hans.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@metadata": {
|
|
3
3
|
"authors": [
|
|
4
|
+
"GuoPC",
|
|
4
5
|
"HellojoeAoPS",
|
|
5
6
|
"TsuyaMarisa",
|
|
6
7
|
"列维劳德"
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
"ep_comments_page.comments_template.accept_change.value": "接受更改",
|
|
20
21
|
"ep_comments_page.comments_template.revert_change.value": "恢复更改",
|
|
21
22
|
"ep_comments_page.comments_template.suggested_change_from": "建议从“{{changeFrom}}”更改为“{{changeTo}}”",
|
|
23
|
+
"ep_comments_page.comments_template.suggested_change_from_label": "建议的更改来自",
|
|
22
24
|
"ep_comments_page.comments_template.suggest_change_from": "建议从“{{changeFrom}}”更改为",
|
|
23
25
|
"ep_comments_page.comments_template.to": "至",
|
|
24
26
|
"ep_comments_page.comments_template.include_suggestion": "包括建议的更改",
|
package/package.json
CHANGED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import {Frame, Locator, Page, expect} from '@playwright/test';
|
|
2
|
+
import {getPadBody, getPadOuter, goToNewPad, goToPad}
|
|
3
|
+
from 'ep_etherpad-lite/tests/frontend-new/helper/padHelper';
|
|
4
|
+
|
|
5
|
+
// Reproduces the legacy `utils.aNewPad`: open a new pad, then wait for
|
|
6
|
+
// ep_comments_page's postAceInit to finish (initDone is a Promise that
|
|
7
|
+
// resolves once plugin init is complete).
|
|
8
|
+
export const aNewCommentsPad = async (page: Page): Promise<string> => {
|
|
9
|
+
const padId = await goToNewPad(page);
|
|
10
|
+
await waitForCommentsInit(page);
|
|
11
|
+
return padId;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const reopenCommentsPad = async (page: Page, padId: string): Promise<void> => {
|
|
15
|
+
await goToPad(page, padId);
|
|
16
|
+
await waitForCommentsInit(page);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const waitForCommentsInit = async (page: Page): Promise<void> => {
|
|
20
|
+
await expect.poll(async () => page.evaluate(async () => {
|
|
21
|
+
const w = window as any;
|
|
22
|
+
const ep = w.pad && w.pad.plugins && w.pad.plugins.ep_comments_page;
|
|
23
|
+
if (!ep || !ep.initDone) return false;
|
|
24
|
+
await ep.initDone;
|
|
25
|
+
return true;
|
|
26
|
+
}), {timeout: 60_000}).toBe(true);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Returns the inner pad body (Locator).
|
|
30
|
+
export const inner = async (page: Page): Promise<Locator> => getPadBody(page);
|
|
31
|
+
|
|
32
|
+
// Returns the outer iframe Frame (where #commentIcons / sidebar comments live).
|
|
33
|
+
export const outer = async (page: Page): Promise<Frame> => getPadOuter(page);
|
|
34
|
+
|
|
35
|
+
export const enlargeScreen = async (page: Page, maxWidth = '3000px'): Promise<void> => {
|
|
36
|
+
await page.evaluate((mw) => {
|
|
37
|
+
const ifr = document.querySelector<HTMLIFrameElement>('#iframe-container iframe');
|
|
38
|
+
if (ifr) ifr.style.maxWidth = mw;
|
|
39
|
+
}, maxWidth);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const restoreScreen = async (page: Page): Promise<void> => {
|
|
43
|
+
await page.evaluate(() => {
|
|
44
|
+
const ifr = document.querySelector<HTMLIFrameElement>('#iframe-container iframe');
|
|
45
|
+
if (ifr) ifr.style.maxWidth = '';
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Replace pad content with provided lines (joined with \n). Mirrors the
|
|
50
|
+
// legacy cleanPad + sendkeys pattern.
|
|
51
|
+
export const setPadLines = async (page: Page, lines: string[]): Promise<void> => {
|
|
52
|
+
const body = await getPadBody(page);
|
|
53
|
+
await body.click();
|
|
54
|
+
await page.keyboard.press('Control+A');
|
|
55
|
+
await page.keyboard.press('Delete');
|
|
56
|
+
// Wait for first line to be empty.
|
|
57
|
+
await expect.poll(async () => body.locator('div').count()).toBeGreaterThan(0);
|
|
58
|
+
for (let i = 0; i < lines.length; i++) {
|
|
59
|
+
await page.keyboard.type(lines[i]);
|
|
60
|
+
if (i < lines.length - 1) await page.keyboard.press('Enter');
|
|
61
|
+
}
|
|
62
|
+
await expect.poll(async () => body.locator('div').count())
|
|
63
|
+
.toBeGreaterThanOrEqual(lines.length);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Select all text on a given line index (0-based). Implemented by triple-click
|
|
67
|
+
// on the corresponding div, which selects the line content.
|
|
68
|
+
export const selectLine = async (page: Page, lineIndex: number): Promise<void> => {
|
|
69
|
+
const body = await getPadBody(page);
|
|
70
|
+
const line = body.locator('div').nth(lineIndex);
|
|
71
|
+
await line.click();
|
|
72
|
+
// Triple-click to select the entire line content.
|
|
73
|
+
await line.click({clickCount: 3});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Clicks the toolbar Add Comment button (lives in the chrome page).
|
|
77
|
+
export const clickAddCommentButton = async (page: Page): Promise<void> => {
|
|
78
|
+
await page.locator('.addComment').first().click();
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const fillCommentForm = async (
|
|
82
|
+
page: Page,
|
|
83
|
+
commentText: string,
|
|
84
|
+
suggestion?: string,
|
|
85
|
+
): Promise<void> => {
|
|
86
|
+
const field = page.locator('textarea.comment-content');
|
|
87
|
+
await field.fill(commentText);
|
|
88
|
+
if (suggestion !== undefined) {
|
|
89
|
+
await page.locator('#newComment .suggestion-checkbox').first().click();
|
|
90
|
+
await page.locator('textarea.to-value').fill(suggestion);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const submitNewCommentForm = async (page: Page): Promise<void> => {
|
|
95
|
+
await page.locator('.comment-buttons input[type=submit]').first().click();
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const getCommentIdOfLine = async (
|
|
99
|
+
page: Page, lineIndex: number,
|
|
100
|
+
): Promise<string | null> => {
|
|
101
|
+
const body = await getPadBody(page);
|
|
102
|
+
const cls = await body.locator('div').nth(lineIndex).locator('.comment').first()
|
|
103
|
+
.getAttribute('class').catch(() => null);
|
|
104
|
+
if (!cls) return null;
|
|
105
|
+
const m = /(?:^| )(c-[A-Za-z0-9]*)/.exec(cls);
|
|
106
|
+
return m ? m[1] : null;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export const waitForCommentOnLine = async (page: Page, lineIndex: number): Promise<string> => {
|
|
110
|
+
await expect.poll(async () => (await getCommentIdOfLine(page, lineIndex)) != null,
|
|
111
|
+
{timeout: 30_000}).toBe(true);
|
|
112
|
+
return (await getCommentIdOfLine(page, lineIndex))!;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const commentIconsEnabled = async (page: Page): Promise<boolean> => {
|
|
116
|
+
const o = await getPadOuter(page);
|
|
117
|
+
return (await o.locator('#commentIcons').count()) > 0;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const displayCommentAsIcon = async (page: Page): Promise<boolean> => {
|
|
121
|
+
return await page.evaluate(() => Boolean((window as any).clientVars?.displayCommentAsIcon));
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export const highlightSelectedTextEnabled = async (page: Page): Promise<boolean> => {
|
|
125
|
+
return await page.evaluate(() => Boolean((window as any).clientVars?.highlightSelectedText));
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Add a comment to the given line, optionally with a suggested change.
|
|
129
|
+
// Returns the new comment id.
|
|
130
|
+
export const addCommentToLine = async (
|
|
131
|
+
page: Page,
|
|
132
|
+
lineIndex: number,
|
|
133
|
+
text: string,
|
|
134
|
+
suggestion?: string,
|
|
135
|
+
): Promise<string> => {
|
|
136
|
+
await selectLine(page, lineIndex);
|
|
137
|
+
await clickAddCommentButton(page);
|
|
138
|
+
await fillCommentForm(page, text, suggestion);
|
|
139
|
+
await submitNewCommentForm(page);
|
|
140
|
+
return await waitForCommentOnLine(page, lineIndex);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const addReplyToLine = async (
|
|
144
|
+
page: Page,
|
|
145
|
+
lineIndex: number,
|
|
146
|
+
replyText: string,
|
|
147
|
+
withSuggestion = false,
|
|
148
|
+
suggestionText?: string,
|
|
149
|
+
): Promise<void> => {
|
|
150
|
+
const o = await getPadOuter(page);
|
|
151
|
+
const commentId = await getCommentIdOfLine(page, lineIndex);
|
|
152
|
+
const existing = await o.locator('.sidebar-comment-reply').count();
|
|
153
|
+
|
|
154
|
+
if (await commentIconsEnabled(page)) {
|
|
155
|
+
await o.locator(`#commentIcons #icon-${commentId}`).first().click();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await o.locator('.comment-content').first().fill(replyText);
|
|
159
|
+
if (withSuggestion) {
|
|
160
|
+
await o.locator('.suggestion-checkbox').first().click();
|
|
161
|
+
if (suggestionText !== undefined) {
|
|
162
|
+
await o.locator('textarea.to-value').first().fill(suggestionText);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
await o.locator("form.new-comment input[type='submit']").first().click();
|
|
166
|
+
await expect.poll(async () => o.locator('.sidebar-comment-reply').count())
|
|
167
|
+
.toBe(existing + 1);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Open Etherpad settings, toggle Show Comments to desired state, close settings.
|
|
171
|
+
export const chooseToShowComments = async (
|
|
172
|
+
page: Page, shouldShow: boolean,
|
|
173
|
+
): Promise<void> => {
|
|
174
|
+
const settings = page.locator('.buttonicon-settings');
|
|
175
|
+
await settings.click();
|
|
176
|
+
const showComments = page.locator('#options-comments');
|
|
177
|
+
const checked = await showComments.isChecked();
|
|
178
|
+
if (checked !== shouldShow) await showComments.click();
|
|
179
|
+
await settings.click();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Change Etherpad UI language.
|
|
183
|
+
export const changeLanguageTo = async (page: Page, lang: string): Promise<void> => {
|
|
184
|
+
const settings = page.locator('.buttonicon-settings');
|
|
185
|
+
await settings.click();
|
|
186
|
+
await page.locator('#languagemenu').selectOption(lang);
|
|
187
|
+
await settings.click();
|
|
188
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {expect, test} from '@playwright/test';
|
|
2
|
+
import {getPadBody, getPadOuter}
|
|
3
|
+
from 'ep_etherpad-lite/tests/frontend-new/helper/padHelper';
|
|
4
|
+
import {
|
|
5
|
+
addCommentToLine,
|
|
6
|
+
addReplyToLine,
|
|
7
|
+
aNewCommentsPad,
|
|
8
|
+
enlargeScreen,
|
|
9
|
+
reopenCommentsPad,
|
|
10
|
+
setPadLines,
|
|
11
|
+
} from '../helper/comments';
|
|
12
|
+
|
|
13
|
+
const textOfComment = 'original comment';
|
|
14
|
+
const textOfReply = 'original reply';
|
|
15
|
+
const FIRST_LINE = 0;
|
|
16
|
+
|
|
17
|
+
test.describe('ep_comments_page - Comment Delete', () => {
|
|
18
|
+
let padId: string;
|
|
19
|
+
|
|
20
|
+
test.beforeEach(async ({page}) => {
|
|
21
|
+
test.setTimeout(60_000);
|
|
22
|
+
padId = await aNewCommentsPad(page);
|
|
23
|
+
await enlargeScreen(page);
|
|
24
|
+
await setPadLines(page, ['something', ' anything']);
|
|
25
|
+
await addCommentToLine(page, FIRST_LINE, textOfComment);
|
|
26
|
+
await addReplyToLine(page, FIRST_LINE, textOfReply);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test.describe('when user presses the delete button on a comment', () => {
|
|
30
|
+
test('should delete comment', async ({page}) => {
|
|
31
|
+
const outer = await getPadOuter(page);
|
|
32
|
+
const inner = await getPadBody(page);
|
|
33
|
+
await outer.locator('.comment-delete').first().click();
|
|
34
|
+
await expect.poll(async () => inner.locator('.comment').count()).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test.describe('when user presses the delete button on other users comment', () => {
|
|
39
|
+
test('should not delete comment', async ({page}) => {
|
|
40
|
+
// Reload as a fresh user — re-opens the same pad and waits for plugin init.
|
|
41
|
+
await page.waitForTimeout(500);
|
|
42
|
+
await reopenCommentsPad(page, padId);
|
|
43
|
+
|
|
44
|
+
const outer = await getPadOuter(page);
|
|
45
|
+
const inner = await getPadBody(page);
|
|
46
|
+
await expect.poll(async () => outer.locator('.comment-delete').count())
|
|
47
|
+
.toBeGreaterThan(0);
|
|
48
|
+
await outer.locator('.comment-delete').first().click();
|
|
49
|
+
|
|
50
|
+
// Error gritter shown.
|
|
51
|
+
await expect.poll(async () =>
|
|
52
|
+
page.locator('#gritter-container .error').count()).toBeGreaterThan(0);
|
|
53
|
+
// Comment was not deleted.
|
|
54
|
+
expect(await inner.locator('.comment').count()).toBeGreaterThan(0);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {expect, test} from '@playwright/test';
|
|
2
|
+
import {getPadBody, getPadOuter}
|
|
3
|
+
from 'ep_etherpad-lite/tests/frontend-new/helper/padHelper';
|
|
4
|
+
import {
|
|
5
|
+
addCommentToLine,
|
|
6
|
+
addReplyToLine,
|
|
7
|
+
aNewCommentsPad,
|
|
8
|
+
enlargeScreen,
|
|
9
|
+
reopenCommentsPad,
|
|
10
|
+
setPadLines,
|
|
11
|
+
} from '../helper/comments';
|
|
12
|
+
|
|
13
|
+
const textOfComment = 'original comment';
|
|
14
|
+
const textOfReply = 'original reply';
|
|
15
|
+
const FIRST_LINE = 0;
|
|
16
|
+
|
|
17
|
+
test.describe('ep_comments_page - Comment Edit', () => {
|
|
18
|
+
let padId: string;
|
|
19
|
+
|
|
20
|
+
test.beforeEach(async ({page}) => {
|
|
21
|
+
test.setTimeout(60_000);
|
|
22
|
+
padId = await aNewCommentsPad(page);
|
|
23
|
+
await enlargeScreen(page);
|
|
24
|
+
await setPadLines(page, ['something', ' anything']);
|
|
25
|
+
await addCommentToLine(page, FIRST_LINE, textOfComment);
|
|
26
|
+
await addReplyToLine(page, FIRST_LINE, textOfReply);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test.describe('when user presses the button edit on a comment', () => {
|
|
30
|
+
test('should add a comment form', async ({page}) => {
|
|
31
|
+
const outer = await getPadOuter(page);
|
|
32
|
+
await outer.locator('.comment-edit').first().click();
|
|
33
|
+
await expect(outer.locator('.comment-edit-form')).toHaveCount(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('should show the original comment text on the edit form', async ({page}) => {
|
|
37
|
+
const outer = await getPadOuter(page);
|
|
38
|
+
await outer.locator('.comment-edit').first().click();
|
|
39
|
+
await expect(outer.locator('.comment-edit-form .comment-edit-text').first())
|
|
40
|
+
.toHaveText(textOfComment);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('and presses edit button again should not add a new form', async ({page}) => {
|
|
44
|
+
const outer = await getPadOuter(page);
|
|
45
|
+
await outer.locator('.comment-edit').first().click();
|
|
46
|
+
await outer.locator('.comment-edit').first().click();
|
|
47
|
+
await expect(outer.locator('.comment-edit-form')).toHaveCount(1);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('and presses cancel should remove the edit form', async ({page}) => {
|
|
51
|
+
const outer = await getPadOuter(page);
|
|
52
|
+
await outer.locator('.comment-edit').first().click();
|
|
53
|
+
await outer.locator('.comment-edit-form .comment-edit-cancel').first().click();
|
|
54
|
+
await expect(outer.locator('.comment-edit-form')).toHaveCount(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('and writes a new comment text and presses save should update the comment text',
|
|
58
|
+
async ({page}) => {
|
|
59
|
+
const outer = await getPadOuter(page);
|
|
60
|
+
const updatedText = 'this comment was edited';
|
|
61
|
+
await outer.locator('.comment-edit').first().click();
|
|
62
|
+
// Set the new text in the contenteditable edit area.
|
|
63
|
+
await outer.locator('.comment-edit-form .comment-edit-text').first()
|
|
64
|
+
.evaluate((el, t) => { (el as HTMLElement).innerText = t; }, updatedText);
|
|
65
|
+
await outer.locator('.comment-edit-form .comment-edit-submit').first().click();
|
|
66
|
+
await expect.poll(async () =>
|
|
67
|
+
(await outer.locator('.comment-text').first().textContent())?.trim())
|
|
68
|
+
.toBe(updatedText);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('and reloads the page shows the comment text updated', async ({page}) => {
|
|
72
|
+
const outer = await getPadOuter(page);
|
|
73
|
+
const updatedText = 'this comment was edited';
|
|
74
|
+
await outer.locator('.comment-edit').first().click();
|
|
75
|
+
await outer.locator('.comment-edit-form .comment-edit-text').first()
|
|
76
|
+
.evaluate((el, t) => { (el as HTMLElement).innerText = t; }, updatedText);
|
|
77
|
+
await outer.locator('.comment-edit-form .comment-edit-submit').first().click();
|
|
78
|
+
await expect.poll(async () =>
|
|
79
|
+
(await outer.locator('.comment-text').first().textContent())?.trim())
|
|
80
|
+
.toBe(updatedText);
|
|
81
|
+
|
|
82
|
+
// Allow time for the change to be persisted, then reload.
|
|
83
|
+
await page.waitForTimeout(1000);
|
|
84
|
+
await reopenCommentsPad(page, padId);
|
|
85
|
+
const outer2 = await getPadOuter(page);
|
|
86
|
+
await expect.poll(async () =>
|
|
87
|
+
(await outer2.locator('.comment-text').first().textContent())?.trim(),
|
|
88
|
+
{timeout: 20_000}).toBe(updatedText);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('new user tries editing should not update the comment text', async ({page}) => {
|
|
92
|
+
const updatedText2 = 'this comment was edited again';
|
|
93
|
+
await page.waitForTimeout(500);
|
|
94
|
+
// Reopen as a "new" user (fresh page load).
|
|
95
|
+
await reopenCommentsPad(page, padId);
|
|
96
|
+
const outer = await getPadOuter(page);
|
|
97
|
+
await expect.poll(async () =>
|
|
98
|
+
outer.locator('#comments .comment-edit').count()).toBeGreaterThan(0);
|
|
99
|
+
|
|
100
|
+
await outer.locator('.comment-edit').first().click();
|
|
101
|
+
await outer.locator('.comment-edit-form .comment-edit-text').first()
|
|
102
|
+
.evaluate((el, t) => { (el as HTMLElement).innerText = t; }, updatedText2);
|
|
103
|
+
await outer.locator('.comment-edit-form .comment-edit-submit').first().click();
|
|
104
|
+
|
|
105
|
+
// Error gritter is shown.
|
|
106
|
+
await expect.poll(async () =>
|
|
107
|
+
page.locator('#gritter-container .error').count()).toBeGreaterThan(0);
|
|
108
|
+
// Comment text is not the new text.
|
|
109
|
+
const got = (await outer.locator('.comment-text').first().textContent())?.trim();
|
|
110
|
+
expect(got).not.toBe(updatedText2);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {expect, test} from '@playwright/test';
|
|
2
|
+
import {getPadBody, getPadOuter}
|
|
3
|
+
from 'ep_etherpad-lite/tests/frontend-new/helper/padHelper';
|
|
4
|
+
import {
|
|
5
|
+
aNewCommentsPad,
|
|
6
|
+
chooseToShowComments,
|
|
7
|
+
displayCommentAsIcon,
|
|
8
|
+
enlargeScreen,
|
|
9
|
+
getCommentIdOfLine,
|
|
10
|
+
setPadLines,
|
|
11
|
+
waitForCommentOnLine,
|
|
12
|
+
} from '../helper/comments';
|
|
13
|
+
|
|
14
|
+
const COMMENT_TEXT = 'This content will receive a comment';
|
|
15
|
+
|
|
16
|
+
const addCommentNoSelect = async (page: import('@playwright/test').Page, text: string) => {
|
|
17
|
+
const inner = await getPadBody(page);
|
|
18
|
+
const before = await inner.locator('.comment').count();
|
|
19
|
+
await page.locator('.addComment').first().click();
|
|
20
|
+
await page.locator('textarea.comment-content').fill(text);
|
|
21
|
+
await page.locator('.comment-buttons input[type=submit]').first().click();
|
|
22
|
+
await expect.poll(async () => inner.locator('.comment').count())
|
|
23
|
+
.toBeGreaterThan(before);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
test.describe('ep_comments_page - Comment icons', () => {
|
|
27
|
+
test.beforeEach(async ({page}) => {
|
|
28
|
+
test.setTimeout(60_000);
|
|
29
|
+
await aNewCommentsPad(page);
|
|
30
|
+
if (!(await displayCommentAsIcon(page))) test.skip();
|
|
31
|
+
// Smaller width — legacy used 1000px for icon tests.
|
|
32
|
+
await enlargeScreen(page, '1000px');
|
|
33
|
+
await chooseToShowComments(page, true);
|
|
34
|
+
// Create the initial commented line.
|
|
35
|
+
await setPadLines(page, [COMMENT_TEXT]);
|
|
36
|
+
const inner = await getPadBody(page);
|
|
37
|
+
// Select the line and add comment.
|
|
38
|
+
await inner.locator('div').first().click({clickCount: 3});
|
|
39
|
+
await addCommentNoSelect(page, 'My comment');
|
|
40
|
+
await waitForCommentOnLine(page, 0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('adds a comment icon on the same height of commented text', async ({page}) => {
|
|
44
|
+
const inner = await getPadBody(page);
|
|
45
|
+
const outer = await getPadOuter(page);
|
|
46
|
+
const commentId = (await getCommentIdOfLine(page, 0))!;
|
|
47
|
+
const icon = outer.locator(`#commentIcons #icon-${commentId}`);
|
|
48
|
+
await expect(icon).toHaveCount(1);
|
|
49
|
+
const iconTop = (await icon.boundingBox())!.y;
|
|
50
|
+
const textTop = (await inner.locator(`.${commentId}`).first().boundingBox())!.y;
|
|
51
|
+
// Legacy: icons are +5px down to adjust position.
|
|
52
|
+
expect(Math.round(iconTop)).toBe(Math.round(textTop + 5));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Legacy was xit('does not show comment icon when commented text is removed')
|
|
56
|
+
test.skip('does not show comment icon when commented text is removed', async () => {});
|
|
57
|
+
// Legacy was xit('does not show comment icon when comment is deleted')
|
|
58
|
+
test.skip('does not show comment icon when comment is deleted', async () => {});
|
|
59
|
+
|
|
60
|
+
test('updates comment icon height when commented text is moved to another line',
|
|
61
|
+
async ({page}) => {
|
|
62
|
+
const inner = await getPadBody(page);
|
|
63
|
+
const outer = await getPadOuter(page);
|
|
64
|
+
const commentId = (await getCommentIdOfLine(page, 0))!;
|
|
65
|
+
|
|
66
|
+
await inner.locator('div').first().click();
|
|
67
|
+
await page.keyboard.press('Home');
|
|
68
|
+
await page.keyboard.press('Enter');
|
|
69
|
+
await page.keyboard.press('Enter');
|
|
70
|
+
|
|
71
|
+
await expect.poll(async () => inner.locator('div').count()).toBeGreaterThan(2);
|
|
72
|
+
await expect.poll(async () =>
|
|
73
|
+
outer.locator('#commentIcons .comment-icon').count()).toBeGreaterThan(0);
|
|
74
|
+
|
|
75
|
+
const icon = outer.locator(`#commentIcons #icon-${commentId}`);
|
|
76
|
+
const iconTop = (await icon.boundingBox())!.y;
|
|
77
|
+
const textTop = (await inner.locator(`.${commentId}`).first().boundingBox())!.y;
|
|
78
|
+
expect(Math.round(iconTop)).toBe(Math.round(textTop + 5));
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('shows comment when user clicks on comment icon', async ({page}) => {
|
|
82
|
+
const outer = await getPadOuter(page);
|
|
83
|
+
const commentId = (await getCommentIdOfLine(page, 0))!;
|
|
84
|
+
await outer.locator(`#commentIcons #icon-${commentId}`).first().click();
|
|
85
|
+
await expect(outer.locator('#comments .sidebar-comment:visible')).toHaveCount(1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('hides comment when user clicks on comment icon twice', async ({page}) => {
|
|
89
|
+
const outer = await getPadOuter(page);
|
|
90
|
+
const commentId = (await getCommentIdOfLine(page, 0))!;
|
|
91
|
+
const icon = outer.locator(`#commentIcons #icon-${commentId}`).first();
|
|
92
|
+
await icon.click();
|
|
93
|
+
await icon.click();
|
|
94
|
+
await expect(outer.locator('#comments .sidebar-comment:visible')).toHaveCount(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('hides comment when user clicks outside of comment box', async ({page}) => {
|
|
98
|
+
const outer = await getPadOuter(page);
|
|
99
|
+
const commentId = (await getCommentIdOfLine(page, 0))!;
|
|
100
|
+
await outer.locator(`#commentIcons #icon-${commentId}`).first().click();
|
|
101
|
+
await outer.locator('#outerdocbody').click();
|
|
102
|
+
await expect(outer.locator('#comments .sidebar-comment:visible')).toHaveCount(0);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('hides 1st, shows 2nd comment when user clicks on one then another icon',
|
|
106
|
+
async ({page}) => {
|
|
107
|
+
const inner = await getPadBody(page);
|
|
108
|
+
const outer = await getPadOuter(page);
|
|
109
|
+
|
|
110
|
+
// Add a second line and a second comment.
|
|
111
|
+
await inner.locator('div').last().click();
|
|
112
|
+
await page.keyboard.press('End');
|
|
113
|
+
await page.keyboard.type('Second line');
|
|
114
|
+
await page.keyboard.press('Enter');
|
|
115
|
+
await expect.poll(async () => inner.locator('div').count()).toBeGreaterThan(2);
|
|
116
|
+
|
|
117
|
+
const second = inner.locator('div').nth(1);
|
|
118
|
+
await second.click({clickCount: 3});
|
|
119
|
+
await addCommentNoSelect(page, 'Second Comment');
|
|
120
|
+
await waitForCommentOnLine(page, 1);
|
|
121
|
+
|
|
122
|
+
const id0 = (await getCommentIdOfLine(page, 0))!;
|
|
123
|
+
const id1 = (await getCommentIdOfLine(page, 1))!;
|
|
124
|
+
await outer.locator(`#commentIcons #icon-${id0}`).first().click();
|
|
125
|
+
await outer.locator(`#commentIcons #icon-${id1}`).first().click();
|
|
126
|
+
|
|
127
|
+
const visibleText = await outer.locator('#comments .sidebar-comment:visible .comment-text')
|
|
128
|
+
.first().textContent();
|
|
129
|
+
expect((visibleText || '').trim()).toBe('Second Comment');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {expect, test} from '@playwright/test';
|
|
2
|
+
import {getPadBody, getPadOuter}
|
|
3
|
+
from 'ep_etherpad-lite/tests/frontend-new/helper/padHelper';
|
|
4
|
+
import {
|
|
5
|
+
aNewCommentsPad,
|
|
6
|
+
chooseToShowComments,
|
|
7
|
+
commentIconsEnabled,
|
|
8
|
+
enlargeScreen,
|
|
9
|
+
getCommentIdOfLine,
|
|
10
|
+
setPadLines,
|
|
11
|
+
waitForCommentOnLine,
|
|
12
|
+
} from '../helper/comments';
|
|
13
|
+
|
|
14
|
+
const createReply = async (page: import('@playwright/test').Page, withSuggestion: boolean) => {
|
|
15
|
+
const outer = await getPadOuter(page);
|
|
16
|
+
const commentId = await getCommentIdOfLine(page, 0);
|
|
17
|
+
const existing = await outer.locator('.sidebar-comment-reply').count();
|
|
18
|
+
if (await commentIconsEnabled(page)) {
|
|
19
|
+
await outer.locator(`#commentIcons #icon-${commentId}`).first().click();
|
|
20
|
+
}
|
|
21
|
+
await outer.locator('.comment-content').first().fill('My reply');
|
|
22
|
+
if (withSuggestion) {
|
|
23
|
+
await outer.locator('.suggestion-checkbox').first().click();
|
|
24
|
+
await outer.locator('textarea.to-value').first().fill('My suggestion');
|
|
25
|
+
}
|
|
26
|
+
await outer.locator("form.new-comment input[type='submit']").first().click();
|
|
27
|
+
await expect.poll(async () => outer.locator('.sidebar-comment-reply').count())
|
|
28
|
+
.toBe(existing + 1);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Faithful 1:1 port — all original tests were xit (skipped) in the legacy spec.
|
|
32
|
+
test.describe('ep_comments_page - Comment Reply', () => {
|
|
33
|
+
test.beforeEach(async ({page}) => {
|
|
34
|
+
test.setTimeout(60_000);
|
|
35
|
+
await aNewCommentsPad(page);
|
|
36
|
+
await chooseToShowComments(page, true);
|
|
37
|
+
await setPadLines(page, ['This content will receive a comment']);
|
|
38
|
+
const inner = await getPadBody(page);
|
|
39
|
+
await inner.locator('div').first().click({clickCount: 3});
|
|
40
|
+
await page.locator('.addComment').first().click();
|
|
41
|
+
await page.locator('textarea.comment-content').fill('My comment');
|
|
42
|
+
const outer = await getPadOuter(page);
|
|
43
|
+
await outer.locator('.suggestion-checkbox').first().click();
|
|
44
|
+
await outer.locator('textarea.to-value').first().fill('Change to this suggestion');
|
|
45
|
+
await page.locator('.comment-buttons input[type=submit]').first().click();
|
|
46
|
+
await waitForCommentOnLine(page, 0);
|
|
47
|
+
await enlargeScreen(page, '1000px');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test.skip('Ensures a comment can be replied', async ({page}) => {
|
|
51
|
+
await createReply(page, false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test.skip('Ensures a comment reply can have suggestion', async ({page}) => {
|
|
55
|
+
await createReply(page, true);
|
|
56
|
+
const outer = await getPadOuter(page);
|
|
57
|
+
await expect(outer.locator('.comment-changeTo-form')).toBeVisible();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test.skip('Clears the comment reply form after submitting a reply with suggestion',
|
|
61
|
+
async ({page}) => {
|
|
62
|
+
await createReply(page, true);
|
|
63
|
+
const outer = await getPadOuter(page);
|
|
64
|
+
const replyForm = outer.locator('form.new-comment');
|
|
65
|
+
expect(await replyForm.locator('.comment-content').textContent()).toBe('');
|
|
66
|
+
expect(await replyForm.locator('.suggestion-checkbox').isChecked()).toBe(false);
|
|
67
|
+
expect(await replyForm.locator('.to-value').textContent()).toBe('');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test.skip('Replaces the original text with reply suggestion', async ({page}) => {
|
|
71
|
+
await createReply(page, true);
|
|
72
|
+
const outer = await getPadOuter(page);
|
|
73
|
+
const inner = await getPadBody(page);
|
|
74
|
+
await outer.locator(".sidebar-comment-reply .comment-changeTo-form input[type='submit']")
|
|
75
|
+
.first().click();
|
|
76
|
+
await expect.poll(async () =>
|
|
77
|
+
(await inner.locator('div').first().textContent())?.trim()).toBe('My suggestion');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test.skip('Replaces orig with reply sugg. after replacing orig with comment sugg.',
|
|
81
|
+
async ({page}) => {
|
|
82
|
+
await createReply(page, true);
|
|
83
|
+
const outer = await getPadOuter(page);
|
|
84
|
+
const inner = await getPadBody(page);
|
|
85
|
+
await outer.locator(".sidebar-comment .comment-changeTo-form input[type='submit']")
|
|
86
|
+
.first().click();
|
|
87
|
+
await outer.locator(".sidebar-comment-reply .comment-changeTo-form input[type='submit']")
|
|
88
|
+
.first().click();
|
|
89
|
+
await expect.poll(async () =>
|
|
90
|
+
(await inner.locator('div').first().textContent())?.trim()).toBe('My suggestion');
|
|
91
|
+
});
|
|
92
|
+
});
|