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.
Files changed (29) hide show
  1. package/locales/be-tarask.json +23 -0
  2. package/locales/fr.json +2 -0
  3. package/locales/nl.json +1 -0
  4. package/locales/zh-hans.json +2 -0
  5. package/package.json +1 -1
  6. package/static/tests/frontend-new/helper/comments.ts +188 -0
  7. package/static/tests/frontend-new/specs/commentDelete.spec.ts +57 -0
  8. package/static/tests/frontend-new/specs/commentEdit.spec.ts +113 -0
  9. package/static/tests/frontend-new/specs/commentIcons.spec.ts +131 -0
  10. package/static/tests/frontend-new/specs/commentReply.spec.ts +92 -0
  11. package/static/tests/frontend-new/specs/commentSuggestion.spec.ts +97 -0
  12. package/static/tests/frontend-new/specs/comment_l10n.spec.ts +63 -0
  13. package/static/tests/frontend-new/specs/comment_settings.spec.ts +47 -0
  14. package/static/tests/frontend-new/specs/newComment.spec.ts +25 -0
  15. package/static/tests/frontend-new/specs/preCommentMark.spec.ts +123 -0
  16. package/static/tests/frontend-new/specs/timeFormat.spec.ts +244 -0
  17. package/static/tests/frontend-new/specs/xcommentCopyPaste.spec.ts +29 -0
  18. package/static/tests/frontend/specs/commentDelete.js +0 -146
  19. package/static/tests/frontend/specs/commentEdit.js +0 -291
  20. package/static/tests/frontend/specs/commentIcons.js +0 -298
  21. package/static/tests/frontend/specs/commentReply.js +0 -200
  22. package/static/tests/frontend/specs/commentSuggestion.js +0 -100
  23. package/static/tests/frontend/specs/comment_l10n.js +0 -141
  24. package/static/tests/frontend/specs/comment_settings.js +0 -132
  25. package/static/tests/frontend/specs/newComment.js +0 -18
  26. package/static/tests/frontend/specs/preCommentMark.js +0 -241
  27. package/static/tests/frontend/specs/timeFormat.js +0 -266
  28. package/static/tests/frontend/specs/xcommentCopyPaste.js +0 -480
  29. package/static/tests/frontend/utils.js +0 -14
