Files
enterprise-addons/social_facebook/static/tests/social_facebook_test_comments.test.js
T

454 lines
18 KiB
JavaScript

import { defineMailModels } from "@mail/../tests/mail_test_helpers";
import { expect, test } from "@odoo/hoot";
import { queryAll } from "@odoo/hoot-dom";
import { StreamPostCommentsReply } from "@social/js/stream_post_comments_reply";
import {
contains,
defineModels,
models,
mountView,
onRpc,
patchWithCleanup,
} from "@web/../tests/web_test_helpers";
class SocialMedia extends models.ServerModel {
_name = "social.media";
_records = [
{
id: 1,
name: "Facebook",
has_streams: true,
media_type: "facebook",
},
];
}
class SocialAccount extends models.ServerModel {
_name = "social.account";
_records = [
{
id: 1,
name: "Jack's Page",
is_media_disconnected: false,
has_account_stats: true,
has_trends: true,
audience: 519,
audience_trend: 50,
engagement: 6000,
engagement_trend: 60,
stories: 70000,
stories_trend: -20,
stats_link: "facebook.com/jack",
media_id: 1,
},
{
id: 2,
name: "Jhon's Page",
has_account_stats: true,
has_trends: false,
audience: 400,
audience_trend: 0,
engagement: 400,
engagement_trend: 0,
stories: 4000,
stories_trend: 0,
stats_link: "facebook.com/jhon",
media_id: 1,
},
];
}
class SocialStream extends models.ServerModel {
_name = "social.stream";
_records = [
{
id: 1,
name: "Stream 1",
media_id: 1,
account_id: 1,
},
{
id: 2,
name: "Stream 2",
media_id: 1,
account_id: 1,
},
];
}
class SocialStreamPostImage extends models.ServerModel {
_name = "social.stream.post.image";
}
class SocialStreamPost extends models.ServerModel {
_name = "social.stream.post";
_records = [
{
id: 1,
author_name: "Jhon",
post_link: "www.odoosocial.com/link1",
author_link: "www.odoosocial.com/author1",
published_date: "2019-08-20 14:16:00",
formatted_published_date: "2019-08-20 14:16:00",
message: "Message 1 Youtube",
link_url: "blog.com/odoosocial",
link_title: "Odoo Social",
link_description: "Odoo Social Description",
facebook_author_id: "1",
facebook_likes_count: 5,
facebook_user_likes: true,
facebook_reactions_count: '{"LIKE": 5}',
facebook_comments_count: 15,
facebook_shares_count: 3,
facebook_reach: 18,
stream_id: 1,
},
{
id: 2,
author_name: "Jack",
post_link: "www.odoosocial.com/link2",
author_link: "www.odoosocial.com/author2",
published_date: "2019-08-20 14:17:00",
formatted_published_date: "2019-08-20 14:17:00",
message: "Message 2 Images",
facebook_author_id: "2",
facebook_likes_count: 10,
facebook_user_likes: false,
facebook_reactions_count: '{"LIKE": 10}',
facebook_comments_count: 25,
facebook_shares_count: 4,
facebook_reach: 33,
stream_id: 2,
},
{
id: 3,
author_name: "Michel",
post_link: "www.odoosocial.com/link3",
author_link: "www.odoosocial.com/author3",
published_date: "2019-08-20 14:18:00",
formatted_published_date: "2019-08-20 14:18:00",
message: "Message 3",
media_type: "facebook",
facebook_author_id: "3",
facebook_likes_count: 0,
facebook_user_likes: false,
facebook_reactions_count: "{}",
facebook_comments_count: 0,
facebook_shares_count: 0,
facebook_reach: 42,
stream_id: 2,
},
];
_views = {
kanban: /*xml*/ `
<kanban class="o_social_stream_post_kanban"
create="0"
edit="0"
records_draggable="false"
group_create="false"
js_class="social_stream_post_kanban_view">
<field name="author_name"/>
<field name="author_link"/>
<field name="post_link"/>
<field name="published_date"/>
<field name="media_type"/>
<field name="account_id"/>
<field name="link_url"/>
<field name="link_image_url"/>
<field name="link_title"/>
<field name="link_description"/>
<field name="stream_post_image_ids"/>
<field name="facebook_author_id"/>
<field name="facebook_user_likes"/>
<field name="facebook_reactions_count"/>
<field name="linkedin_author_image_url"/>
<field name="instagram_facebook_author_id"/>
<field name="twitter_profile_image_url"/>
<templates>
<t t-name="card" class="o_social_stream_post_kanban_global p-0 mb-3">
<div class="o_social_stream_post_message py-2">
<div class="d-flex justify-content-between mb-2 px-2">
<t t-set="author_info">
<span class="o_social_stream_post_author_image o_social_author_image o_avatar position-relative rounded overflow-hidden"/>
<span class="o_social_stream_post_author_name text-truncate ms-2" t-esc="record.author_name.value or 'Unknown'" t-att-title="record.author_name.value or 'Unknown'"/>
</t>
<div class="o_social_author_information d-flex align-items-center">
<a t-if="record.author_link.value"
class="d-flex align-items-center"
t-att-href="record.author_link.value"
t-att-title="record.author_name.value or 'Unknown'"
t-out="author_info"
target="_blank"/>
<div t-else=""
class="d-flex align-items-center"
t-out="author_info"/>
</div>
<a t-att-href="record.post_link.value" target="_blank" class="o_social_stream_post_published_date small" t-att-title="record.published_date.value">
<field name="formatted_published_date"/>
</a>
</div>
<div name="o_social_stream_post_message_body"
class="o_social_stream_post_message_body px-2 pb-2 mb-2 border-bottom}">
<field name="message" widget="social_post_formatter" class="o_social_stream_post_message_text overflow-hidden mb-2"/>
</div>
<div class="o_social_stream_post_facebook_stats px-2 d-flex justify-content-around"
t-if="record.media_type.raw_value === 'facebook'">
<div t-if="record.facebook_likes_count.raw_value !== 0" t-attf-class="o_social_facebook_likes o_social_subtle_btn ps-2 pe-3 #{record.facebook_user_likes.raw_value ? 'o_social_facebook_user_likes' : ''}">
<i class="fa fa-thumbs-up me-1" title="Likes"/>
<field name="facebook_likes_count" class="fw-bold"/>
</div>
<div class="o_social_facebook_comments o_social_comments o_social_subtle_btn px-3">
<i class="fa fa-comments me-1" title="Comments"/>
<field t-if="record.facebook_comments_count.value !== '0'" name="facebook_comments_count" class="fw-bold"/>
</div>
<div class="flex-grow-1 d-flex text-muted justify-content-end">
<field name="facebook_shares_count" class="me-1"/>
Shares
<div class="ms-3">
<field name="facebook_reach"/>
Views
</div>
</div>
</div>
</div>
</t>
</templates>
</kanban>
`,
};
}
class SocialLivePost extends models.ServerModel {
_name = "social.live.post";
}
defineMailModels();
defineModels([
SocialMedia,
SocialAccount,
SocialStream,
SocialStreamPostImage,
SocialStreamPost,
SocialLivePost,
]);
function mockStatsRpc({ checkSteps } = {}) {
onRpc(["refresh_all", "refresh_statistics"], ({ model, method }) => {
if (checkSteps) {
expect.step(`[${model}] ${method}`);
}
return {};
});
}
onRpc("https://graph.facebook.com/", () => "");
test("Check accounts statistics", async () => {
mockStatsRpc({ checkSteps: true });
await mountView({
type: "kanban",
resModel: "social.stream.post",
});
expect.verifySteps([
"[social.stream] refresh_all",
"[social.live.post] refresh_statistics",
"[social.account] refresh_statistics",
]);
expect(".o_social_stream_stat_box").toHaveCount(2, {
message: "Kanban View should contain exactly 2 lines of account statistics.",
});
// 2 because '50%' and `60%` counts as a match
// so if we want to check that there are no actual 0%, it means we want only 2 times "contains 0%"
expect(".o_social_stream_stat_box small:contains('0%')").toHaveCount(2, {
message: "Accounts with has_trends = false should not display trends.",
});
expect(".o_social_stream_stat_box b:contains('519')").toHaveCount(1, {
message: "Audience is correctly displayed.",
});
expect(".o_social_stream_stat_box small:contains('50%')").toHaveCount(1, {
message: "Audience trend is correctly displayed.",
});
expect(".o_social_stream_stat_box small:contains('60%')").toHaveCount(1, {
message: "Engagement trend is correctly displayed.",
});
});
test("Check messages display", async () => {
mockStatsRpc();
await mountView({
type: "kanban",
resModel: "social.stream.post",
});
expect(".o_social_stream_post_kanban_global").toHaveCount(3, {
message: "There should be 3 posts displayed on kanban view.",
});
expect(
".o_social_stream_post_kanban_global:first-child .o_social_stream_post_facebook_stats .o_social_facebook_likes"
).toHaveText("5", { message: "The first post should have 5 likes" });
expect(
".o_social_stream_post_kanban_global:first-child .o_social_stream_post_facebook_stats .o_social_facebook_comments"
).toHaveText("15", { message: "The first post should have 15 comments" });
expect(
".o_social_stream_post_kanban_global:first-child .o_social_stream_post_facebook_stats > *:not(.o_social_facebook_likes):not(.o_social_facebook_comments)"
).toHaveText(`3\nShares\n18 Views`, {
message: "The first post should have 3 shares and 18 'reach'",
});
});
test("Check comments behavior", async () => {
mockStatsRpc();
onRpc("/social_facebook/like_comment", () => {
expect.step("like_comment");
return {};
});
onRpc("/social_facebook/get_comments", () => ({
summary: { total_count: 1 },
comments: [
{
from: {
id: 1,
picture: { data: { url: "socialtest/picture" } },
},
user_likes: false,
message: "Root Comment",
reactions: { LIKE: 3 },
comments: {
data: [
{
from: {
id: 2,
picture: { data: { url: "socialtest/picture" } },
},
user_likes: true,
message: "Sub Comment 1",
reactions: { LIKE: 5 },
},
{
from: {
id: 3,
picture: { data: { url: "socialtest/picture" } },
},
user_likes: false,
message: "Sub Comment 2",
reactions: { LIKE: 10 },
},
],
},
},
],
}));
onRpc("socialtest/picture", () => "");
await mountView({
type: "kanban",
resModel: "social.stream.post",
});
await contains(".o_social_stream_post_facebook_stats .fa-comments").click();
// 1. Root comment is displayed with 3 likes and 2 replies options.
expect(
".o_social_comments_messages .o_social_comment_text:contains('Root Comment')"
).toHaveCount(1, { message: "Root comment should be displayed." });
expect(
".o_social_comment_wrapper .o_social_comment_message:contains('View 2 replies')"
).toHaveCount(1, { message: "There are 2 replies below the root comment." });
expect(".o_social_comment_wrapper .o_social_likes_count:contains('3')").toHaveCount(1, {
message: "The root comment should have 3 likes",
});
// 2. Load replies and check display.
await contains(".o_social_comment_wrapper span.o_social_comment_load_replies").click();
expect(
".o_social_comment_wrapper .o_social_comment_message div.o_social_comment_text:contains('Sub Comment 1')"
).toHaveCount(1, { message: "First sub comment should be loaded" });
expect(
".o_social_comment_wrapper .o_social_comment_message div.o_social_comment_text:contains('Sub Comment 2')"
).toHaveCount(1, { message: "Second sub comment should be loaded" });
// 3. Check like/dislike behavior
// 3a. Check like status and count
expect(
".o_social_comment .o_social_comment:contains('Sub Comment 1') .o_social_comment_user_likes"
).toHaveCount(1, { message: "First comment is liked" });
expect(
".o_social_comment .o_social_comment:contains('Sub Comment 2'):not(.o_social_comment_user_likes)"
).toHaveCount(1, { message: "Second comment is NOT liked" });
expect(
".o_social_comment .o_social_comment:contains('Sub Comment 1') .o_social_likes_count:contains('5')"
).toHaveCount(1, { message: "Sub comment 1 should have 5 likes" });
expect(
".o_social_comment .o_social_comment:contains('Sub Comment 2') .o_social_likes_count:contains('10')"
).toHaveCount(1, { message: "Sub comment 2 should have 10 likes" });
// 3b. Dislike first and like second sub-comments
const subComments = queryAll(".o_social_comment .o_social_comment");
await contains(".o_social_comment_like", { root: subComments[0] }).click();
expect.verifySteps(["like_comment"]);
await contains(".o_social_comment_like", { root: subComments[1] }).click();
expect.verifySteps(["like_comment"]);
// 3a. Check like status and count now that it's reversed
expect(
".o_social_comment .o_social_comment:contains('Sub Comment 1'):not(.o_social_comment_user_likes)"
).toHaveCount(1, { message: "First comment is NOT liked" });
expect(
".o_social_comment .o_social_comment:contains('Sub Comment 2') .o_social_comment_user_likes"
).toHaveCount(1, { message: "Second comment is liked" });
expect(
".o_social_comment .o_social_comment:contains('Sub Comment 1') .o_social_likes_count:contains('4')"
).toHaveCount(1, { message: "Sub comment 1 should have 4 likes" });
expect(
".o_social_comment .o_social_comment:contains('Sub Comment 2') .o_social_likes_count:contains('11')"
).toHaveCount(1, { message: "Sub comment 2 should have 11 likes" });
// 4. Add comment
// Patch "addComment" to return new comment
// Sadly 'XMLHttpRequest' cannot be mocked easily (would have been better)
patchWithCleanup(StreamPostCommentsReply.prototype, {
async _addComment(textarea) {
const formData = new FormData(
textarea.closest(".o_social_write_reply").querySelector("form")
);
this.props.onAddComment({
from: {
id: 1,
picture: { data: { url: "socialtest/picture" } },
},
message: formData.get("message"),
likes: { summary: { total_count: 3 } },
});
},
});
await contains(".o_social_write_reply .o_social_add_comment").edit("New Comment");
await contains(".o_social_write_reply .o_social_add_comment").press("Enter");
expect(
".o_social_comment_wrapper .o_social_comment_message div.o_social_comment_text:contains('New Comment')"
).toHaveCount(1, { message: "New Comment should be displayed." });
// 5. Add reply to comment
await contains(".o_social_comment_wrapper span.o_social_comment_load_replies").click();
await contains(".o_social_comment .o_social_comment .o_social_comment_reply").click();
await contains(".o_social_comment .o_social_add_comment").edit("New Reply");
await contains(".o_social_comment .o_social_add_comment").press("Enter");
expect(
".o_social_comment_wrapper .o_social_comment_message div.o_social_comment_text:contains('New Reply')"
).toHaveCount(1, { message: "New Reply should be displayed" });
});