0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Added Playwright test for comment reply pagination

refs https://github.com/TryGhost/Team/issues/3504
This commit is contained in:
Simon Backx 2023-06-22 16:44:58 +02:00 committed by Simon Backx
parent 9035a87e50
commit 9135ca92e0
3 changed files with 147 additions and 7 deletions

View file

@ -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: '<p>This is comment 1</p>',
replies: [
mockedApi.buildReply({
html: '<p>This is reply 1</p>'
}),
mockedApi.buildReply({
html: '<p>This is reply 2</p>'
}),
mockedApi.buildReply({
html: '<p>This is reply 3</p>'
}),
mockedApi.buildReply({
html: '<p>This is reply 4</p>'
})
]
});
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();
});
});

View file

@ -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
}))
});
});

View file

@ -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: '<p>Empty</p>',
replies: [],
count: {
@ -36,7 +36,7 @@ export function buildComment(override: any = {}) {
export function buildReply(override: any = {}) {
return {
id: ObjectId(),
id: ObjectId().toString(),
html: '<p>Empty</p>',
count: {
likes: 0