diff --git a/apps/comments-ui/test/e2e/pagination.test.ts b/apps/comments-ui/test/e2e/pagination.test.ts index ae39dcfc15..04de3dcdaf 100644 --- a/apps/comments-ui/test/e2e/pagination.test.ts +++ b/apps/comments-ui/test/e2e/pagination.test.ts @@ -58,5 +58,60 @@ test.describe('Pagination', async () => { // Check the pagination button is not visible await expect(frame.getByTestId('pagination-component')).not.toBeVisible(); }); + + test('Shows pagination button for replies', async ({page}) => { + const mockedApi = new MockedApi({}); + + mockedApi.addComment({ + html: '

This is comment 1

', + replies: [ + mockedApi.buildReply({ + html: '

This is reply 1

' + }), + mockedApi.buildReply({ + html: '

This is reply 2

' + }), + mockedApi.buildReply({ + html: '

This is reply 3

' + }), + mockedApi.buildReply({ + html: '

This is reply 4

' + }) + ] + }); + + const {frame} = await initialize({ + mockedApi, + page, + publication: 'Publisher Weekly' + }); + + await expect(frame.getByTestId('reply-pagination-button')).toBeVisible(); + + // Check text in pagination button + await expect(frame.getByTestId('reply-pagination-button')).toContainText('Show 1 more reply'); + + await expect(frame.getByTestId('comment-component')).toHaveCount(4); + + // Check only the first 5 comments are visible + await expect(frame.getByText('This is comment 1')).toBeVisible(); + await expect(frame.getByText('This is reply 1')).toBeVisible(); + await expect(frame.getByText('This is reply 2')).toBeVisible(); + await expect(frame.getByText('This is reply 3')).toBeVisible(); + await expect(frame.getByText('This is reply 4')).not.toBeVisible(); + + // Click the pagination button + await frame.getByTestId('reply-pagination-button').click(); + + // No longer visible + await expect(frame.getByTestId('reply-pagination-button')).not.toBeVisible(); + + await expect(frame.getByTestId('comment-component')).toHaveCount(5); + await expect(frame.getByText('This is comment 1')).toBeVisible(); + await expect(frame.getByText('This is reply 1')).toBeVisible(); + await expect(frame.getByText('This is reply 2')).toBeVisible(); + await expect(frame.getByText('This is reply 3')).toBeVisible(); + await expect(frame.getByText('This is reply 4')).toBeVisible(); + }); }); diff --git a/apps/comments-ui/test/utils/MockedApi.ts b/apps/comments-ui/test/utils/MockedApi.ts index e673235889..dbf418c8d6 100644 --- a/apps/comments-ui/test/utils/MockedApi.ts +++ b/apps/comments-ui/test/utils/MockedApi.ts @@ -1,5 +1,5 @@ import nql from '@tryghost/nql'; -import {buildComment, buildMember} from './fixtures'; +import {buildComment, buildMember, buildReply} from './fixtures'; export class MockedApi { comments: any[]; @@ -27,6 +27,18 @@ export class MockedApi { this.comments.push(fixture); } + buildReply(overrides: any = {}) { + if (!overrides.created_at) { + overrides.created_at = this.#lastCommentDate.toISOString(); + this.#lastCommentDate = new Date(this.#lastCommentDate.getTime() + 1); + } + + return buildReply({ + ...overrides, + post_id: this.postId + }); + } + addComments(count, overrides = {}) { for (let i = 0; i < count; i += 1) { this.addComment(overrides); @@ -72,7 +84,16 @@ export class MockedApi { const comments = filteredComments.slice(startIndex, endIndex); return { - comments, + comments: comments.map((comment) => { + return { + ...comment, + replies: comment.replies.slice(0, 3), + count: { + ...comment.count, + replies: comment.replies.length + } + }; + }), meta: { pagination: { pages: Math.ceil(filteredComments.length / limit), @@ -84,6 +105,51 @@ export class MockedApi { }; } + browseReplies({commentId, filter, limit = 5}: {commentId: string, filter?: string, limit?: number}) { + const comment = this.comments.find(c => c.id === commentId); + if (!comment) { + return { + error: 'Comment ' + commentId + ' not found' + }; + } + + let replies: any[] = comment.replies; + + // Sort replies on created at + id + replies.sort((a, b) => { + const aDate = new Date(a.created_at).getTime(); + const bDate = new Date(b.created_at).getTime(); + + if (aDate === bDate) { + return a.id > b.id ? 1 : -1; + } + + return aDate > bDate ? 1 : -1; + }); + + // Parse NQL filter + if (filter) { + const parsed = nql(filter); + replies = replies.filter((reply) => { + return parsed.queryJSON(reply); + }); + } + + const limitedReplies = replies.slice(0, limit); + + return { + comments: limitedReplies, + meta: { + pagination: { + pages: Math.ceil(replies.length / limit), + total: replies.length, + page: 1, + limit + } + } + }; + } + async listen({page, path}: {page: any, path: string}) { await page.route(`${path}/members/api/member/`, async (route) => { if (!this.member) { @@ -105,13 +171,32 @@ export class MockedApi { const p = parseInt(url.searchParams.get('page') ?? '1'); const limit = parseInt(url.searchParams.get('limit') ?? '5'); const order = url.searchParams.get('order') ?? ''; + const filter = url.searchParams.get('filter') ?? ''; await route.fulfill({ status: 200, body: JSON.stringify(this.browseComments({ page: p, limit, - order + order, + filter + })) + }); + }); + + await page.route(`${path}/members/api/comments/*/replies/*`, async (route) => { + const url = new URL(route.request().url()); + + const limit = parseInt(url.searchParams.get('limit') ?? '5'); + const commentId = url.pathname.split('/').reverse()[2]; + const filter = url.searchParams.get('filter') ?? ''; + + await route.fulfill({ + status: 200, + body: JSON.stringify(this.browseReplies({ + limit, + filter, + commentId })) }); }); diff --git a/apps/comments-ui/test/utils/fixtures.ts b/apps/comments-ui/test/utils/fixtures.ts index 7033d55b7d..c5b2696a0c 100644 --- a/apps/comments-ui/test/utils/fixtures.ts +++ b/apps/comments-ui/test/utils/fixtures.ts @@ -5,11 +5,11 @@ export function buildMember(override: any = {}) { memberCounter += 1; return { + id: ObjectId().toString(), avatar_image: 'https://www.gravatar.com/avatar/7a68f69cc9c9e9b45d97ecad6f24184a?s=250&r=g&d=blank', expertise: 'Head of Testing', - id: ObjectId(), name: 'Test Member ' + memberCounter, - uuid: ObjectId(), + uuid: ObjectId().toString(), paid: override.status === 'paid', status: 'free', ...override @@ -18,7 +18,7 @@ export function buildMember(override: any = {}) { export function buildComment(override: any = {}) { return { - id: ObjectId(), + id: ObjectId().toString(), html: '

Empty

', replies: [], count: { @@ -36,7 +36,7 @@ export function buildComment(override: any = {}) { export function buildReply(override: any = {}) { return { - id: ObjectId(), + id: ObjectId().toString(), html: '

Empty

', count: { likes: 0