diff --git a/tests/e2e/specs/moderate-comments.test.js b/tests/e2e/specs/moderate-comments.test.js new file mode 100644 index 0000000000000..624089862bf37 --- /dev/null +++ b/tests/e2e/specs/moderate-comments.test.js @@ -0,0 +1,241 @@ +/** + * WordPress dependencies + */ +import { test, expect } from '@wordpress/e2e-test-utils-playwright'; + +/** The comment content used across tests. */ +const COMMENT_CONTENT = 'A comment awaiting moderation.'; + +/** + * Creates a post with a comment in the given moderation status. + * + * Comments are created via the REST API as the admin user (which approves + * them automatically), then moved to the requested status so each test + * starts from a known moderation state. + * + * @param {import('@wordpress/e2e-test-utils-playwright').RequestUtils} requestUtils The request utils fixture. + * @param {string} status Comment status: 'approved' or 'hold'. + * @param {string} [content] Comment content. + * @return {Promise<{post: Object, comment: Object}>} The created records. + */ +async function createPostWithComment( + requestUtils, + status, + content = COMMENT_CONTENT +) { + const post = await requestUtils.createPost( { + title: 'Post with comments', + content: '

Comment target.

', + status: 'publish', + } ); + const comment = await requestUtils.createComment( { + post: post.id, + content, + } ); + + if ( status !== 'approved' ) { + await requestUtils.rest( { + method: 'PUT', + path: `/wp/v2/comments/${ comment.id }`, + data: { status }, + } ); + } + + return { post, comment }; +} + +/** + * Reads a comment's current status via the REST API. + * + * @param {import('@wordpress/e2e-test-utils-playwright').RequestUtils} requestUtils The request utils fixture. + * @param {number} commentId The comment ID. + * @return {Promise} The comment status. + */ +async function getCommentStatus( requestUtils, commentId ) { + const comment = await requestUtils.rest( { + path: `/wp/v2/comments/${ commentId }`, + params: { context: 'edit' }, + } ); + return comment.status; +} + +test.describe( 'Moderate Comments', () => { + test.beforeEach( async ( { requestUtils } ) => { + await requestUtils.deleteAllComments(); + await requestUtils.deleteAllPosts(); + } ); + + test( 'approves a pending comment', async ( { + admin, + page, + requestUtils, + } ) => { + const { comment } = await createPostWithComment( requestUtils, 'hold' ); + + await admin.visitAdminPage( + '/edit-comments.php', + 'comment_status=moderated' + ); + + const row = page.locator( `#comment-${ comment.id }` ); + await expect( row ).toBeVisible(); + + // Row actions appear on hover. + await row.hover(); + await row + .getByRole( 'button', { name: 'Approve this comment' } ) + .click(); + + await expect + .poll( () => getCommentStatus( requestUtils, comment.id ), { + message: 'the comment should be approved', + } ) + .toBe( 'approved' ); + } ); + + test( 'unapproves an approved comment', async ( { + admin, + page, + requestUtils, + } ) => { + const { comment } = await createPostWithComment( + requestUtils, + 'approved' + ); + + await admin.visitAdminPage( + '/edit-comments.php', + 'comment_status=approved' + ); + + const row = page.locator( `#comment-${ comment.id }` ); + await expect( row ).toBeVisible(); + + await row.hover(); + await row + .getByRole( 'button', { name: 'Unapprove this comment' } ) + .click(); + + await expect + .poll( () => getCommentStatus( requestUtils, comment.id ), { + message: 'the comment should return to moderation', + } ) + .toBe( 'hold' ); + } ); + + test( 'marks a comment as spam', async ( { + admin, + page, + requestUtils, + } ) => { + const { comment } = await createPostWithComment( + requestUtils, + 'approved' + ); + + await admin.visitAdminPage( '/edit-comments.php' ); + + const row = page.locator( `#comment-${ comment.id }` ); + await expect( row ).toBeVisible(); + + await row.hover(); + await row + .getByRole( 'button', { name: 'Mark this comment as spam' } ) + .click(); + + await expect + .poll( () => getCommentStatus( requestUtils, comment.id ), { + message: 'the comment should be marked as spam', + } ) + .toBe( 'spam' ); + } ); + + test( 'replies to a comment from the admin', async ( { + admin, + page, + requestUtils, + } ) => { + const { post, comment } = await createPostWithComment( + requestUtils, + 'approved' + ); + + await admin.visitAdminPage( '/edit-comments.php' ); + + const row = page.locator( `#comment-${ comment.id }` ); + await expect( row ).toBeVisible(); + + await row.hover(); + await row + .getByRole( 'button', { + name: 'Reply to this comment', + } ) + .click(); + + // The inline reply form replaces the row. + const replyField = page.getByRole( 'textbox', { name: 'Comment' } ); + await expect( replyField ).toBeVisible(); + await replyField.fill( 'An admin reply.' ); + await page + .getByRole( 'button', { name: 'Reply', exact: true } ) + .click(); + + // The reply lands as an approved comment on the same post. + await expect + .poll( async () => { + const comments = await requestUtils.rest( { + path: '/wp/v2/comments', + params: { post: post.id, status: 'approve' }, + } ); + return comments.length; + } ) + .toBe( 2 ); + } ); + + test( 'moves multiple comments to spam with a bulk action', async ( { + admin, + page, + requestUtils, + } ) => { + const { post, comment } = await createPostWithComment( + requestUtils, + 'hold' + ); + const secondComment = await requestUtils.createComment( { + post: post.id, + content: 'A second comment awaiting moderation.', + } ); + await requestUtils.rest( { + method: 'PUT', + path: `/wp/v2/comments/${ secondComment.id }`, + data: { status: 'hold' }, + } ); + + await admin.visitAdminPage( + '/edit-comments.php', + 'comment_status=moderated' + ); + + // Select every comment in the list. + await page + .getByRole( 'checkbox', { name: 'Select All' } ) + .first() + .check(); + await page + .getByLabel( 'Select bulk action' ) + .first() + .selectOption( 'spam' ); + await page.getByRole( 'button', { name: 'Apply' } ).first().click(); + + await expect + .poll( () => getCommentStatus( requestUtils, comment.id ), { + message: 'the first comment should be spam', + } ) + .toBe( 'spam' ); + await expect + .poll( () => getCommentStatus( requestUtils, secondComment.id ), { + message: 'the second comment should be spam', + } ) + .toBe( 'spam' ); + } ); +} );