diff --git a/ghost/core/core/server/api/endpoints/comments-members.js b/ghost/core/core/server/api/endpoints/comments-members.js index b6f888d86e4..9f41d778170 100644 --- a/ghost/core/core/server/api/endpoints/comments-members.js +++ b/ghost/core/core/server/api/endpoints/comments-members.js @@ -109,7 +109,6 @@ const controller = { }, options: [ 'include' - ], validation: { options: { diff --git a/ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js b/ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js index d67a3dcbbe8..0c7723f1561 100644 --- a/ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +++ b/ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js @@ -1,9 +1,12 @@ const _ = require('lodash'); const utils = require('../../..'); const url = require('../utils/url'); +const htmlToPlaintext = require('@tryghost/html-to-plaintext'); const commentFields = [ 'id', + 'in_reply_to_id', + 'in_reply_to_snippet', 'status', 'html', 'created_at', @@ -42,6 +45,10 @@ const countFields = [ const commentMapper = (model, frame) => { const jsonModel = model.toJSON ? model.toJSON(frame.options) : model; + if (jsonModel.inReplyTo && jsonModel.inReplyTo.status === 'published') { + jsonModel.in_reply_to_snippet = htmlToPlaintext.commentSnippet(jsonModel.inReplyTo.html); + } + const response = _.pick(jsonModel, commentFields); if (jsonModel.member) { @@ -59,7 +66,7 @@ const commentMapper = (model, frame) => { } if (jsonModel.post) { - // We could use the post mapper here, but we need less field + don't need al the async behavior support + // We could use the post mapper here, but we need less field + don't need all the async behavior support url.forPost(jsonModel.post.id, jsonModel.post, frame); response.post = _.pick(jsonModel.post, postFields); } @@ -77,7 +84,7 @@ const commentMapper = (model, frame) => { response.html = null; } } - + return response; }; diff --git a/ghost/core/core/server/models/comment.js b/ghost/core/core/server/models/comment.js index f0e3b3c9ecf..5b7c5b19d7e 100644 --- a/ghost/core/core/server/models/comment.js +++ b/ghost/core/core/server/models/comment.js @@ -46,6 +46,10 @@ const Comment = ghostBookshelf.Model.extend({ return this.belongsTo('Comment', 'parent_id'); }, + inReplyTo() { + return this.belongsTo('Comment', 'in_reply_to_id'); + }, + likes() { return this.hasMany('CommentLike', 'comment_id'); }, @@ -181,25 +185,26 @@ const Comment = ghostBookshelf.Model.extend({ /** * We have to ensure consistency. If you listen on model events (e.g. `member.added`), you can expect that you always * receive all fields including relations. Otherwise you can't rely on a consistent flow. And we want to avoid - * that event listeners have to re-fetch a resource. This function is used in the context of inserting - * and updating resources. We won't return the relations by default for now. + * that event listeners have to re-fetch a resource. */ defaultRelations: function defaultRelations(methodName, options) { - // @todo: the default relations are not working for 'add' when we add it below + // @TODO: the default relations are not working for 'add' when we add it below + // this is because bookshelf does not automatically call `fetch` after adding so + // our bookshelf eager-load plugin doesn't use the `withRelated` options if (['findAll', 'findPage', 'edit', 'findOne', 'destroy'].indexOf(methodName) !== -1) { if (!options.withRelated || options.withRelated.length === 0) { if (options.parentId) { // Do not include replies for replies options.withRelated = [ // Relations - 'member', 'count.likes', 'count.liked' + 'inReplyTo', 'member', 'count.likes', 'count.liked' ]; } else { options.withRelated = [ // Relations - 'member', 'count.replies', 'count.likes', 'count.liked', + 'member', 'inReplyTo', 'count.replies', 'count.likes', 'count.liked', // Replies (limited to 3) - 'replies', 'replies.member' , 'replies.count.likes', 'replies.count.liked' + 'replies', 'replies.member', 'replies.inReplyTo', 'replies.count.likes', 'replies.count.liked' ]; } } diff --git a/ghost/core/core/server/services/comments/CommentsController.js b/ghost/core/core/server/services/comments/CommentsController.js index 14d7b985683..0a514fc5e91 100644 --- a/ghost/core/core/server/services/comments/CommentsController.js +++ b/ghost/core/core/server/services/comments/CommentsController.js @@ -115,6 +115,7 @@ module.exports = class CommentsController { if (data.parent_id) { result = await this.service.replyToComment( data.parent_id, + data.in_reply_to_id, frame.options.context.member.id, data.html, frame.options diff --git a/ghost/core/core/server/services/comments/CommentsService.js b/ghost/core/core/server/services/comments/CommentsService.js index b9049fb6d18..0f65ee88440 100644 --- a/ghost/core/core/server/services/comments/CommentsService.js +++ b/ghost/core/core/server/services/comments/CommentsService.js @@ -83,7 +83,10 @@ class CommentsService { await this.emails.notifyPostAuthors(comment); if (comment.get('parent_id')) { - await this.emails.notifyParentCommentAuthor(comment); + await this.emails.notifyParentCommentAuthor(comment, {type: 'parent'}); + } + if (comment.get('in_reply_to_id')) { + await this.emails.notifyParentCommentAuthor(comment, {type: 'in_reply_to'}); } } @@ -253,11 +256,12 @@ class CommentsService { /** * @param {string} parent - The ID of the Comment to reply to + * @param {string} inReplyTo - The ID of the Reply to reply to * @param {string} member - The ID of the Member to comment as * @param {string} comment - The HTML content of the Comment * @param {any} options */ - async replyToComment(parent, member, comment, options) { + async replyToComment(parent, inReplyTo, member, comment, options) { this.checkEnabled(); const memberModel = await this.models.Member.findOne({ id: member @@ -281,6 +285,7 @@ class CommentsService { message: tpl(messages.replyToReply) }); } + const postModel = await this.models.Post.findOne({ id: parentComment.get('post_id') }, { @@ -291,10 +296,27 @@ class CommentsService { this.checkPostAccess(postModel, memberModel); + let inReplyToComment; + if (parent && inReplyTo) { + inReplyToComment = await this.getCommentByID(inReplyTo, options); + + // we only allow references to published comments to avoid leaking + // hidden data via the snippet included in API responses + if (inReplyToComment && inReplyToComment.get('status') !== 'published') { + inReplyToComment = null; + } + + // we don't allow in_reply_to references across different parents + if (inReplyToComment && inReplyToComment.get('parent_id') !== parent) { + inReplyToComment = null; + } + } + const model = await this.models.Comment.add({ post_id: parentComment.get('post_id'), member_id: member, parent_id: parentComment.id, + in_reply_to_id: inReplyToComment && inReplyToComment.get('id'), html: comment, status: 'published' }, options); diff --git a/ghost/core/core/server/services/comments/CommentsServiceEmails.js b/ghost/core/core/server/services/comments/CommentsServiceEmails.js index 252ec074739..6fc308f6ba3 100644 --- a/ghost/core/core/server/services/comments/CommentsServiceEmails.js +++ b/ghost/core/core/server/services/comments/CommentsServiceEmails.js @@ -60,8 +60,13 @@ class CommentsServiceEmails { } } - async notifyParentCommentAuthor(reply) { - const parent = await this.models.Comment.findOne({id: reply.get('parent_id')}); + async notifyParentCommentAuthor(reply, {type = 'parent'} = {}) { + let parent; + if (type === 'in_reply_to') { + parent = await this.models.Comment.findOne({id: reply.get('in_reply_to_id')}); + } else { + parent = await this.models.Comment.findOne({id: reply.get('parent_id')}); + } const parentMember = parent.related('member'); if (parent?.get('status') !== 'published' || !parentMember.get('enable_comment_notifications')) { diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snap index 558a7444a19..6e2fb6b818e 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snap @@ -22699,7 +22699,7 @@ exports[`Activity Feed API Can filter events by post id 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "17559", + "content-length": "17625", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -22867,7 +22867,7 @@ exports[`Activity Feed API Filter splitting Can use NQL OR for type only 2: [hea Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "5114", + "content-length": "5180", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -23914,7 +23914,7 @@ exports[`Activity Feed API Returns comments in activity feed 2: [headers] 1`] = Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1238", + "content-length": "1304", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, diff --git a/ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap b/ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap index d4a21aba3eb..871fadc0334 100644 --- a/ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap +++ b/ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap @@ -1,75 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Comments API Tier only access posts Can comment on a post 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a message

New line

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API Tier only access posts Can comment on a post 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "373", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API Tier only access posts Can not comment on a post that a member does not have access to 1: [body] 1`] = ` -Object { - "errors": Array [ - Object { - "code": null, - "context": "You do not have permission to comment on this post.", - "details": null, - "ghostErrorCode": null, - "help": null, - "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - "message": "Permission error, cannot save comment.", - "property": null, - "type": "NoPermissionError", - }, - ], -} -`; - -exports[`Comments API Tier only access posts Can not comment on a post that a member does not have access to 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "277", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - exports[`Comments API Tier-only posts Members with access Can comment on a post 1: [body] 1`] = ` Object { "comments": Array [ @@ -82,6 +12,7 @@ Object { "edited_at": null, "html": "

This is a message

New line

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -101,12 +32,12 @@ exports[`Comments API Tier-only posts Members with access Can comment on a post Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "379", + "content-length": "401", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, "vary": "Accept-Encoding", - "x-cache-invalidate": "/api/members/comments/post/618ba1ffbe2896088840a6df/", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\//, "x-powered-by": "Express", } `; @@ -123,6 +54,7 @@ Object { "edited_at": null, "html": "This is a reply", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -142,7 +74,7 @@ exports[`Comments API Tier-only posts Members with access Can reply to a comment Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "348", + "content-length": "370", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, @@ -182,1485 +114,29 @@ Object { } `; -exports[`Comments API Tier-only posts Members without access Can not reply to a comment 1: [body] 1`] = ` -Object { - "errors": Array [ - Object { - "code": null, - "context": "You do not have permission to comment on this post.", - "details": null, - "ghostErrorCode": null, - "help": null, - "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - "message": "Permission error, cannot save comment.", - "property": null, - "type": "NoPermissionError", - }, - ], -} -`; - -exports[`Comments API Tier-only posts Members without access Can not reply to a comment 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "277", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can browse all comments of a post 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

First.

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Mr Egg", - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

Really original

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a message

New line

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], - "meta": Object { - "pagination": Object { - "limit": 15, - "next": null, - "page": 1, - "pages": 1, - "prev": null, - "total": 2, - }, - }, -} -`; - -exports[`Comments API when authenticated Can browse all comments of a post 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1100", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can comment on a post 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a message

New line

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can comment on a post 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "373", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can edit a comment on a post 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "html": "Updated comment", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can edit a comment on a post 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "666", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can fetch counts 1: [body] 1`] = ` -Object { - "618ba1ffbe2896088840a6df": 13, - "618ba1ffbe2896088840a6e1": 0, - "618ba1ffbe2896088840a6e3": 0, -} -`; - -exports[`Comments API when authenticated Can fetch counts 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "89", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can like a comment 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a message

New line

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can like a comment 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can like a comment 2: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a message", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "likes_count": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - }, - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can like a comment 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "675", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can like a comment 3: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can like a comment 4: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a message

New line

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can like a comment 5: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "674", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can like a reply 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can like a reply 2: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 0", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can like a reply 3: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "343", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can not edit a comment as a member who is not you 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "html": "Illegal comment update", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can not edit a comment as a member who is not you 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "673", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can not edit a comment post_id 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "Updated comment", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "likes_count": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can not edit a comment post_id 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "326", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can not edit a comment which does not belong to you 1: [body] 1`] = ` -Object { - "errors": Array [ - Object { - "code": null, - "context": "You do not have permission to edit comments", - "details": null, - "ghostErrorCode": null, - "help": null, - "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - "message": "Permission error, cannot edit comment.", - "property": null, - "type": "NoPermissionError", - }, - ], -} -`; - -exports[`Comments API when authenticated Can not edit a comment which does not belong to you 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "269", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can not reply to a reply 1: [body] 1`] = ` -Object { - "errors": Array [ - Object { - "code": null, - "context": "Can not reply to a reply", - "details": null, - "ghostErrorCode": null, - "help": null, - "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - "message": "Request not understood error, cannot save comment.", - "property": null, - "type": "BadRequestError", - }, - ], -} -`; - -exports[`Comments API when authenticated Can not reply to a reply 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "260", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can remove a like (unlike) 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can remove a like (unlike) 2: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a message

New line

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can remove a like (unlike) 3: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "675", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can reply to a comment 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can reply to a comment 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "342", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can reply to your own comment 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Can reply to your own comment 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "342", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can report a comment 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can request second page of replies 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 1", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 2", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "meta": Object { - "pagination": Object { - "limit": 3, - "next": null, - "page": 2, - "pages": 2, - "prev": 1, - "total": 5, - }, - }, -} -`; - -exports[`Comments API when authenticated Can request second page of replies 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "708", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Can return replies 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

Really original

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 0", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 1", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 2", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "meta": Object { - "pagination": Object { - "limit": 15, - "next": null, - "page": 1, - "pages": 1, - "prev": null, - "total": 5, - }, - }, -} -`; - -exports[`Comments API when authenticated Can return replies 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1629", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Cannot like a comment multiple times 1: [body] 1`] = ` -Object { - "errors": Array [ - Object { - "code": null, - "context": null, - "details": null, - "ghostErrorCode": null, - "help": null, - "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - "message": "This comment was liked already", - "property": null, - "type": "BadRequestError", - }, - ], -} -`; - -exports[`Comments API when authenticated Cannot like a comment multiple times 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Cannot like a comment multiple times 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "218", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Cannot report a comment twice 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Cannot unlike a comment if it has not been liked 1: [body] 1`] = ` -Object { - "errors": Array [ - Object { - "code": null, - "context": null, - "details": null, - "ghostErrorCode": null, - "help": null, - "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - "message": "Unable to find like", - "property": null, - "type": "NotFoundError", - }, - ], -} -`; - -exports[`Comments API when authenticated Cannot unlike a comment if it has not been liked 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "205", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

First.

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Mr Egg", - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

Really original

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "956", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 3: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 0", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 4: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "344", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 5: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 1", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 6: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "344", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 7: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 2", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 8: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "344", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 9: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

First.

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Mr Egg", - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

Really original

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 0", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when authenticated Limits returned replies to 3 10: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1261", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated can paginate replies 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 1", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 2", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "meta": Object { - "pagination": Object { - "limit": 3, - "next": null, - "page": 2, - "pages": 2, - "prev": 1, - "total": 5, - }, - }, -} -`; - -exports[`Comments API when authenticated can paginate replies 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "708", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when authenticated can return replies 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

Really original

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 0", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 1", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, +exports[`Comments API Tier-only posts Members without access Can not reply to a comment 1: [body] 1`] = ` +Object { + "errors": Array [ Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 2", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", + "code": null, + "context": "You do not have permission to comment on this post.", + "details": null, + "ghostErrorCode": null, + "help": null, + "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "message": "Permission error, cannot save comment.", + "property": null, + "type": "NoPermissionError", }, ], - "meta": Object { - "pagination": Object { - "limit": 15, - "next": null, - "page": 1, - "pages": 1, - "prev": null, - "total": 5, - }, - }, } `; -exports[`Comments API when authenticated can return replies 2: [headers] 1`] = ` +exports[`Comments API Tier-only posts Members without access Can not reply to a comment 2: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1630", + "content-length": "277", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -1680,6 +156,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -1700,6 +177,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": null, "liked": Any, "member": Object { "avatar_image": null, @@ -1717,6 +195,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -1748,7 +227,7 @@ exports[`Comments API when commenting enabled for all when authenticated Browsin Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1118", + "content-length": "1184", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -1768,6 +247,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": null, "liked": Any, "member": Object { "avatar_image": null, @@ -1785,6 +265,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -1807,6 +288,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -1836,7 +318,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can bro Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1118", + "content-length": "1184", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -1856,6 +338,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": null, "liked": Any, "member": Object { "avatar_image": null, @@ -1873,6 +356,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -1895,6 +379,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -1924,7 +409,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can bro Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1118", + "content-length": "1184", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -1944,6 +429,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -1964,6 +450,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": null, "liked": Any, "member": Object { "avatar_image": null, @@ -1981,6 +468,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2012,7 +500,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can bro Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1118", + "content-length": "1184", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -2032,6 +520,7 @@ Object { "edited_at": null, "html": "

This is a message

New line

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2051,12 +540,12 @@ exports[`Comments API when commenting enabled for all when authenticated Can com Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "379", + "content-length": "401", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, "vary": "Accept-Encoding", - "x-cache-invalidate": "/api/members/comments/post/618ba1ffbe2896088840a6df/", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\//, "x-powered-by": "Express", } `; @@ -2073,6 +562,7 @@ Object { "edited_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "html": "Updated comment", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2092,7 +582,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can edi Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "370", + "content-length": "392", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -2121,54 +611,6 @@ Object { } `; -exports[`Comments API when commenting enabled for all when authenticated Can like a comment 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a message

New line

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, - "liked": Any, - "member": Object { - "avatar_image": null, - "expertise": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, - "liked": Any, - "member": Object { - "avatar_image": null, - "expertise": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], -} -`; - exports[`Comments API when commenting enabled for all when authenticated Can like a comment 1: [headers] 1`] = ` Object { "access-control-allow-origin": "*", @@ -2191,6 +633,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2206,83 +649,11 @@ Object { } `; -exports[`Comments API when commenting enabled for all when authenticated Can like a comment 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "731", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - exports[`Comments API when commenting enabled for all when authenticated Can like a comment 3: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "367", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when commenting enabled for all when authenticated Can like a comment 4: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a message

New line

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, - "liked": Any, - "member": Object { - "avatar_image": null, - "expertise": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, - "liked": Any, - "member": Object { - "avatar_image": null, - "expertise": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when commenting enabled for all when authenticated Can like a comment 5: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "730", + "content-length": "389", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -2312,6 +683,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2331,7 +703,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can lik Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "356", + "content-length": "378", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -2351,6 +723,7 @@ Object { "edited_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "html": "Illegal comment update", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2370,7 +743,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can not Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "377", + "content-length": "399", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -2460,6 +833,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2479,7 +853,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can rem Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "357", + "content-length": "379", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -2499,6 +873,7 @@ Object { "edited_at": null, "html": "This is a reply", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2518,7 +893,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can rep Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "348", + "content-length": "370", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, @@ -2540,6 +915,7 @@ Object { "edited_at": null, "html": "This is a reply", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2559,7 +935,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can rep Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "348", + "content-length": "370", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, @@ -2581,6 +957,7 @@ Object { "edited_at": null, "html": "This is a reply", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2589,135 +966,40 @@ Object { "name": null, "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when commenting enabled for all when authenticated Can reply to a comment with www domain 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "348", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\/, \\\\/api\\\\/members\\\\/comments\\\\/\\[0-9a-f\\]\\{24\\}\\\\/replies\\\\//, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when commenting enabled for all when authenticated Can reply to your own comment 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "expertise": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when commenting enabled for all when authenticated Can reply to your own comment 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "348", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\/, \\\\/api\\\\/members\\\\/comments\\\\/\\[0-9a-f\\]\\{24\\}\\\\/replies\\\\//, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when commenting enabled for all when authenticated Can report a comment 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when commenting enabled for all when authenticated Can request last page of replies 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a reply

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "expertise": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "meta": Object { - "pagination": Object { - "limit": 3, - "next": null, - "page": 3, - "pages": 3, - "prev": 2, - "total": 7, + "replies": Array [], + "status": "published", }, - }, + ], } `; -exports[`Comments API when commenting enabled for all when authenticated Can request last page of replies 2: [headers] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated Can reply to a comment with www domain 2: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "414", + "content-length": "370", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, "vary": "Accept-Encoding", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\/, \\\\/api\\\\/members\\\\/comments\\\\/\\[0-9a-f\\]\\{24\\}\\\\/replies\\\\//, "x-powered-by": "Express", } `; -exports[`Comments API when commenting enabled for all when authenticated Can request second page of replies 1: [body] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated Can reply to your own comment 1: [body] 1`] = ` Object { "comments": Array [ Object { "count": Object { "likes": Any, + "replies": 0, }, "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "edited_at": null, - "html": "This is a reply 1", + "html": "This is a reply", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2726,16 +1008,48 @@ Object { "name": null, "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, }, + "replies": Array [], "status": "published", }, + ], +} +`; + +exports[`Comments API when commenting enabled for all when authenticated Can reply to your own comment 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "*", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "370", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, + "vary": "Accept-Encoding", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\/, \\\\/api\\\\/members\\\\/comments\\\\/\\[0-9a-f\\]\\{24\\}\\\\/replies\\\\//, + "x-powered-by": "Express", +} +`; + +exports[`Comments API when commenting enabled for all when authenticated Can report a comment 1: [headers] 1`] = ` +Object { + "access-control-allow-origin": "*", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "x-powered-by": "Express", +} +`; + +exports[`Comments API when commenting enabled for all when authenticated Can request last page of replies 1: [body] 1`] = ` +Object { + "comments": Array [ Object { "count": Object { "likes": Any, }, "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "edited_at": null, - "html": "This is a reply 2", + "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2751,20 +1065,20 @@ Object { "pagination": Object { "limit": 3, "next": null, - "page": 2, - "pages": 2, - "prev": 1, - "total": 5, + "page": 3, + "pages": 3, + "prev": 2, + "total": 7, }, }, } `; -exports[`Comments API when commenting enabled for all when authenticated Can request second page of replies 2: [headers] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated Can request last page of replies 2: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "720", + "content-length": "436", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -2783,6 +1097,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2801,6 +1116,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2819,6 +1135,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2837,6 +1154,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2855,6 +1173,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2873,6 +1192,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2891,6 +1211,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -2919,7 +1240,7 @@ exports[`Comments API when commenting enabled for all when authenticated Can ret Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "2313", + "content-length": "2467", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -3008,6 +1329,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": null, "liked": Any, "member": Object { "avatar_image": null, @@ -3025,6 +1347,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -3043,6 +1366,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -3061,6 +1385,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -3082,7 +1407,7 @@ exports[`Comments API when commenting enabled for all when authenticated Limits Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1308", + "content-length": "1396", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -3090,7 +1415,7 @@ Object { } `; -exports[`Comments API when commenting enabled for all when authenticated Limits returned replies to 3 3: [body] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies can set in_reply_to_id when creating a reply 1: [body] 1`] = ` Object { "comments": Array [ Object { @@ -3100,9 +1425,10 @@ Object { }, "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "edited_at": null, - "html": "This is a reply 0", + "html": "

This is a reply to a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, + "in_reply_to_id": Nullable, + "in_reply_to_snippet": "This is a reply", "liked": Any, "member": Object { "avatar_image": null, @@ -3118,21 +1444,21 @@ Object { } `; -exports[`Comments API when commenting enabled for all when authenticated Limits returned replies to 3 4: [headers] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies can set in_reply_to_id when creating a reply 2: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "372", + "content-length": "450", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, "vary": "Accept-Encoding", - "x-cache-invalidate": "/api/members/comments/post/618ba1ffbe2896088840a6df/, /api/members/comments/6195c6a1e792de832cd08144/replies/", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\/, \\\\/api\\\\/members\\\\/comments\\\\/\\[0-9a-f\\]\\{24\\}\\\\/replies\\\\//, "x-powered-by": "Express", } `; -exports[`Comments API when commenting enabled for all when authenticated Limits returned replies to 3 5: [body] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies cannot set in_reply_to_id to a deleted comment 1: [body] 1`] = ` Object { "comments": Array [ Object { @@ -3142,9 +1468,9 @@ Object { }, "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "edited_at": null, - "html": "This is a reply 1", + "html": "

This is a reply to a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -3160,21 +1486,21 @@ Object { } `; -exports[`Comments API when commenting enabled for all when authenticated Limits returned replies to 3 6: [headers] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies cannot set in_reply_to_id to a deleted comment 2: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "372", + "content-length": "388", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, "vary": "Accept-Encoding", - "x-cache-invalidate": "/api/members/comments/post/618ba1ffbe2896088840a6df/, /api/members/comments/6195c6a1e792de832cd08144/replies/", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\/, \\\\/api\\\\/members\\\\/comments\\\\/\\[0-9a-f\\]\\{24\\}\\\\/replies\\\\//, "x-powered-by": "Express", } `; -exports[`Comments API when commenting enabled for all when authenticated Limits returned replies to 3 7: [body] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies cannot set in_reply_to_id to a hidden comment 1: [body] 1`] = ` Object { "comments": Array [ Object { @@ -3184,9 +1510,9 @@ Object { }, "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "edited_at": null, - "html": "This is a reply 2", + "html": "

This is a reply to a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -3202,129 +1528,228 @@ Object { } `; -exports[`Comments API when commenting enabled for all when authenticated Limits returned replies to 3 8: [headers] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies cannot set in_reply_to_id to a hidden comment 2: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "372", + "content-length": "388", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, "vary": "Accept-Encoding", - "x-cache-invalidate": "/api/members/comments/post/618ba1ffbe2896088840a6df/, /api/members/comments/6195c6a1e792de832cd08144/replies/", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\/, \\\\/api\\\\/members\\\\/comments\\\\/\\[0-9a-f\\]\\{24\\}\\\\/replies\\\\//, "x-powered-by": "Express", } `; -exports[`Comments API when commenting enabled for all when authenticated Limits returned replies to 3 9: [body] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies does not include in_reply_to_snippet for deleted comments 1: [body] 1`] = ` Object { "comments": Array [ Object { "count": Object { "likes": Any, - "replies": Any, + "replies": 0, }, "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "edited_at": null, - "html": "

First.

", + "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, "expertise": null, "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Mr Egg", + "name": null, "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

Really original

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, - "liked": Any, - "member": Object { - "avatar_image": null, - "expertise": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, - "liked": Any, - "member": Object { - "avatar_image": null, - "expertise": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply 0", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "in_reply_to_id": null, - "liked": Any, - "member": Object { - "avatar_image": null, - "expertise": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], + "replies": Array [], + "status": "published", + }, + ], +} +`; + +exports[`Comments API when commenting enabled for all when authenticated replies to replies does not include in_reply_to_snippet for deleted comments 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "*", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "401", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Accept-Encoding", + "x-powered-by": "Express", +} +`; + +exports[`Comments API when commenting enabled for all when authenticated replies to replies does not include in_reply_to_snippet for hidden comments 1: [body] 1`] = ` +Object { + "comments": Array [ + Object { + "count": Object { + "likes": Any, + "replies": 0, + }, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "edited_at": null, + "html": "

This is a comment

", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, + "liked": Any, + "member": Object { + "avatar_image": null, + "expertise": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": null, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + "replies": Array [], + "status": "published", + }, + ], +} +`; + +exports[`Comments API when commenting enabled for all when authenticated replies to replies does not include in_reply_to_snippet for hidden comments 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "*", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "401", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Accept-Encoding", + "x-powered-by": "Express", +} +`; + +exports[`Comments API when commenting enabled for all when authenticated replies to replies in_reply_to_id is ignored id in_reply_to_id has a different parent 1: [body] 1`] = ` +Object { + "comments": Array [ + Object { + "count": Object { + "likes": Any, + "replies": 0, + }, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "edited_at": null, + "html": "

This is a reply to a reply

", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, + "liked": Any, + "member": Object { + "avatar_image": null, + "expertise": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": null, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + "replies": Array [], + "status": "published", + }, + ], +} +`; + +exports[`Comments API when commenting enabled for all when authenticated replies to replies in_reply_to_id is ignored id in_reply_to_id has a different parent 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "*", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "388", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, + "vary": "Accept-Encoding", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\/, \\\\/api\\\\/members\\\\/comments\\\\/\\[0-9a-f\\]\\{24\\}\\\\/replies\\\\//, + "x-powered-by": "Express", +} +`; + +exports[`Comments API when commenting enabled for all when authenticated replies to replies in_reply_to_id is ignored when no parent specified 1: [body] 1`] = ` +Object { + "comments": Array [ + Object { + "count": Object { + "likes": Any, + "replies": 0, + }, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "edited_at": null, + "html": "

This is a reply to a reply

", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, + "liked": Any, + "member": Object { + "avatar_image": null, + "expertise": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": null, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + "replies": Array [], + "status": "published", + }, + ], +} +`; + +exports[`Comments API when commenting enabled for all when authenticated replies to replies in_reply_to_id is ignored when no parent specified 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "*", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "388", + "content-type": "application/json; charset=utf-8", + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, + "vary": "Accept-Encoding", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\//, + "x-powered-by": "Express", +} +`; + +exports[`Comments API when commenting enabled for all when authenticated replies to replies includes in_reply_to_snippet in response 1: [body] 1`] = ` +Object { + "comments": Array [ + Object { + "count": Object { + "likes": Any, + "replies": 0, + }, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "edited_at": null, + "html": "

This is a reply to a reply

", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, + "in_reply_to_snippet": "This is what was replied to", + "liked": Any, + "member": Object { + "avatar_image": null, + "expertise": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": null, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + "replies": Array [], "status": "published", }, ], } `; -exports[`Comments API when commenting enabled for all when authenticated Limits returned replies to 3 10: [headers] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies includes in_reply_to_snippet in response 2: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1373", + "content-length": "462", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, "vary": "Accept-Encoding", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\/, \\\\/api\\\\/members\\\\/comments\\\\/\\[0-9a-f\\]\\{24\\}\\\\/replies\\\\//, "x-powered-by": "Express", } `; -exports[`Comments API when commenting enabled for all when authenticated can show most liked comment first when order param = best 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-cache-invalidate": "/api/members/comments/post/618ba1ffbe2896088840a6df/", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when commenting enabled for all when authenticated can show most liked comment first when order param = best 2: [body] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies includes in_reply_to_snippet in response 3: [body] 1`] = ` Object { "comments": Array [ Object { @@ -3334,14 +1759,16 @@ Object { }, "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "edited_at": null, - "html": "

This is a comment

", + "html": "

This is a reply to a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, + "in_reply_to_snippet": "This is what was replied to", "liked": Any, "member": Object { "avatar_image": null, "expertise": null, "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Egon Spengler", + "name": null, "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, }, "replies": Array [], @@ -3351,11 +1778,11 @@ Object { } `; -exports[`Comments API when commenting enabled for all when authenticated can show most liked comment first when order param = best 3: [headers] 1`] = ` +exports[`Comments API when commenting enabled for all when authenticated replies to replies includes in_reply_to_snippet in response 4: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "367", + "content-length": "462", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -3375,6 +1802,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": null, "liked": Any, "member": Object { "avatar_image": null, @@ -3392,6 +1820,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -3423,7 +1852,7 @@ exports[`Comments API when commenting enabled for all when not authenticated Can Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "764", + "content-length": "808", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -3443,6 +1872,7 @@ Object { "edited_at": null, "html": "

This is a comment

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": null, "liked": Any, "member": Object { "avatar_image": null, @@ -3460,6 +1890,7 @@ Object { "edited_at": null, "html": "

This is a reply

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -3491,7 +1922,7 @@ exports[`Comments API when commenting enabled for all when not authenticated Can Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "764", + "content-length": "808", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", @@ -3649,262 +2080,6 @@ Object { } `; -exports[`Comments API when not authenticated but enabled Can browse all comments of a post 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

First.

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": "Mr Egg", - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [ - Object { - "count": Object { - "likes": Any, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

Really original

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "status": "published", - }, - ], - "status": "published", - }, - ], - "meta": Object { - "pagination": Object { - "limit": 15, - "next": null, - "page": 1, - "pages": 1, - "prev": null, - "total": 1, - }, - }, -} -`; - -exports[`Comments API when not authenticated but enabled Can browse all comments of a post 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "741", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when not authenticated but enabled Can report a comment 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when not authenticated but enabled cannot report a comment 1: [body] 1`] = ` -Object { - "errors": Array [ - Object { - "code": null, - "context": null, - "details": null, - "ghostErrorCode": null, - "help": null, - "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - "message": "Unable to find member", - "property": null, - "type": "UnauthorizedError", - }, - ], -} -`; - -exports[`Comments API when not authenticated but enabled cannot report a comment 1: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "x-powered-by": "Express", -} -`; - -exports[`Comments API when not authenticated but enabled cannot report a comment 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "211", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when paid only Members with access Can comment on a post 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "

This is a message

New line

", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when paid only Members with access Can comment on a post 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "373", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when paid only Members with access Can reply to a comment 1: [body] 1`] = ` -Object { - "comments": Array [ - Object { - "count": Object { - "likes": Any, - "replies": 0, - }, - "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, - "edited_at": null, - "html": "This is a reply", - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "liked": Any, - "member": Object { - "avatar_image": null, - "bio": null, - "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, - "name": null, - "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - }, - "replies": Array [], - "status": "published", - }, - ], -} -`; - -exports[`Comments API when paid only Members with access Can reply to a comment 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "342", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when paid only Members without access Can not comment on a post 1: [body] 1`] = ` -Object { - "errors": Array [ - Object { - "code": null, - "context": "You do not have permission to comment on this post.", - "details": null, - "ghostErrorCode": null, - "help": null, - "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - "message": "Permission error, cannot save comment.", - "property": null, - "type": "NoPermissionError", - }, - ], -} -`; - -exports[`Comments API when paid only Members without access Can not comment on a post 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "277", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - -exports[`Comments API when paid only Members without access Can not reply to a comment 1: [body] 1`] = ` -Object { - "errors": Array [ - Object { - "code": null, - "context": "You do not have permission to comment on this post.", - "details": null, - "ghostErrorCode": null, - "help": null, - "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, - "message": "Permission error, cannot save comment.", - "property": null, - "type": "NoPermissionError", - }, - ], -} -`; - -exports[`Comments API when paid only Members without access Can not reply to a comment 2: [headers] 1`] = ` -Object { - "access-control-allow-origin": "*", - "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "277", - "content-type": "application/json; charset=utf-8", - "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, - "vary": "Accept-Encoding", - "x-powered-by": "Express", -} -`; - exports[`Comments API when paid only commenting Members with access Can comment on a post 1: [body] 1`] = ` Object { "comments": Array [ @@ -3917,6 +2092,7 @@ Object { "edited_at": null, "html": "

This is a message

New line

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -3936,12 +2112,12 @@ exports[`Comments API when paid only commenting Members with access Can comment Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "379", + "content-length": "401", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, "vary": "Accept-Encoding", - "x-cache-invalidate": "/api/members/comments/post/618ba1ffbe2896088840a6df/", + "x-cache-invalidate": StringMatching /\\\\/api\\\\/members\\\\/comments\\\\/post\\\\/\\[0-9a-f\\]\\{24\\}\\\\//, "x-powered-by": "Express", } `; @@ -3958,6 +2134,7 @@ Object { "edited_at": null, "html": "This is a reply", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "in_reply_to_id": Nullable, "liked": Any, "member": Object { "avatar_image": null, @@ -3977,7 +2154,7 @@ exports[`Comments API when paid only commenting Members with access Can reply to Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "348", + "content-length": "370", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/comments\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, diff --git a/ghost/core/test/e2e-api/members-comments/comments.test.js b/ghost/core/test/e2e-api/members-comments/comments.test.js index 19065fd85a7..984b755b62d 100644 --- a/ghost/core/test/e2e-api/members-comments/comments.test.js +++ b/ghost/core/test/e2e-api/members-comments/comments.test.js @@ -1,6 +1,6 @@ const assert = require('assert/strict'); const {agentProvider, mockManager, fixtureManager, matchers, configUtils, dbUtils} = require('../../utils/e2e-framework'); -const {anyEtag, anyObjectId, anyLocationFor, anyISODateTime, anyErrorId, anyUuid, anyNumber, anyBoolean} = matchers; +const {nullable, anyEtag, anyObjectId, anyLocationFor, anyISODateTime, anyErrorId, anyUuid, anyNumber, anyBoolean} = matchers; const should = require('should'); const models = require('../../../core/server/models'); const moment = require('moment-timezone'); @@ -20,13 +20,17 @@ const dbFns = { * @property {string} [post_id=postId] * @property {string} member_id * @property {string} [parent_id] + * @property {string} [in_reply_to_id] * @property {string} [html='This is a comment'] + * @property {string} [status] + * @property {Date} [created_at] */ /** * @typedef {Object} AddCommentReplyData * @property {string} member_id * @property {string} [html='This is a reply'] - * @property {date} [created_at] + * @property {Date} [created_at] + * @property {string} [status] */ /** * @typedef {AddCommentData & {replies: AddCommentReplyData[]}} AddCommentWithRepliesData @@ -42,7 +46,9 @@ const dbFns = { member_id: data.member_id, parent_id: data.parent_id, html: data.html || '

This is a comment

', - created_at: data.created_at + created_at: data.created_at, + in_reply_to_id: data.in_reply_to_id, + status: data.status || 'published' }); }, /** @@ -60,7 +66,8 @@ const dbFns = { post_id: parent.get('post_id'), member_id: reply.member_id, parent_id: parent.get('id'), - html: reply.html || '

This is a reply

' + html: reply.html || '

This is a reply

', + status: reply.status }); createdReplies.push(createdReply); } @@ -95,6 +102,7 @@ const dbFns = { const commentMatcher = { id: anyObjectId, + in_reply_to_id: nullable(anyObjectId), created_at: anyISODateTime, member: { id: anyObjectId, @@ -184,24 +192,32 @@ function testGetComments(url, commentsMatcher) { * @param {string} data.post_id * @param {string} data.html * @param {string} [data.parent_id] + * @param {string} [data.in_reply_to_id] * @param {Object} [options] * @param {number} [options.status = 201] * @param {Object} [options.matchHeaderSnapshot] * @param {Object} [options.matchBodySnapshot] * @returns {any} ExpectRequest */ -function testPostComment({post_id, html, parent_id}, {status = 201, matchHeaderSnapshot = {}, matchBodySnapshot} = {}) { +function testPostComment({post_id, html, parent_id, in_reply_to_id}, {status = 201, matchHeaderSnapshot = {}, matchBodySnapshot} = {}) { return membersAgent .post(`/api/comments/`) .body({comments: [{ post_id, parent_id, + in_reply_to_id, html }]}) .expectStatus(status) .matchHeaderSnapshot({ etag: anyEtag, location: anyLocationFor('comments'), + 'x-cache-invalidate': matchers.stringMatching( + parent_id + ? new RegExp('/api/members/comments/post/[0-9a-f]{24}/, /api/members/comments/[0-9a-f]{24}/replies/') + : new RegExp('/api/members/comments/post/[0-9a-f]{24}/') + + ), ...matchHeaderSnapshot }) .matchBodySnapshot({ @@ -256,12 +272,6 @@ async function testCanReply(member, emailMatchers = {}) { post_id: postId, parent_id: parentComment.get('id'), html: 'This is a reply' - }, { - matchHeaderSnapshot: { - 'x-cache-invalidate': matchers.stringMatching( - new RegExp('/api/members/comments/post/[0-9a-f]{24}/, /api/members/comments/[0-9a-f]{24}/replies/') - ) - } }); mockManager.assert.sentEmailCount(2); @@ -587,12 +597,6 @@ describe('Comments API', function () { post_id: postId, parent_id: parentComment.id, html: 'This is a reply' - }, { - matchHeaderSnapshot: { - 'x-cache-invalidate': matchers.stringMatching( - new RegExp('/api/members/comments/post/[0-9a-f]{24}/, /api/members/comments/[0-9a-f]{24}/replies/') - ) - } }); // Check only the author got an email (because we are the author of this parent comment) @@ -1020,6 +1024,160 @@ describe('Comments API', function () { assert(!deletedComment.html); }); + + describe('replies to replies', function () { + it('can set in_reply_to_id when creating a reply', async function () { + const {replies: [reply]} = await dbFns.addCommentWithReplies({ + member_id: fixtureManager.get('members', 1).id, + replies: [{ + member_id: fixtureManager.get('members', 2).id + }] + }); + + const {body: {comments: [newComment]}} = await testPostComment({ + post_id: postId, + parent_id: reply.get('parent_id'), + in_reply_to_id: reply.get('id'), + html: '

This is a reply to a reply

' + }); + + // in_reply_to is set + newComment.in_reply_to_id.should.eql(reply.get('id')); + newComment.in_reply_to_snippet.should.eql('This is a reply'); + + // replied-to comment author is notified + // parent comment author is notified + mockManager.assert.sentEmailCount(3); + assertAuthorEmailSent(postAuthorEmail, postTitle); + mockManager.assert.sentEmail({ + subject: '↪️ New reply to your comment on Ghost', + to: fixtureManager.get('members', 1).email + }); + mockManager.assert.sentEmail({ + subject: '↪️ New reply to your comment on Ghost', + to: fixtureManager.get('members', 2).email + }); + }); + + ['deleted', 'hidden'].forEach((status) => { + it(`cannot set in_reply_to_id to a ${status} comment`, async function () { + const {replies: [reply]} = await dbFns.addCommentWithReplies({ + member_id: fixtureManager.get('members', 1).id, + replies: [{ + member_id: fixtureManager.get('members', 2).id, + status + }] + }); + + const {body: {comments: [newComment]}} = await testPostComment({ + post_id: postId, + parent_id: reply.get('parent_id'), + in_reply_to_id: reply.get('id'), + html: '

This is a reply to a reply

' + }); + + // in_reply_to is not set + should.not.exist(newComment.in_reply_to_id); + should.not.exist(newComment.in_reply_to_snippet); + + // only author and parent email sent + mockManager.assert.sentEmailCount(2); + }); + }); + + it('in_reply_to_id is ignored when no parent specified', async function () { + const {replies: [reply]} = await dbFns.addCommentWithReplies({ + member_id: fixtureManager.get('members', 1).id, + replies: [{ + member_id: fixtureManager.get('members', 2).id + }] + }); + + const {body: {comments: [newComment]}} = await testPostComment({ + post_id: postId, + in_reply_to_id: reply.get('id'), + html: '

This is a reply to a reply

' + }); + + // in_reply_to is not set + should.not.exist(newComment.in_reply_to_id); + should.not.exist(newComment.in_reply_to_snippet); + + should.not.exist(newComment.parent_id); + + // only author email sent + mockManager.assert.sentEmailCount(1); + }); + + it('in_reply_to_id is ignored id in_reply_to_id has a different parent', async function () { + const {replies: [reply]} = await dbFns.addCommentWithReplies({ + member_id: fixtureManager.get('members', 1).id, + replies: [{ + member_id: fixtureManager.get('members', 2).id + }] + }); + + const diffParentComment = await dbFns.addComment({ + member_id: fixtureManager.get('members', 1).id + }); + + const {body: {comments: [newComment]}} = await testPostComment({ + post_id: postId, + parent_id: diffParentComment.get('id'), + in_reply_to_id: reply.get('id'), + html: '

This is a reply to a reply

' + }); + + // in_reply_to is not set + should.not.exist(newComment.in_reply_to_id); + should.not.exist(newComment.in_reply_to_snippet); + }); + + it('includes in_reply_to_snippet in response', async function () { + const {replies: [reply]} = await dbFns.addCommentWithReplies({ + member_id: fixtureManager.get('members', 1).id, + replies: [{ + member_id: fixtureManager.get('members', 2).id, + html: '

This is what was replied to

' + }] + }); + + const {body: {comments: [newComment]}} = await testPostComment({ + post_id: postId, + parent_id: reply.get('parent_id'), + in_reply_to_id: reply.get('id'), + html: '

This is a reply to a reply

' + }); + + const {body: {comments: [comment]}} = await testGetComments(`/api/comments/${newComment.id}`, [commentMatcher]); + + // in_reply_to_snippet is included + comment.in_reply_to_snippet.should.eql('This is what was replied to'); + }); + + ['deleted', 'hidden'].forEach((status) => { + it(`does not include in_reply_to_snippet for ${status} comments`, async function () { + const {replies: [reply]} = await dbFns.addCommentWithReplies({ + member_id: fixtureManager.get('members', 1).id, + replies: [{ + member_id: fixtureManager.get('members', 2).id, + html: `

This is a ${status} reply

`, + status + }] + }); + + const newComment = await dbFns.addComment({ + member_id: loggedInMember.id, + parent_id: reply.get('parent_id'), + in_reply_to_id: reply.get('id') + }); + + const {body: {comments: [comment]}} = await testGetComments(`/api/comments/${newComment.id}`, [commentMatcher]); + + should.not.exist(comment.in_reply_to_snippet); + }); + }); + }); }); }); diff --git a/ghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.js b/ghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.js index 547de671487..6126caa70cd 100644 --- a/ghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.js +++ b/ghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.js @@ -7,6 +7,7 @@ const cleanUtil = require('../../../../../../../core/server/api/endpoints/utils/ const extraAttrsUtils = require('../../../../../../../core/server/api/endpoints/utils/serializers/output/utils/extra-attrs'); const mappers = require('../../../../../../../core/server/api/endpoints/utils/serializers/output/mappers'); const memberAttribution = require('../../../../../../../core/server/services/member-attribution'); +const htmlToPlaintext = require('@tryghost/html-to-plaintext'); function createJsonModel(data) { return Object.assign(data, {toJSON: sinon.stub().returns(data)}); @@ -609,4 +610,80 @@ describe('Unit: utils/serializers/output/mappers', function () { }); }); }); + + describe('Comment mapper', function () { + it('includes in_reply_to_snippet for published replies-to-replies', function () { + const frame = {}; + + const model = { + id: 'comment3', + html: '

comment 3

', + member: {id: 'member1'}, + parent: { + id: 'comment1', + html: '

comment 1

', + member: {id: 'member1'} + }, + in_reply_to_id: 'comment2', + inReplyTo: { + id: 'comment2', + parent_id: 'comment1', + html: '

comment 2

', + status: 'published', + member: {id: 'member2'} + } + }; + + const mapped = mappers.comments(model, frame); + + mapped.should.eql({ + id: 'comment3', + html: '

comment 3

', + member: {id: 'member1'}, + parent: {id: 'comment1', html: '

comment 1

', member: {id: 'member1'}}, + in_reply_to_id: 'comment2', + in_reply_to_snippet: 'comment 2' + }); + }); + + it('calls correct html-to-plaintext converter for in_reply_to_snippet', function () { + const converterSpy = sinon.spy(htmlToPlaintext, 'commentSnippet'); + + const frame = {}; + + const model = { + inReplyTo: { + html: '

First paragraph with link,
and new line.

Second paragraph

', + status: 'published' + } + }; + + const mapped = mappers.comments(model, frame); + + converterSpy.calledOnce.should.eql(true, 'htmlToPlaintext.commentSnippet was not called'); + + mapped.should.eql({ + in_reply_to_snippet: 'First paragraph with link, and new line. Second paragraph', + member: null + }); + }); + + it('does not include in_reply_to_snippet for top-level comments', function () { + const frame = {}; + + const model = { + id: 'comment1', + html: '

comment 1

', + inReplyTo: undefined + }; + + const mapped = mappers.comments(model, frame); + + mapped.should.eql({ + id: 'comment1', + html: '

comment 1

', + member: null + }); + }); + }); }); diff --git a/ghost/html-to-plaintext/lib/html-to-plaintext.js b/ghost/html-to-plaintext/lib/html-to-plaintext.js index 88e262980d1..76f6d96b4f4 100644 --- a/ghost/html-to-plaintext/lib/html-to-plaintext.js +++ b/ghost/html-to-plaintext/lib/html-to-plaintext.js @@ -35,6 +35,7 @@ const baseSettings = { let excerptConverter; let emailConverter; let commentConverter; +let commentSnippetConverter; const loadConverters = () => { if (excerptConverter && emailConverter) { @@ -78,9 +79,18 @@ const loadConverters = () => { ] }); + const commentSnippetSettings = mergeSettings({ + preserveNewlines: false, + ignoreHref: true, + selectors: [ + {selector: 'blockquote', format: 'skip'} + ] + }); + excerptConverter = compile(excerptSettings); emailConverter = compile(emailSettings); commentConverter = compile(commentSettings); + commentSnippetConverter = compile(commentSnippetSettings); }; module.exports.excerpt = (html) => { @@ -100,3 +110,11 @@ module.exports.comment = (html) => { return commentConverter(html); }; + +module.exports.commentSnippet = (html) => { + loadConverters(); + + return commentSnippetConverter(html) + .replace(/\n/g, ' ') + .replace(/\s+/g, ' '); +}; diff --git a/ghost/html-to-plaintext/test/html-to-plaintext.test.js b/ghost/html-to-plaintext/test/html-to-plaintext.test.js index 065366c82eb..7a5c30fd319 100644 --- a/ghost/html-to-plaintext/test/html-to-plaintext.test.js +++ b/ghost/html-to-plaintext/test/html-to-plaintext.test.js @@ -87,4 +87,28 @@ describe('Html to Plaintext', function () { assert.equal(excerpt, expected); }); }); + + describe('commentSnippet converter', function () { + function testConverter({input, expected}) { + return () => { + const output = htmlToPlaintext.commentSnippet(input); + assert.equal(output, expected); + }; + } + + it('skips href urls', testConverter({ + input: 'A snippet from a post template', + expected: 'A snippet from a post template' + })); + + it('skips blockquotes', testConverter({ + input: '
Previous comment quote

And the new comment text

', + expected: 'And the new comment text' + })); + + it('returns a single line', testConverter({ + input: '

First paragraph.

Second paragraph.

', + expected: 'First paragraph. Second paragraph.' + })); + }); });