mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-28 05:37:34 +03:00
Wired up admin api for hidden comments (#21724)
ref PLG-270 - Updated the getCommentByID service to filter out hidden and deleted replies. - Ensured all replies are loaded before applying the filter. - Simplified logic to handle non-paginated routes by directly removing unwanted replies. - Wired up new Admin Endpoint that shows hidden replies but not deleted replies. - Updated comments-ui client - Added unit tests for mocking apiClient event listeners. - added eventlistener playwright tests to ensure it fires on UI clicks.
This commit is contained in:
parent
63c210199f
commit
781bfdd60f
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@tryghost/comments-ui",
|
||||
"version": "0.22.4",
|
||||
"version": "0.23.1",
|
||||
"license": "MIT",
|
||||
"repository": "git@github.com:TryGhost/comments-ui.git",
|
||||
"author": "Ghost Foundation",
|
||||
|
@ -139,7 +139,13 @@ async function showComment({state, api, data: comment}: {state: EditableAppConte
|
||||
}
|
||||
// We need to refetch the comment, to make sure we have an up to date HTML content
|
||||
// + all relations are loaded as the current member (not the admin)
|
||||
const data = await api.comments.read(comment.id);
|
||||
let data;
|
||||
if (state.admin && state.adminApi && state.labs.commentImprovements) {
|
||||
data = await state.adminApi.read({commentId: comment.id});
|
||||
} else {
|
||||
data = await api.comments.read(comment.id);
|
||||
}
|
||||
|
||||
const updatedComment = data.comments[0];
|
||||
|
||||
return {
|
||||
|
333
apps/comments-ui/src/utils/adminAPI.test.ts
Normal file
333
apps/comments-ui/src/utils/adminAPI.test.ts
Normal file
@ -0,0 +1,333 @@
|
||||
import * as vi from 'vitest';
|
||||
import {setupAdminAPI} from './adminApi';
|
||||
|
||||
describe('setupAdminAPI', () => {
|
||||
let addEventListenerSpy: vi.SpyInstance;
|
||||
let postMessageMock: vi.Mock;
|
||||
let frame: HTMLIFrameElement;
|
||||
|
||||
beforeEach(() => {
|
||||
frame = document.createElement('iframe');
|
||||
frame.dataset.frame = 'admin-auth';
|
||||
Object.defineProperty(frame, 'contentWindow', {
|
||||
value: {
|
||||
postMessage: vi.vitest.fn()
|
||||
},
|
||||
writable: false
|
||||
});
|
||||
|
||||
document.body.appendChild(frame);
|
||||
|
||||
// Mock window.addEventListener - at runtime this gets injected into the theme.
|
||||
// from here https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/frontend/src/admin-auth/message-handler.js
|
||||
// In which case, we have to mock it in order to test it.
|
||||
addEventListenerSpy = vi.vitest.spyOn(window, 'addEventListener');
|
||||
postMessageMock = frame.contentWindow!.postMessage as vi.Mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore mocks and remove iframe
|
||||
vi.vitest.restoreAllMocks();
|
||||
frame.remove();
|
||||
});
|
||||
|
||||
it('can call getUser', async () => {
|
||||
const adminUrl = 'https://example.com';
|
||||
const api = setupAdminAPI({adminUrl});
|
||||
|
||||
const apiPromise = api.getUser();
|
||||
|
||||
const eventHandler = addEventListenerSpy.mock.calls.find(
|
||||
([eventType]) => eventType === 'message'
|
||||
)?.[1];
|
||||
|
||||
const mockEvent = new MessageEvent('message', {
|
||||
origin: new URL(adminUrl).origin,
|
||||
data: JSON.stringify({
|
||||
uid: 2,
|
||||
result: {
|
||||
users: [{id: 1, name: 'Test User'}]
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
eventHandler!(mockEvent);
|
||||
|
||||
const user = await apiPromise;
|
||||
|
||||
expect(user).toEqual({id: 1, name: 'Test User'});
|
||||
|
||||
expect(postMessageMock).toHaveBeenCalledWith(
|
||||
JSON.stringify({uid: 2, action: 'getUser'}),
|
||||
new URL(adminUrl).origin
|
||||
);
|
||||
});
|
||||
|
||||
it('can call hideComment', async () => {
|
||||
const adminUrl = 'https://example.com';
|
||||
const api = setupAdminAPI({adminUrl});
|
||||
|
||||
const apiPromise = api.hideComment('123');
|
||||
|
||||
const eventHandler = addEventListenerSpy.mock.calls.find(
|
||||
([eventType]) => eventType === 'message'
|
||||
)?.[1];
|
||||
|
||||
const mockEvent = new MessageEvent('message', {
|
||||
origin: new URL(adminUrl).origin,
|
||||
data: JSON.stringify({
|
||||
uid: 2,
|
||||
result: {success: true} // not the actual endpoint, we're just testing the event listener
|
||||
})
|
||||
});
|
||||
|
||||
eventHandler!(mockEvent);
|
||||
|
||||
const result = await apiPromise;
|
||||
|
||||
expect(result).toEqual({success: true});
|
||||
|
||||
expect(postMessageMock).toHaveBeenCalledWith(
|
||||
JSON.stringify({uid: 2, action: 'hideComment', id: '123'}),
|
||||
new URL(adminUrl).origin
|
||||
);
|
||||
});
|
||||
|
||||
it('can call showComment', async () => {
|
||||
const adminUrl = 'https://example.com';
|
||||
const api = setupAdminAPI({adminUrl});
|
||||
|
||||
const apiPromise = api.showComment('123');
|
||||
|
||||
const eventHandler = addEventListenerSpy.mock.calls.find(
|
||||
([eventType]) => eventType === 'message'
|
||||
)?.[1];
|
||||
|
||||
const mockEvent = new MessageEvent('message', {
|
||||
origin: new URL(adminUrl).origin,
|
||||
data: JSON.stringify({
|
||||
uid: 2,
|
||||
result: {success: true} // not the actual data, we're just testing the event listener and functions execution
|
||||
})
|
||||
});
|
||||
|
||||
eventHandler!(mockEvent);
|
||||
|
||||
const result = await apiPromise;
|
||||
|
||||
expect(result).toEqual({success: true});
|
||||
|
||||
expect(postMessageMock).toHaveBeenCalledWith(
|
||||
JSON.stringify({uid: 2, action: 'showComment', id: '123'}),
|
||||
new URL(adminUrl).origin
|
||||
);
|
||||
});
|
||||
|
||||
it('can call browse', async () => {
|
||||
const adminUrl = 'https://example.com';
|
||||
const api = setupAdminAPI({adminUrl});
|
||||
|
||||
const apiPromise = api.browse({page: 1, postId: '123', order: 'asc'});
|
||||
|
||||
const eventHandler = addEventListenerSpy.mock.calls.find(
|
||||
([eventType]) => eventType === 'message'
|
||||
)?.[1];
|
||||
|
||||
const mockEvent = new MessageEvent('message', {
|
||||
origin: new URL(adminUrl).origin,
|
||||
data: JSON.stringify({
|
||||
uid: 2,
|
||||
result: {
|
||||
comments: [{id: 1, body: 'Test Comment'}],
|
||||
meta: {
|
||||
pagination: {
|
||||
page: 1,
|
||||
limit: 15,
|
||||
pages: 1,
|
||||
total: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
eventHandler!(mockEvent);
|
||||
|
||||
const result = await apiPromise;
|
||||
|
||||
expect(result).toEqual({
|
||||
comments: [{id: 1, body: 'Test Comment'}],
|
||||
meta: {
|
||||
pagination: {
|
||||
page: 1,
|
||||
limit: 15,
|
||||
pages: 1,
|
||||
total: 1
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(postMessageMock).toHaveBeenCalledWith(
|
||||
JSON.stringify({uid: 2, action: 'browseComments', postId: '123', params: 'limit=20&page=1&order=asc'}),
|
||||
new URL(adminUrl).origin
|
||||
);
|
||||
});
|
||||
|
||||
it('can call replies', async () => {
|
||||
const adminUrl = 'https://example.com';
|
||||
const api = setupAdminAPI({adminUrl});
|
||||
|
||||
const apiPromise = api.replies({commentId: '123', afterReplyId: '456', limit: 10});
|
||||
|
||||
const eventHandler = addEventListenerSpy.mock.calls.find(
|
||||
([eventType]) => eventType === 'message'
|
||||
)?.[1];
|
||||
|
||||
const mockEvent = new MessageEvent('message', {
|
||||
origin: new URL(adminUrl).origin,
|
||||
data: JSON.stringify({
|
||||
uid: 2,
|
||||
result: {
|
||||
comments: [{id: 1, body: 'Test Reply'}]
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
eventHandler!(mockEvent);
|
||||
|
||||
const result = await apiPromise;
|
||||
|
||||
expect(result).toEqual({
|
||||
comments: [{id: 1, body: 'Test Reply'}]
|
||||
});
|
||||
|
||||
expect(postMessageMock).toHaveBeenCalledWith(
|
||||
JSON.stringify({
|
||||
uid: 2,
|
||||
action: 'getReplies',
|
||||
commentId: '123',
|
||||
params: 'limit=10&filter=id%3A%3E%27456%27'
|
||||
}),
|
||||
new URL(adminUrl).origin
|
||||
);
|
||||
});
|
||||
|
||||
it('can call read', async () => {
|
||||
const adminUrl = 'https://example.com';
|
||||
const api = setupAdminAPI({adminUrl});
|
||||
|
||||
const apiPromise = api.read({commentId: '123'});
|
||||
|
||||
const eventHandler = addEventListenerSpy.mock.calls.find(
|
||||
([eventType]) => eventType === 'message'
|
||||
)?.[1];
|
||||
|
||||
const mockEvent = new MessageEvent('message', {
|
||||
origin: new URL(adminUrl).origin,
|
||||
data: JSON.stringify({
|
||||
uid: 2,
|
||||
result: {
|
||||
comments: [{id: 1, body: 'Test Comment'}]
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
eventHandler!(mockEvent);
|
||||
|
||||
const result = await apiPromise;
|
||||
|
||||
expect(result).toEqual({comments: [{id: 1, body: 'Test Comment'}]});
|
||||
|
||||
expect(postMessageMock).toHaveBeenCalledWith(
|
||||
JSON.stringify({uid: 2, action: 'readComment', commentId: '123'}),
|
||||
new URL(adminUrl).origin
|
||||
);
|
||||
});
|
||||
|
||||
it('should call postMessage with the correct data on API call', async () => {
|
||||
const adminUrl = 'https://example.com';
|
||||
const api = setupAdminAPI({adminUrl});
|
||||
|
||||
// Simulate an API call
|
||||
const apiPromise = api.getUser();
|
||||
|
||||
// Simulate a message event to resolve the promise
|
||||
const eventHandler = addEventListenerSpy.mock.calls.find(
|
||||
([eventType]) => eventType === 'message'
|
||||
)?.[1];
|
||||
|
||||
const mockEvent = new MessageEvent('message', {
|
||||
origin: new URL(adminUrl).origin,
|
||||
data: JSON.stringify({
|
||||
uid: 2, // Mock UID from the handler
|
||||
result: {
|
||||
users: [{id: 1, name: 'Test User'}]
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Trigger the event handler manually
|
||||
eventHandler!(mockEvent);
|
||||
|
||||
// Await the result
|
||||
const user = await apiPromise;
|
||||
|
||||
expect(user).toEqual({id: 1, name: 'Test User'});
|
||||
expect(postMessageMock).toHaveBeenCalledWith(
|
||||
JSON.stringify({uid: 2, action: 'getUser'}),
|
||||
new URL(adminUrl).origin
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject the promise if an error occurs', async () => {
|
||||
const adminUrl = 'https://example.com';
|
||||
const api = setupAdminAPI({adminUrl});
|
||||
|
||||
// Simulate an API call
|
||||
const apiPromise = api.getUser();
|
||||
|
||||
// Simulate a message event with an error
|
||||
const eventHandler = addEventListenerSpy.mock.calls.find(
|
||||
([eventType]) => eventType === 'message'
|
||||
)?.[1];
|
||||
|
||||
const mockEvent = new MessageEvent('message', {
|
||||
origin: new URL(adminUrl).origin,
|
||||
data: JSON.stringify({
|
||||
uid: 2, // Mock UID from the handler
|
||||
error: {message: 'Test Error'}
|
||||
})
|
||||
});
|
||||
|
||||
// Trigger the event handler manually
|
||||
eventHandler!(mockEvent);
|
||||
|
||||
await expect(apiPromise).rejects.toEqual({message: 'Test Error'});
|
||||
});
|
||||
|
||||
it('should ignore messages from an invalid origin', async () => {
|
||||
const adminUrl = 'https://example.com';
|
||||
const api = setupAdminAPI({adminUrl});
|
||||
|
||||
const apiPromise = api.getUser();
|
||||
|
||||
// Simulate a message event from an invalid origin
|
||||
const eventHandler = addEventListenerSpy.mock.calls.find(
|
||||
([eventType]) => eventType === 'message'
|
||||
)?.[1];
|
||||
|
||||
const mockEvent = new MessageEvent('message', {
|
||||
origin: 'https://invalid.com',
|
||||
data: JSON.stringify({
|
||||
uid: 2,
|
||||
result: {users: [{id: 1, name: 'Invalid User'}]}
|
||||
})
|
||||
});
|
||||
|
||||
// Trigger the event handler manually
|
||||
eventHandler!(mockEvent);
|
||||
|
||||
// Ensure the promise doesn't resolve
|
||||
await expect(Promise.race([apiPromise, Promise.resolve('unresolved')])).resolves.toBe('unresolved');
|
||||
});
|
||||
});
|
@ -105,6 +105,11 @@ export function setupAdminAPI({adminUrl}: {adminUrl: string}) {
|
||||
|
||||
const response = await callApi('getReplies', {commentId, params: params.toString()});
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
async read({commentId}: {commentId: string}) {
|
||||
const response = await callApi('readComment', {commentId});
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
@ -240,5 +240,105 @@ test.describe('Auth Frame', async () => {
|
||||
await moreButtons.nth(1).getByText('Show comment').click();
|
||||
await expect(secondComment).toContainText('This is comment 2');
|
||||
});
|
||||
});
|
||||
|
||||
test('authFrameMain fires getUser (exposed function)', async ({page}) => {
|
||||
const mockedApi = new MockedApi({});
|
||||
mockedApi.addComment({
|
||||
html: '<p>This is comment 1</p>'
|
||||
});
|
||||
mockedApi.addComment({
|
||||
html: '<p>This is comment 2</p>'
|
||||
});
|
||||
mockedApi.addComment({
|
||||
html: '<p>This is comment 3</p>'
|
||||
});
|
||||
mockedApi.addComment({
|
||||
html: '<p>This is comment 4</p>'
|
||||
});
|
||||
mockedApi.addComment({
|
||||
html: '<p>This is comment 5</p>'
|
||||
});
|
||||
|
||||
const actions: string[] = [];
|
||||
|
||||
await page.exposeFunction('__testHelper', (action: string) => {
|
||||
actions.push(action);
|
||||
});
|
||||
|
||||
await mockAdminAuthFrame({
|
||||
admin,
|
||||
page
|
||||
});
|
||||
|
||||
await initialize({
|
||||
mockedApi,
|
||||
page,
|
||||
publication: 'Publisher Weekly',
|
||||
admin,
|
||||
labs: {
|
||||
commentImprovements: true
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger the message event
|
||||
await page.evaluate(() => {
|
||||
const event = new MessageEvent('message', {
|
||||
data: JSON.stringify({uid: 'test', action: 'getUser'}),
|
||||
origin: 'https://localhost:1234'
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
});
|
||||
|
||||
// Validate that "getUser" was captured
|
||||
expect(actions).toContain('getUser');
|
||||
});
|
||||
|
||||
test('fires admin read when making a hidden comment visible', async ({page}) => {
|
||||
const mockedApi = new MockedApi({});
|
||||
|
||||
mockedApi.addComment({
|
||||
html: '<p>This is comment 1</p>'
|
||||
});
|
||||
mockedApi.addComment({
|
||||
html: '<p>This is comment 2</p>',
|
||||
status: 'hidden'
|
||||
});
|
||||
|
||||
const actions: string[] = [];
|
||||
|
||||
await page.exposeFunction('__testHelper', (action: string) => {
|
||||
actions.push(action);
|
||||
});
|
||||
|
||||
await mockAdminAuthFrame({
|
||||
admin,
|
||||
page
|
||||
});
|
||||
|
||||
const {frame} = await initialize({
|
||||
mockedApi,
|
||||
page,
|
||||
publication: 'Publisher Weekly',
|
||||
admin,
|
||||
labs: {
|
||||
commentImprovements: true
|
||||
}
|
||||
});
|
||||
|
||||
const iframeElement = await page.locator('iframe[data-frame="admin-auth"]');
|
||||
await expect(iframeElement).toHaveCount(1);
|
||||
|
||||
// Check if more actions button is visible on each comment
|
||||
const comments = await frame.getByTestId('comment-component');
|
||||
await expect(comments).toHaveCount(2);
|
||||
|
||||
const moreButtons = await frame.getByTestId('more-button');
|
||||
await expect(moreButtons).toHaveCount(2);
|
||||
|
||||
// Click the 2nd button
|
||||
await moreButtons.nth(1).click();
|
||||
await moreButtons.nth(1).getByText('Show comment').click();
|
||||
|
||||
await expect(actions).toContain('readComment');
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {E2E_PORT} from '../../playwright.config';
|
||||
import {LabsType, MockedApi} from './MockedApi';
|
||||
import {Locator, Page} from '@playwright/test';
|
||||
import {MockedApi} from './MockedApi';
|
||||
import {expect} from '@playwright/test';
|
||||
|
||||
export const MOCKED_SITE_URL = 'https://localhost:1234';
|
||||
@ -21,6 +21,12 @@ function escapeHtml(unsafe: string) {
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__testHelper?: (action: string) => void;
|
||||
}
|
||||
}
|
||||
|
||||
function authFrameMain() {
|
||||
window.addEventListener('message', function (event) {
|
||||
let d = null;
|
||||
@ -44,6 +50,9 @@ function authFrameMain() {
|
||||
}
|
||||
|
||||
if (data.action === 'getUser') {
|
||||
if (window.__testHelper) {
|
||||
window.__testHelper('getUser');
|
||||
}
|
||||
try {
|
||||
respond(null, {
|
||||
users: [
|
||||
@ -58,6 +67,23 @@ function authFrameMain() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.action === 'readComment') {
|
||||
if (window.__testHelper) {
|
||||
window.__testHelper('readComment');
|
||||
}
|
||||
try {
|
||||
respond(null, {
|
||||
comment: {
|
||||
id: 'comment-id',
|
||||
html: '<p>This is a comment</p>'
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
respond(err, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Other actions: return empty object
|
||||
try {
|
||||
respond(null, {});
|
||||
|
@ -49,6 +49,17 @@ window.addEventListener('message', async function (event) {
|
||||
}
|
||||
}
|
||||
|
||||
if (data.action === 'readComment') {
|
||||
try {
|
||||
const {commentId} = data;
|
||||
const res = await fetch(adminUrl + '/comments/' + commentId + '/');
|
||||
const json = await res.json();
|
||||
respond(null, json);
|
||||
} catch (err) {
|
||||
respond(err, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.action === 'getUser') {
|
||||
try {
|
||||
const res = await fetch(
|
||||
|
@ -1,6 +1,7 @@
|
||||
// This is a new endpoint for the admin API to return replies to a comment with pagination
|
||||
|
||||
const commentsService = require('../../services/comments');
|
||||
const ALLOWED_INCLUDES = ['member', 'replies', 'replies.member', 'replies.count.likes', 'replies.liked', 'count.replies', 'count.likes', 'liked', 'post', 'parent'];
|
||||
|
||||
/** @type {import('@tryghost/api-framework').Controller} */
|
||||
const controller = {
|
||||
@ -30,6 +31,28 @@ const controller = {
|
||||
query(frame) {
|
||||
return commentsService.controller.adminReplies(frame);
|
||||
}
|
||||
},
|
||||
read: {
|
||||
headers: {
|
||||
cacheInvalidate: false
|
||||
},
|
||||
options: [
|
||||
'include'
|
||||
],
|
||||
data: [
|
||||
'id',
|
||||
'email'
|
||||
],
|
||||
validation: {
|
||||
options: {
|
||||
include: ALLOWED_INCLUDES
|
||||
}
|
||||
},
|
||||
permissions: true,
|
||||
query(frame) {
|
||||
frame.options.isAdmin = true;
|
||||
return commentsService.controller.read(frame);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2,6 +2,7 @@ const tpl = require('@tryghost/tpl');
|
||||
const errors = require('@tryghost/errors');
|
||||
const {MemberCommentEvent} = require('@tryghost/member-events');
|
||||
const DomainEvents = require('@tryghost/domain-events');
|
||||
const labs = require('../../../shared/labs');
|
||||
|
||||
const messages = {
|
||||
commentNotFound: 'Comment could not be found',
|
||||
@ -208,6 +209,28 @@ class CommentsService {
|
||||
});
|
||||
}
|
||||
|
||||
if (labs.isSet('commentImprovements')) {
|
||||
const replies = model.related('replies'); // Get the loaded replies relation
|
||||
await replies.fetch({withRelated: ['member', 'count.likes']}); // Fetch all replies
|
||||
|
||||
if (replies && replies.length > 0) {
|
||||
// Filter out deleted replies for all, and hidden replies for non-admins
|
||||
replies.remove(
|
||||
replies.filter((reply) => {
|
||||
const status = reply.get('status');
|
||||
if (status === 'deleted') {
|
||||
return true;
|
||||
} // Always remove deleted replies
|
||||
if (!options.isAdmin && status === 'hidden') {
|
||||
return true;
|
||||
} // Remove hidden replies for non-admins
|
||||
return false; // Keep others
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// this route does not need to handle pagination, so we can remove hidden/deleted replies here
|
||||
return model;
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ module.exports = function apiRoutes() {
|
||||
|
||||
router.get('/mentions', mw.authAdminApi, http(api.mentions.browse));
|
||||
|
||||
router.get('/comments/:id', mw.authAdminApi, http(api.commentReplies.read));
|
||||
router.get('/comments/:id/replies', mw.authAdminApi, http(api.commentReplies.browse));
|
||||
router.get('/comments/post/:post_id', mw.authAdminApi, http(api.comments.browse));
|
||||
router.put('/comments/:id', mw.authAdminApi, http(api.comments.edit));
|
||||
|
@ -210,7 +210,7 @@
|
||||
},
|
||||
"comments": {
|
||||
"url": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/comments-ui.min.js",
|
||||
"version": "0.22"
|
||||
"version": "0.23"
|
||||
},
|
||||
"signupForm": {
|
||||
"url": "https://cdn.jsdelivr.net/ghost/signup-form@~{version}/umd/signup-form.min.js",
|
||||
|
@ -458,6 +458,97 @@ describe('Admin Comments API', function () {
|
||||
assert.equal(res2.body.comments[0].html, 'Reply 4');
|
||||
});
|
||||
|
||||
it('Does not return deleted replies', async function () {
|
||||
const post = fixtureManager.get('posts', 1);
|
||||
await mockManager.mockLabsEnabled('commentImprovements');
|
||||
const {parent} = await dbFns.addCommentWithReplies({
|
||||
post_id: post.id,
|
||||
member_id: fixtureManager.get('members', 0).id,
|
||||
replies: [{
|
||||
member_id: fixtureManager.get('members', 1).id,
|
||||
status: 'hidden'
|
||||
}, {
|
||||
member_id: fixtureManager.get('members', 2).id,
|
||||
status: 'deleted'
|
||||
},
|
||||
{
|
||||
member_id: fixtureManager.get('members', 3).id,
|
||||
status: 'hidden'
|
||||
},
|
||||
{
|
||||
member_id: fixtureManager.get('members', 4).id,
|
||||
status: 'published'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const res = await adminApi.get(`/comments/${parent.get('id')}/`);
|
||||
res.body.comments[0].replies.length.should.eql(3);
|
||||
|
||||
res.body.comments[0].replies[0].member.should.be.an.Object().with.properties('id', 'uuid', 'name', 'avatar_image');
|
||||
|
||||
res.body.comments[0].replies[0].should.be.an.Object().with.properties('id', 'html', 'status', 'created_at', 'member', 'count');
|
||||
});
|
||||
|
||||
it('Does return published replies', async function () {
|
||||
const post = fixtureManager.get('posts', 1);
|
||||
await mockManager.mockLabsEnabled('commentImprovements');
|
||||
const {parent} = await dbFns.addCommentWithReplies({
|
||||
post_id: post.id,
|
||||
member_id: fixtureManager.get('members', 0).id,
|
||||
replies: [{
|
||||
member_id: fixtureManager.get('members', 1).id,
|
||||
status: 'published'
|
||||
}, {
|
||||
member_id: fixtureManager.get('members', 2).id,
|
||||
status: 'published'
|
||||
},
|
||||
{
|
||||
member_id: fixtureManager.get('members', 3).id,
|
||||
status: 'published'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const res = await adminApi.get(`/comments/${parent.get('id')}/`);
|
||||
res.body.comments[0].replies.length.should.eql(3);
|
||||
res.body.comments[0].replies[0].member.should.be.an.Object().with.properties('id', 'uuid', 'name', 'avatar_image');
|
||||
res.body.comments[0].replies[0].should.be.an.Object().with.properties('id', 'html', 'status', 'created_at', 'member', 'count');
|
||||
});
|
||||
|
||||
it('Does return published and hidden replies but not deleted', async function () {
|
||||
const post = fixtureManager.get('posts', 1);
|
||||
await mockManager.mockLabsEnabled('commentImprovements');
|
||||
const {parent} = await dbFns.addCommentWithReplies({
|
||||
post_id: post.id,
|
||||
member_id: fixtureManager.get('members', 0).id,
|
||||
replies: [{
|
||||
member_id: fixtureManager.get('members', 1).id,
|
||||
status: 'published'
|
||||
}, {
|
||||
member_id: fixtureManager.get('members', 2).id,
|
||||
status: 'published'
|
||||
},
|
||||
{
|
||||
member_id: fixtureManager.get('members', 3).id,
|
||||
status: 'published'
|
||||
},
|
||||
{
|
||||
member_id: fixtureManager.get('members', 4).id,
|
||||
status: 'hidden'
|
||||
},
|
||||
{
|
||||
member_id: fixtureManager.get('members', 5).id,
|
||||
status: 'deleted'
|
||||
}
|
||||
]
|
||||
});
|
||||
const res = await adminApi.get(`/comments/${parent.get('id')}/`);
|
||||
res.body.comments[0].replies.length.should.eql(4);
|
||||
res.body.comments[0].replies[0].member.should.be.an.Object().with.properties('id', 'uuid', 'name', 'avatar_image');
|
||||
res.body.comments[0].replies[0].should.be.an.Object().with.properties('id', 'html', 'status', 'created_at', 'member', 'count');
|
||||
});
|
||||
|
||||
it('ensure replies are always ordered from oldest to newest', async function () {
|
||||
const post = fixtureManager.get('posts', 1);
|
||||
const {parent} = await dbFns.addCommentWithReplies({
|
||||
|
Loading…
Reference in New Issue
Block a user