@@ -0,0 +1,97 @@
1
+ import {expect, test} from '@playwright/test';
2
+ import {getPadBody, getPadOuter}
3
+ from 'ep_etherpad-lite/tests/frontend-new/helper/padHelper';
4
+ import {aNewCommentsPad} from '../helper/comments';
5
+
6
+ // Replace pad content with raw HTML and select all (mirrors legacy
7
+ // `$firstTextElement.html(targetText).sendkeys('{selectall}')`).
8
+ const setHtmlAndSelectAll = async (
9
+ page: import('@playwright/test').Page, html: string,
10
+ ) => {
11
+ const inner = await getPadBody(page);
12
+ await inner.click();
13
+ await page.keyboard.press('Control+A');
14
+ await page.keyboard.press('Delete');
15
+ // Inject raw HTML into the first line.
16
+ await inner.evaluate((root, h) => {
17
+ const first = root.querySelector('div');
18
+ if (first) first.innerHTML = h;
19
+ }, html);
20
+ // Select-all so the addComment button selection covers the inserted content.
21
+ await inner.click();
22
+ await page.keyboard.press('Control+A');
23
+ };
24
+
25
+ const openCommentFormWithSuggestion = async (
26
+ page: import('@playwright/test').Page, targetText: string,
27
+ ) => {
28
+ await setHtmlAndSelectAll(page, targetText);
29
+ await page.locator('.addComment').first().click();
30
+ await expect.poll(async () =>
31
+ page.locator('#newComment.popup-show .suggestion-checkbox').count()).toBeGreaterThan(0);
32
+ await page.locator('#newComment.popup-show .suggestion-checkbox').first().click();
33
+ };
34
+
35
+ test.describe('ep_comments_page - Comment Suggestion', () => {
36
+ test.beforeEach(async ({page}) => {
37
+ test.setTimeout(60_000);
38
+ await aNewCommentsPad(page);
39
+ });
40
+
41
+ test('Fills suggestion Change From field when adding a comment with suggestion',
42
+ async ({page}) => {
43
+ const targetText =
44
+ '<span>A</span><ul><li> text with</li><li> line attributes</li></ul>';
45
+ await openCommentFormWithSuggestion(page, targetText);
46
+ const text = await page.locator('.from-value').textContent();
47
+ expect(text).toBe('A\n text with\n line attributes');
48
+ });
49
+
50
+ test('Cancel suggestion and try again fills suggestion Change From field',
51
+ async ({page}) => {
52
+ const outer = await getPadOuter(page);
53
+ await openCommentFormWithSuggestion(page, 'This content will receive a comment');
54
+ await page.locator('#comment-reset').click();
55
+ await expect.poll(async () =>
56
+ outer.locator('#newComments.active').count()).toBe(0);
57
+ await openCommentFormWithSuggestion(page, 'New target for comment');
58
+ const text = await page.locator('.from-value').textContent();
59
+ expect(text).toBe('New target for comment');
60
+ });
61
+
62
+ test('Fills suggestion Change From field, adds sugestion', async ({page}) => {
63
+ const inner = await getPadBody(page);
64
+ const outer = await getPadOuter(page);
65
+ const origText = 'This content will receive a comment';
66
+ const suggestedText = 'amp: & dq: " sq: \' lt: < gt: > bs: \\ end';
67
+ await openCommentFormWithSuggestion(page, origText);
68
+
69
+ await expect(page.locator('#newComment.popup-show')).toBeVisible();
70
+ await page.locator('#newComment textarea.comment-content').fill('A new comment text');
71
+ // Legacy bug-compatible selector: chrome$('#newComment').find('suggestion-checkbox').click()
72
+ // — that selector is invalid (missing dot) and matches nothing, but the suggestion
73
+ // checkbox was already clicked inside openCommentFormWithSuggestion above, so the
74
+ // textarea.to-value is already visible.
75
+ await expect.poll(async () =>
76
+ page.locator('#newComment textarea.to-value').count()).toBeGreaterThan(0);
77
+ await page.locator('#newComment textarea.to-value').fill(suggestedText);
78
+ await page.locator('#comment-create-btn').click();
79
+
80
+ await expect.poll(async () =>
81
+ inner.locator('div').first().locator('.comment').count()).toBeGreaterThan(0);
82
+ await inner.locator('div').first().locator('.comment').first().click();
83
+ await expect.poll(async () =>
84
+ outer.locator('.comment-container .full-display-content:visible').count())
85
+ .toBeGreaterThan(0);
86
+ await expect.poll(async () => {
87
+ const t = await outer.locator('.comment-container .comment-title-wrapper .from-label')
88
+ .first().textContent();
89
+ return t && t.includes(suggestedText);
90
+ }).toBe(true);
91
+
92
+ await outer.locator('.approve-suggestion-btn:visible').first().click();
93
+ await expect.poll(async () =>
94
+ (await inner.locator('div').first().locator('.comment').textContent())?.trim())
95
+ .toBe(suggestedText);
96
+ });
97
+ });
@@ -0,0 +1,63 @@
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
+ changeLanguageTo,
7
+ getCommentIdOfLine,
8
+ setPadLines,
9
+ waitForCommentOnLine,
10
+ } from '../helper/comments';
11
+
12
+ const commentedText = 'This content will receive a comment';
13
+ const suggestedText = 'Change to this suggestion';
14
+
15
+ test.describe('ep_comments_page - l10n', () => {
16
+ test.beforeEach(async ({page}) => {
17
+ test.setTimeout(60_000);
18
+ await aNewCommentsPad(page);
19
+ await setPadLines(page, [commentedText]);
20
+ const inner = await getPadBody(page);
21
+ await inner.locator('div').first().click({clickCount: 3});
22
+ await page.locator('.addComment').first().click();
23
+ await page.locator('textarea.comment-content').fill('My comment');
24
+ await page.locator('#newComment .suggestion-checkbox').first().click();
25
+ await page.locator('textarea.to-value').fill(suggestedText);
26
+ await page.locator('.comment-buttons input[type=submit]').first().click();
27
+ await waitForCommentOnLine(page, 0);
28
+ await changeLanguageTo(page, 'en');
29
+ });
30
+
31
+ test.afterEach(async ({page}) => {
32
+ // Restore English to avoid breaking other tests.
33
+ await changeLanguageTo(page, 'en');
34
+ });
35
+
36
+ test('uses default values when language was not localized yet', async ({page}) => {
37
+ await changeLanguageTo(page, 'oc');
38
+ const outer = await getPadOuter(page);
39
+ const text = await outer.locator('.comment-suggest').first().textContent();
40
+ expect(text).toBe(
41
+ ' Include suggested change ');
42
+ });
43
+
44
+ test('localizes comment when Etherpad language is changed', async ({page}) => {
45
+ await changeLanguageTo(page, 'pt-br');
46
+ const outer = await getPadOuter(page);
47
+ const commentId = await getCommentIdOfLine(page, 0);
48
+ const text = await outer.locator(`#${commentId} .from-label`).first().textContent();
49
+ expect(text).toBe(`Alteração sugerida de "${commentedText}" para "${suggestedText}"`);
50
+ });
51
+
52
+ test("localizes 'new comment' form when Etherpad language is changed", async ({page}) => {
53
+ const inner = await getPadBody(page);
54
+ const outer = await getPadOuter(page);
55
+ // Select again and re-open the new-comment form.
56
+ await inner.locator('div').first().click({clickCount: 3});
57
+ await page.locator('.addComment').first().click();
58
+ await changeLanguageTo(page, 'pt-br');
59
+ const txt = await outer.locator('.new-comment label.label-suggestion-checkbox').first()
60
+ .textContent();
61
+ expect((txt || '').trim()).toBe('Incluir alteração sugerida');
62
+ });
63
+ });
@@ -0,0 +1,47 @@
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
+ } from '../helper/comments';
8
+
9
+ // Faithful 1:1 port — all original tests were xit (skipped) in the legacy spec.
10
+ test.describe('ep_comments_page - Comment settings', () => {
11
+ test.describe("when user unchecks 'Show Comments'", () => {
12
+ test.beforeEach(async ({page}) => {
13
+ test.setTimeout(60_000);
14
+ await aNewCommentsPad(page);
15
+ await chooseToShowComments(page, false);
16
+ });
17
+
18
+ test.skip('sidebar comments should not be visible when opening a new pad',
19
+ async ({page}) => {
20
+ // Force a new pad for validation on a brand new pad.
21
+ await aNewCommentsPad(page);
22
+ const outer = await getPadOuter(page);
23
+ await expect(outer.locator('#comments')).toBeHidden();
24
+ });
25
+
26
+ test.skip('sidebar comments not visible when adding a new comment to a new pad',
27
+ async ({page}) => {
28
+ await aNewCommentsPad(page);
29
+ const inner = await getPadBody(page);
30
+ const outer = await getPadOuter(page);
31
+ await inner.click();
32
+ await page.keyboard.press('Control+A');
33
+ await page.keyboard.press('Delete');
34
+ await page.keyboard.type('This content will receive a comment');
35
+ await inner.locator('div').first().click({clickCount: 3});
36
+ await page.locator('.addComment').first().click();
37
+ await page.locator('textarea.comment-content').fill('My comment');
38
+ await outer.locator('.suggestion-checkbox').first().click();
39
+ await outer.locator('textarea.to-value').first().fill('Change to this suggestion');
40
+ await page.locator('.comment-buttons input[type=submit]').first().click();
41
+ // After creating the comment, click Add Comment again — sidebar must remain hidden.
42
+ await inner.locator('div').first().click({clickCount: 3});
43
+ await page.locator('.addComment').first().click();
44
+ expect(await outer.locator('#comments:visible').count()).toBe(0);
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,25 @@
1
+ import {expect, test} from '@playwright/test';
2
+ import {getPadBody} from 'ep_etherpad-lite/tests/frontend-new/helper/padHelper';
3
+ import {aNewCommentsPad} from '../helper/comments';
4
+
5
+ test.describe('ep_comments_page - New comment', () => {
6
+ test('new comment button focuses on comment textarea', async ({page}) => {
7
+ await aNewCommentsPad(page);
8
+ const inner = await getPadBody(page);
9
+ await inner.click();
10
+ await page.keyboard.press('Control+A');
11
+ await page.keyboard.press('Delete');
12
+ await page.keyboard.type('commented text');
13
+ await inner.locator('div').first().click({clickCount: 3});
14
+
15
+ await page.locator('.addComment').click();
16
+
17
+ // The textarea must be the active element in the chrome document.
18
+ const isFocused = await page.evaluate(() => {
19
+ const ta = document.querySelector<HTMLTextAreaElement>(
20
+ '#newComment .comment-content');
21
+ return !!ta && document.activeElement === ta;
22
+ });
23
+ expect(isFocused).toBe(true);
24
+ });
25
+ });
@@ -0,0 +1,123 @@
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
+ highlightSelectedTextEnabled,
7
+ reopenCommentsPad,
8
+ waitForCommentOnLine,
9
+ } from '../helper/comments';
10
+
11
+ const createPadWithTwoLines = async (page: import('@playwright/test').Page) => {
12
+ const inner = await getPadBody(page);
13
+ await inner.click();
14
+ await page.keyboard.press('Control+A');
15
+ await page.keyboard.press('Delete');
16
+ await page.keyboard.type('Line 1');
17
+ await page.keyboard.press('Enter');
18
+ await page.keyboard.type('Line 2');
19
+ await expect.poll(async () => inner.locator('div').count()).toBeGreaterThanOrEqual(2);
20
+ };
21
+
22
+ const selectLineAndOpenCommentForm = async (
23
+ page: import('@playwright/test').Page, lineIndex: number,
24
+ ) => {
25
+ const inner = await getPadBody(page);
26
+ await inner.locator('div').nth(lineIndex).click({clickCount: 3});
27
+ await page.locator('.addComment').click();
28
+ };
29
+
30
+ test.describe('ep_comments_page - Pre-comment text mark', () => {
31
+ let padId: string;
32
+
33
+ test.beforeEach(async ({page}) => {
34
+ test.setTimeout(60_000);
35
+ padId = await aNewCommentsPad(page);
36
+ await createPadWithTwoLines(page);
37
+ await selectLineAndOpenCommentForm(page, 0);
38
+ });
39
+
40
+ test('marks selected text when New Comment form is opened', async ({page}) => {
41
+ if (!(await highlightSelectedTextEnabled(page))) return;
42
+ const inner = await getPadBody(page);
43
+ const marked = inner.locator('.pre-selected-comment');
44
+ await expect(marked).toHaveCount(1);
45
+ expect((await marked.textContent())?.trim()).toBe('Line 1');
46
+ });
47
+
48
+ test.describe('when user reloads pad', () => {
49
+ test('does not have any marked text after pad is fully loaded', async ({page}) => {
50
+ if (!(await highlightSelectedTextEnabled(page))) return;
51
+ // Wait for revision to be saved.
52
+ await page.waitForTimeout(5000);
53
+ await reopenCommentsPad(page, padId);
54
+ const inner = await getPadBody(page);
55
+ await expect.poll(async () =>
56
+ inner.locator('.pre-selected-comment').count(), {timeout: 20_000}).toBe(0);
57
+ });
58
+ });
59
+
60
+ test.describe('when user performs UNDO operation', () => {
61
+ test('keeps marked text', async ({page}) => {
62
+ if (!(await highlightSelectedTextEnabled(page))) return;
63
+ // Wait for revision and reload.
64
+ await page.waitForTimeout(5000);
65
+ await reopenCommentsPad(page, padId);
66
+ // Re-mark the text.
67
+ await selectLineAndOpenCommentForm(page, 0);
68
+ // Trigger UNDO.
69
+ await page.locator('.buttonicon-undo').click();
70
+ const inner = await getPadBody(page);
71
+ const marked = inner.locator('.pre-selected-comment');
72
+ await expect(marked).toHaveCount(1);
73
+ expect((await marked.textContent())?.trim()).toBe('Line 1');
74
+ });
75
+ });
76
+
77
+ test.describe('when user changes selected text', () => {
78
+ test('keeps marked text', async ({page}) => {
79
+ if (!(await highlightSelectedTextEnabled(page))) return;
80
+ const inner = await getPadBody(page);
81
+ // Select the second line.
82
+ await inner.locator('div').nth(1).click({clickCount: 3});
83
+ const marked = inner.locator('.pre-selected-comment');
84
+ await expect(marked).toHaveCount(1);
85
+ expect((await marked.textContent())?.trim()).toBe('Line 1');
86
+ });
87
+ });
88
+
89
+ test.describe('when user closes the New Comment form', () => {
90
+ test('unmarks text', async ({page}) => {
91
+ if (!(await highlightSelectedTextEnabled(page))) return;
92
+ const outer = await getPadOuter(page);
93
+ await outer.locator('#comment-reset').click();
94
+ const inner = await getPadBody(page);
95
+ await expect(inner.locator('.pre-selected-comment')).toHaveCount(0);
96
+ });
97
+ });
98
+
99
+ test.describe('when user submits the comment', () => {
100
+ test('unmarks text', async ({page}) => {
101
+ if (!(await highlightSelectedTextEnabled(page))) return;
102
+ const outer = await getPadOuter(page);
103
+ await page.locator('textarea.comment-content').fill('My comment');
104
+ await outer.locator('.suggestion-checkbox').first().click();
105
+ await outer.locator('textarea.to-value').first().fill('Change to this suggestion');
106
+ await page.locator('.comment-buttons input[type=submit]').first().click();
107
+ await waitForCommentOnLine(page, 0);
108
+ const inner = await getPadBody(page);
109
+ await expect(inner.locator('.pre-selected-comment')).toHaveCount(0);
110
+ });
111
+ });
112
+
113
+ test.describe('when user selects another text range and opens New Comment form for it', () => {
114
+ test('changes the marked text', async ({page}) => {
115
+ if (!(await highlightSelectedTextEnabled(page))) return;
116
+ await selectLineAndOpenCommentForm(page, 1);
117
+ const inner = await getPadBody(page);
118
+ const marked = inner.locator('.pre-selected-comment');
119
+ await expect(marked).toHaveCount(1);
120
+ expect((await marked.textContent())?.trim()).toBe('Line 2');
121
+ });
122
+ });
123
+ });
@@ -0,0 +1,244 @@
1
+ import {expect, test} from '@playwright/test';
2
+ import {aNewCommentsPad, changeLanguageTo} from '../helper/comments';
3
+
4
+ // Time helpers (mirror legacy).
5
+ const minutes = (n: number) => 60 * n;
6
+ const hours = (n: number) => 60 * minutes(n);
7
+ const days = (n: number) => 24 * hours(n);
8
+ const weeks = (n: number) => 7 * days(n);
9
+ const months = (n: number) => 4 * weeks(n);
10
+ const years = (n: number) => 12 * months(n);
11
+
12
+ // Evaluate `moment(<offsetMs from now>).fromNow()` inside the chrome window.
13
+ // We initialise moment lazily on first call and set its 'ss' threshold to 0 to
14
+ // match the legacy spec's `moment.relativeTimeThreshold('ss', 0)`.
15
+ const initMoment = async (page: import('@playwright/test').Page) => {
16
+ await page.evaluate(() => {
17
+ const w = window as any;
18
+ if (w.__epcpMoment) return;
19
+ w.__epcpMoment = w.require(
20
+ 'ep_comments_page/static/js/moment-with-locales.min');
21
+ w.__epcpMoment.relativeTimeThreshold('ss', 0);
22
+ });
23
+ };
24
+
25
+ const fromNow = async (
26
+ page: import('@playwright/test').Page, secondsOffset: number,
27
+ ): Promise<string> => {
28
+ return await page.evaluate((sec) => {
29
+ const w = window as any;
30
+ return w.__epcpMoment(Date.now() + sec * 1000).fromNow();
31
+ }, secondsOffset);
32
+ };
33
+
34
+ const waitMomentLocale = async (
35
+ page: import('@playwright/test').Page, expected: string,
36
+ ) => {
37
+ await expect.poll(async () => page.evaluate(() => (window as any).__epcpMoment.locale()))
38
+ .toBe(expected);
39
+ };
40
+
41
+ const switchLang = async (page: import('@playwright/test').Page, lang: string) => {
42
+ // First switch to a supported non-en language so we can detect when change took effect.
43
+ await changeLanguageTo(page, 'pt-br');
44
+ await waitMomentLocale(page, 'pt-br');
45
+ await changeLanguageTo(page, lang);
46
+ await waitMomentLocale(page, lang === 'qqq' ? 'en' : lang);
47
+ };
48
+
49
+ test.describe('ep_comments_page - Time format', () => {
50
+ test.beforeAll(async () => {
51
+ test.setTimeout(60_000);
52
+ });
53
+
54
+ for (const [lang, description] of Object.entries({
55
+ en: 'English',
56
+ qqq: 'a language that moment.js does not support',
57
+ })) {
58
+ test.describe(`in ${description}`, () => {
59
+ test.beforeEach(async ({page}) => {
60
+ test.setTimeout(60_000);
61
+ await aNewCommentsPad(page);
62
+ await initMoment(page);
63
+ await switchLang(page, lang);
64
+ });
65
+
66
+ test.afterEach(async ({page}) => {
67
+ // Restore English to avoid breaking other tests.
68
+ await changeLanguageTo(page, 'en');
69
+ });
70
+
71
+ test('returns "12 seconds ago" when time is 12 seconds in the past', async ({page}) => {
72
+ expect(await fromNow(page, -12)).toBe('12 seconds ago');
73
+ });
74
+ test('returns "in 12 seconds" when time is 12 seconds in the future', async ({page}) => {
75
+ expect(await fromNow(page, 12)).toBe('in 12 seconds');
76
+ });
77
+ test('returns "a minute ago" when time is 75 seconds in the past', async ({page}) => {
78
+ expect(await fromNow(page, -75)).toBe('a minute ago');
79
+ });
80
+ test('returns "in a minute" when time is 75 seconds in the future', async ({page}) => {
81
+ expect(await fromNow(page, 75)).toBe('in a minute');
82
+ });
83
+ test('returns "17 minutes ago" when time is 17+ε minutes in the past', async ({page}) => {
84
+ expect(await fromNow(page, -(minutes(17) + 2))).toBe('17 minutes ago');
85
+ });
86
+ test('returns "in 17 minutes" when time is 17+ε minutes in the future', async ({page}) => {
87
+ expect(await fromNow(page, minutes(17) + 2)).toBe('in 17 minutes');
88
+ });
89
+ test('returns "an hour ago" when time is 1+ε hour in the past', async ({page}) => {
90
+ expect(await fromNow(page, -(hours(1) + 3))).toBe('an hour ago');
91
+ });
92
+ test('returns "in an hour" when time is 1+ε hour in the future', async ({page}) => {
93
+ expect(await fromNow(page, hours(1) + 3)).toBe('in an hour');
94
+ });
95
+ test('returns "2 hours ago" when time is 2+ε hours in the past', async ({page}) => {
96
+ expect(await fromNow(page, -(hours(2) + 4))).toBe('2 hours ago');
97
+ });
98
+ test('returns "in 2 hours" when time is 2+ε hours in the future', async ({page}) => {
99
+ expect(await fromNow(page, hours(2) + 4)).toBe('in 2 hours');
100
+ });
101
+ test('returns "a day ago" when time is 24+ε hours in the past', async ({page}) => {
102
+ expect(await fromNow(page, -(hours(24) + 5))).toBe('a day ago');
103
+ });
104
+ test('returns "in a day" when time is 24+ε hours in the future', async ({page}) => {
105
+ expect(await fromNow(page, hours(24) + 5)).toBe('in a day');
106
+ });
107
+ test('returns "6 days ago" when time is 6+ε days in the past', async ({page}) => {
108
+ expect(await fromNow(page, -(days(6) + 6))).toBe('6 days ago');
109
+ });
110
+ test('returns "in 6 days" when time is 6+ε days in the future', async ({page}) => {
111
+ expect(await fromNow(page, days(6) + 6)).toBe('in 6 days');
112
+ });
113
+ test('returns "7 days ago" when time is 7+ε days in the past', async ({page}) => {
114
+ expect(await fromNow(page, -(days(7) + 7))).toBe('7 days ago');
115
+ });
116
+ test('returns "in 7 days" when time is 7+ε days in the future', async ({page}) => {
117
+ expect(await fromNow(page, days(7) + 7)).toBe('in 7 days');
118
+ });
119
+ test('returns "14 days ago" when time is 2+ε weeks in the past', async ({page}) => {
120
+ expect(await fromNow(page, -(weeks(2) + 8))).toBe('14 days ago');
121
+ });
122
+ test('returns "in 14 days" when time is 2+ε weeks in the future', async ({page}) => {
123
+ expect(await fromNow(page, weeks(2) + 8)).toBe('in 14 days');
124
+ });
125
+ test('returns "a month ago" when time is 4+ε weeks in the past', async ({page}) => {
126
+ expect(await fromNow(page, -(weeks(4) + 9))).toBe('a month ago');
127
+ });
128
+ test('returns "in a month" when time is 4+ε weeks in the future', async ({page}) => {
129
+ expect(await fromNow(page, weeks(4) + 9)).toBe('in a month');
130
+ });
131
+ test('returns "8 months ago" when time is 9+ε months in the past', async ({page}) => {
132
+ expect(await fromNow(page, -(months(9) + 10))).toBe('8 months ago');
133
+ });
134
+ test('returns "in 8 months" when time is 9+ε months in the future', async ({page}) => {
135
+ expect(await fromNow(page, months(9) + 10)).toBe('in 8 months');
136
+ });
137
+ test('returns "a year ago" when time is 12+ε months in the past', async ({page}) => {
138
+ expect(await fromNow(page, -(months(12) + 11))).toBe('a year ago');
139
+ });
140
+ test('returns "in a year" when time is 12+ε months in the future', async ({page}) => {
141
+ expect(await fromNow(page, months(12) + 11)).toBe('in a year');
142
+ });
143
+ test('returns "14 years ago" when time is 15+ε years in the past', async ({page}) => {
144
+ expect(await fromNow(page, -(years(15) + 12))).toBe('14 years ago');
145
+ });
146
+ test('returns " in 14 years" when time is 15+ε years in the future', async ({page}) => {
147
+ expect(await fromNow(page, years(15) + 12)).toBe('in 14 years');
148
+ });
149
+ });
150
+ }
151
+
152
+ test.describe('in Portuguese', () => {
153
+ test.beforeEach(async ({page}) => {
154
+ test.setTimeout(60_000);
155
+ await aNewCommentsPad(page);
156
+ await initMoment(page);
157
+ await changeLanguageTo(page, 'pt-br');
158
+ await waitMomentLocale(page, 'pt-br');
159
+ });
160
+
161
+ test.afterEach(async ({page}) => {
162
+ await changeLanguageTo(page, 'en');
163
+ });
164
+
165
+ test('returns "há 12 segundos" when time is 12 seconds in the past', async ({page}) => {
166
+ expect(await fromNow(page, -12)).toBe('há 12 segundos');
167
+ });
168
+ test('returns "em 12 segundos" when time is 12 seconds in the future', async ({page}) => {
169
+ expect(await fromNow(page, 12)).toBe('em 12 segundos');
170
+ });
171
+ test('returns "há um minuto" when time is 75 seconds in the past', async ({page}) => {
172
+ expect(await fromNow(page, -75)).toBe('há um minuto');
173
+ });
174
+ test('returns "em um minuto" when time is 75 seconds in the future', async ({page}) => {
175
+ expect(await fromNow(page, 75)).toBe('em um minuto');
176
+ });
177
+ test('returns "há 17 minutos" when time is 17+ε minutes in the past', async ({page}) => {
178
+ expect(await fromNow(page, -(minutes(17) + 2))).toBe('há 17 minutos');
179
+ });
180
+ test('returns "em 17 minutos" when time is 17+ε minutes in the future', async ({page}) => {
181
+ expect(await fromNow(page, minutes(17) + 2)).toBe('em 17 minutos');
182
+ });
183
+ test('returns "há uma hora" when time is 1+ε hour in the past', async ({page}) => {
184
+ expect(await fromNow(page, -(hours(1) + 3))).toBe('há uma hora');
185
+ });
186
+ test('returns "em uma hora" when time is 1+ε hour in the future', async ({page}) => {
187
+ expect(await fromNow(page, hours(1) + 3)).toBe('em uma hora');
188
+ });
189
+ test('returns "há 2 horas" when time is 2+ε hours in the past', async ({page}) => {
190
+ expect(await fromNow(page, -(hours(2) + 4))).toBe('há 2 horas');
191
+ });
192
+ test('returns "em 2 horas" when time is 2+ε hours in the future', async ({page}) => {
193
+ expect(await fromNow(page, hours(2) + 4)).toBe('em 2 horas');
194
+ });
195
+ test('returns "há um dia" when time is 24+ε hours in the past', async ({page}) => {
196
+ expect(await fromNow(page, -(hours(24) + 5))).toBe('há um dia');
197
+ });
198
+ test('returns "em um dia" when time is 24+ε hours in the future', async ({page}) => {
199
+ expect(await fromNow(page, hours(24) + 5)).toBe('em um dia');
200
+ });
201
+ test('returns "há 6 dias" when time is 6+ε days in the past', async ({page}) => {
202
+ expect(await fromNow(page, -(days(6) + 6))).toBe('há 6 dias');
203
+ });
204
+ test('returns "em 6 dias" when time is 6+ε days in the future', async ({page}) => {
205
+ expect(await fromNow(page, days(6) + 6)).toBe('em 6 dias');
206
+ });
207
+ test('returns "há 7 dias" when time is 7+ε days in the past', async ({page}) => {
208
+ expect(await fromNow(page, -(days(7) + 7))).toBe('há 7 dias');
209
+ });
210
+ test('returns "em 7 dias" when time is 7+ε days in the future', async ({page}) => {
211
+ expect(await fromNow(page, days(7) + 7)).toBe('em 7 dias');
212
+ });
213
+ test('returns "há 14 dias" when time is 2+ε weeks in the past', async ({page}) => {
214
+ expect(await fromNow(page, -(weeks(2) + 8))).toBe('há 14 dias');
215
+ });
216
+ test('returns "em 14 dias" when time is 2+ε weeks in the future', async ({page}) => {
217
+ expect(await fromNow(page, weeks(2) + 8)).toBe('em 14 dias');
218
+ });
219
+ test('returns "há um mês" when time is 4+ε weeks in the past', async ({page}) => {
220
+ expect(await fromNow(page, -(weeks(4) + 9))).toBe('há um mês');
221
+ });
222
+ test('returns "em um mês" when time is 4+ε weeks in the future', async ({page}) => {
223
+ expect(await fromNow(page, weeks(4) + 9)).toBe('em um mês');
224
+ });
225
+ test('returns "há 8 meses" when time is 9+ε months in the past', async ({page}) => {
226
+ expect(await fromNow(page, -(months(9) + 10))).toBe('há 8 meses');
227
+ });
228
+ test('returns "em 8 meses" when time is 9+ε months in the future', async ({page}) => {
229
+ expect(await fromNow(page, months(9) + 10)).toBe('em 8 meses');
230
+ });
231
+ test('returns "há um ano" when time is 12+ε months in the past', async ({page}) => {
232
+ expect(await fromNow(page, -(months(12) + 11))).toBe('há um ano');
233
+ });
234
+ test('returns "em um ano" when time is 12+ε months in the future', async ({page}) => {
235
+ expect(await fromNow(page, months(12) + 11)).toBe('em um ano');
236
+ });
237
+ test('returns "há 14 anos" when time is 15+ε years in the past', async ({page}) => {
238
+ expect(await fromNow(page, -(years(15) + 12))).toBe('há 14 anos');
239
+ });
240
+ test('returns "em 14 anos" when time is 15+ε years in the future', async ({page}) => {
241
+ expect(await fromNow(page, years(15) + 12)).toBe('em 14 anos');
242
+ });
243
+ });
244
+ });
@@ -0,0 +1,29 @@
1
+ import {test} from '@playwright/test';
2
+
3
+ // Faithful 1:1 port — every test in the legacy spec was xit (skipped) because
4
+ // the copy/paste flow could not be reliably triggered headlessly. We preserve
5
+ // the test names with test.skip so the migration is visible in CI.
6
+
7
+ test.describe('ep_comments_page - Comment copy and paste', () => {
8
+ test.describe('when user copies a text with a comment', () => {
9
+ test.skip('keeps the text copied on the buffer', async () => {});
10
+ test.skip('generates a fake comment class', async () => {});
11
+ test.skip('puts the comment data on the clipboardData', async () => {});
12
+ test.skip('puts the comment reply data on the clipboardData', async () => {});
13
+ test.skip('has the fields required to build a comment', async () => {});
14
+ test.skip('has the fields required to build a comment reply', async () => {});
15
+ });
16
+
17
+ test.describe('when user pastes a text with comment', () => {
18
+ test.skip('generates a different comment id for the comment pasted', async () => {});
19
+ test.skip('creates a new icon for the comment pasted', async () => {});
20
+ test.skip('creates the comment text field with the same text of the one copied',
21
+ async () => {});
22
+ test.skip('creates comment reply text field with the same text of the one copied',
23
+ async () => {});
24
+
25
+ test.describe('when user removes the original comment', () => {
26
+ test.skip('does not remove the comment pasted', async () => {});
27
+ });
28
+ });
29
+ });