import { module, test } from "qunit"; import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { click, render } from "@ember/test-helpers"; import { count, exists, query, queryAll, } from "discourse/tests/helpers/qunit-helpers"; import { hbs } from "ember-cli-htmlbars"; import EmberObject from "@ember/object"; import I18n from "I18n"; import User from "discourse/models/user"; import { getOwner } from "discourse-common/lib/get-owner"; module("Integration | Component | Widget | post", function (hooks) { setupRenderingTest(hooks); test("basic elements", async function (assert) { this.set("args", { shareUrl: "/example", post_number: 1 }); await render(hbs``); assert.ok(exists(".names"), "includes poster name"); assert.ok(exists("a.post-date"), "includes post date"); }); test("post - links", async function (assert) { this.set("args", { cooked: "first link and second link", linkCounts: [ { url: "http://link1.example.com/", clicks: 1, internal: true }, { url: "http://link2.example.com/", clicks: 2, internal: true }, ], }); await render( hbs`` ); assert.strictEqual(queryAll(".badge.clicks")[0].innerText, "1"); assert.strictEqual(queryAll(".badge.clicks")[1].innerText, "2"); }); test("post - onebox links", async function (assert) { this.set("args", { cooked: `

Other URL

`, linkCounts: [ { url: "https://example.com", clicks: 1 }, { url: "https://twitter.com/codinghorror", clicks: 2 }, ], }); await render( hbs`` ); assert.strictEqual(count(".badge.clicks"), 2); assert.strictEqual(queryAll(".badge.clicks")[0].innerText, "1"); assert.strictEqual(queryAll(".badge.clicks")[1].innerText, "2"); }); test("wiki", async function (assert) { this.set("args", { wiki: true, version: 2, canViewEditHistory: true }); this.set("showHistory", () => (this.historyShown = true)); await render(hbs` `); await click(".post-info .wiki"); assert.ok( this.historyShown, "clicking the wiki icon displays the post history" ); }); test("wiki without revision", async function (assert) { this.set("args", { wiki: true, version: 1, canViewEditHistory: true }); this.set("editPost", () => (this.editPostCalled = true)); await render(hbs` `); await click(".post-info .wiki"); assert.ok(this.editPostCalled, "clicking wiki icon edits the post"); }); test("via-email", async function (assert) { this.set("args", { via_email: true, canViewRawEmail: true }); this.set("showRawEmail", () => (this.rawEmailShown = true)); await render(hbs` `); await click(".post-info.via-email"); assert.ok(this.rawEmailShown, "clicking the envelope shows the raw email"); }); test("via-email without permission", async function (assert) { this.set("args", { via_email: true, canViewRawEmail: false }); this.set("showRawEmail", () => (this.rawEmailShown = true)); await render(hbs` `); await click(".post-info.via-email"); assert.ok( !this.rawEmailShown, "clicking the envelope doesn't show the raw email" ); }); test("history", async function (assert) { this.set("args", { version: 3, canViewEditHistory: true }); this.set("showHistory", () => (this.historyShown = true)); await render(hbs` `); await click(".post-info.edits button"); assert.ok(this.historyShown, "clicking the pencil shows the history"); }); test("history without view permission", async function (assert) { this.set("args", { version: 3, canViewEditHistory: false }); this.set("showHistory", () => (this.historyShown = true)); await render(hbs` `); await click(".post-info.edits"); assert.ok( !this.historyShown, `clicking the pencil doesn't show the history` ); }); test("whisper", async function (assert) { this.set("args", { isWhisper: true }); await render(hbs``); assert.strictEqual(count(".topic-post.whisper"), 1); assert.strictEqual(count(".post-info.whisper"), 1); }); test("like count button", async function (assert) { const store = getOwner(this).lookup("service:store"); const topic = store.createRecord("topic", { id: 123 }); const post = store.createRecord("post", { id: 1, post_number: 1, topic, like_count: 3, actions_summary: [{ id: 2, count: 1, hidden: false, can_act: true }], }); this.set("post", post); this.set("args", { likeCount: 1 }); await render( hbs`` ); assert.strictEqual(count("button.like-count"), 1); assert.ok(!exists(".who-liked")); // toggle it on await click("button.like-count"); assert.strictEqual(count(".who-liked"), 1); assert.strictEqual(count(".who-liked a.trigger-user-card"), 1); // toggle it off await click("button.like-count"); assert.ok(!exists(".who-liked")); assert.ok(!exists(".who-liked a.trigger-user-card")); }); test(`like count with no likes`, async function (assert) { this.set("args", { likeCount: 0 }); await render( hbs`` ); assert.ok(!exists("button.like-count")); }); test("share button", async function (assert) { this.set("args", { shareUrl: "http://share-me.example.com" }); await render(hbs``); assert.ok(exists(".actions button.share"), "it renders a share button"); }); test("liking", async function (assert) { const args = { showLike: true, canToggleLike: true, id: 5 }; this.set("args", args); this.set("toggleLike", () => { args.liked = !args.liked; args.likeCount = args.liked ? 1 : 0; }); await render(hbs` `); assert.ok(exists(".actions button.like")); assert.ok(!exists(".actions button.like-count")); await click(".actions button.like"); assert.ok(!exists(".actions button.like")); assert.ok(exists(".actions button.has-like")); assert.strictEqual(count(".actions button.like-count"), 1); await click(".actions button.has-like"); assert.ok(exists(".actions button.like")); assert.ok(!exists(".actions button.has-like")); assert.ok(!exists(".actions button.like-count")); }); test("anon liking", async function (assert) { this.owner.unregister("service:current-user"); const args = { showLike: true }; this.set("args", args); this.set("showLogin", () => (this.loginShown = true)); await render(hbs` `); assert.ok(exists(".actions button.like")); assert.ok(!exists(".actions button.like-count")); assert.strictEqual( query("button.like").getAttribute("title"), I18n.t("post.controls.like"), `shows the right button title for anonymous users` ); await click(".actions button.like"); assert.ok(this.loginShown); }); test("edit button", async function (assert) { this.set("args", { canEdit: true }); this.set("editPost", () => (this.editPostCalled = true)); await render(hbs` `); await click("button.edit"); assert.ok(this.editPostCalled, "it triggered the edit action"); }); test(`edit button - can't edit`, async function (assert) { this.set("args", { canEdit: false }); await render(hbs``); assert.ok(!exists("button.edit"), "button is not displayed"); }); test("recover button", async function (assert) { this.set("args", { canDelete: true }); this.set("deletePost", () => (this.deletePostCalled = true)); await render(hbs` `); await click("button.delete"); assert.ok(this.deletePostCalled, "it triggered the delete action"); }); test("delete topic button", async function (assert) { this.set("args", { canDeleteTopic: true }); this.set("deletePost", () => (this.deletePostCalled = true)); await render(hbs` `); await click("button.delete"); assert.ok(this.deletePostCalled, "it triggered the delete action"); }); test(`delete topic button - can't delete`, async function (assert) { this.set("args", { canDeleteTopic: false }); await render(hbs``); assert.ok(!exists("button.delete"), `button is not displayed`); }); test(`delete topic button - can't delete when topic author without permission`, async function (assert) { this.set("args", { canDeleteTopic: false, showFlagDelete: true, canFlag: true, }); await render(hbs``); await click(".show-more-actions"); assert.strictEqual(count("button.create-flag"), 1, `button is displayed`); assert.strictEqual(count("button.delete"), 1, `button is displayed`); assert.strictEqual( query("button.delete").getAttribute("title"), I18n.t("post.controls.delete_topic_disallowed"), `shows the right button title for users without permissions` ); }); test("recover topic button", async function (assert) { this.set("args", { canRecoverTopic: true }); this.set("recoverPost", () => (this.recovered = true)); await render(hbs` `); await click("button.recover"); assert.ok(this.recovered); }); test(`recover topic button - can't recover`, async function (assert) { this.set("args", { canRecoverTopic: false }); await render(hbs``); assert.ok(!exists("button.recover"), `button is not displayed`); }); test("delete post button", async function (assert) { this.set("args", { canDelete: true, canFlag: true }); this.set("deletePost", () => (this.deletePostCalled = true)); await render(hbs` `); await click(".show-more-actions"); await click("button.delete"); assert.ok(this.deletePostCalled, "it triggered the delete action"); }); test(`delete post button - can't delete`, async function (assert) { this.set("args", { canDelete: false }); await render(hbs``); assert.ok(!exists("button.delete"), `button is not displayed`); }); test(`delete post button - can't delete, can't flag`, async function (assert) { this.set("args", { canDeleteTopic: false, showFlagDelete: false, canFlag: false, }); await render(hbs``); assert.ok(!exists("button.delete"), `delete button is not displayed`); assert.ok(!exists("button.create-flag"), `flag button is not displayed`); }); test("recover post button", async function (assert) { this.set("args", { canRecover: true }); this.set("recoverPost", () => (this.recovered = true)); await render(hbs` `); await click("button.recover"); assert.ok(this.recovered); }); test(`recover post button - can't recover`, async function (assert) { this.set("args", { canRecover: false }); await render(hbs``); assert.ok(!exists("button.recover"), `button is not displayed`); }); test(`flagging`, async function (assert) { this.set("args", { canFlag: true }); this.set("showFlags", () => (this.flagsShown = true)); await render(hbs` `); assert.strictEqual(count("button.create-flag"), 1); await click("button.create-flag"); assert.ok(this.flagsShown, "it triggered the action"); }); test(`flagging: can't flag`, async function (assert) { this.set("args", { canFlag: false }); await render(hbs``); assert.ok(!exists("button.create-flag")); }); test(`flagging: can't flag when post is hidden`, async function (assert) { this.set("args", { canFlag: true, hidden: true }); await render(hbs``); assert.ok(!exists("button.create-flag")); }); test(`read indicator`, async function (assert) { this.set("args", { read: true }); await render(hbs``); assert.ok(exists(".read-state.read")); }); test(`unread indicator`, async function (assert) { this.set("args", { read: false }); await render(hbs``); assert.ok(exists(".read-state")); }); test("reply directly above (suppressed)", async function (assert) { this.set("args", { replyToUsername: "eviltrout", replyToAvatarTemplate: "/images/avatar.png", replyDirectlyAbove: true, }); await render(hbs``); assert.ok(!exists("a.reply-to-tab"), "hides the tab"); assert.ok(!exists(".avoid-tab"), "doesn't have the avoid tab class"); }); test("reply a few posts above (suppressed)", async function (assert) { this.set("args", { replyToUsername: "eviltrout", replyToAvatarTemplate: "/images/avatar.png", replyDirectlyAbove: false, }); await render(hbs``); assert.ok(exists("a.reply-to-tab"), "shows the tab"); assert.strictEqual(count(".avoid-tab"), 1, "has the avoid tab class"); }); test("reply directly above", async function (assert) { this.set("args", { replyToUsername: "eviltrout", replyToAvatarTemplate: "/images/avatar.png", replyDirectlyAbove: true, }); this.siteSettings.suppress_reply_directly_above = false; await render(hbs``); assert.strictEqual(count(".avoid-tab"), 1, "has the avoid tab class"); await click("a.reply-to-tab"); assert.strictEqual(count("section.embedded-posts.top .cooked"), 1); assert.strictEqual(count("section.embedded-posts .d-icon-arrow-up"), 1); }); test("cooked content hidden", async function (assert) { this.set("args", { cooked_hidden: true }); this.set("expandHidden", () => (this.unhidden = true)); await render(hbs` `); await click(".topic-body .expand-hidden"); assert.ok(this.unhidden, "triggers the action"); }); test("expand first post", async function (assert) { const store = getOwner(this).lookup("service:store"); this.set("args", { expandablePost: true }); this.set("post", store.createRecord("post", { id: 1234 })); await render( hbs`` ); await click(".topic-body .expand-post"); assert.ok(!exists(".expand-post"), "button is gone"); }); test("can't bookmark", async function (assert) { this.set("args", { canBookmark: false }); await render(hbs``); assert.ok(!exists("button.bookmark")); assert.ok(!exists("button.bookmarked")); }); test("bookmark", async function (assert) { const args = { canBookmark: true }; this.set("args", args); this.set("toggleBookmark", () => (args.bookmarked = true)); await render(hbs` `); assert.strictEqual(count(".post-menu-area .bookmark"), 1); assert.ok(!exists("button.bookmarked")); }); test("can't show admin menu when you can't manage", async function (assert) { this.set("args", { canManage: false }); await render(hbs``); assert.ok(!exists(".post-menu-area .show-post-admin-menu")); }); test("show admin menu", async function (assert) { this.set("args", { canManage: true }); await render(hbs``); assert.ok(!exists(".post-admin-menu")); await click(".post-menu-area .show-post-admin-menu"); assert.strictEqual(count(".post-admin-menu"), 1, "it shows the popup"); await click(".post-menu-area"); assert.ok(!exists(".post-admin-menu"), "clicking outside clears the popup"); }); test("permanently delete topic", async function (assert) { this.set("args", { canManage: true, canPermanentlyDelete: true }); this.set("permanentlyDeletePost", () => (this.deleted = true)); await render( hbs`` ); await click(".post-menu-area .show-post-admin-menu"); await click(".post-admin-menu .permanently-delete"); assert.ok(this.deleted); assert.ok(!exists(".post-admin-menu"), "also hides the menu"); }); test("permanently delete post", async function (assert) { this.set("args", { canManage: true, canPermanentlyDelete: true }); this.set("permanentlyDeletePost", () => (this.deleted = true)); await render(hbs` `); await click(".post-menu-area .show-post-admin-menu"); await click(".post-admin-menu .permanently-delete"); assert.ok(this.deleted); assert.ok(!exists(".post-admin-menu"), "also hides the menu"); }); test("toggle moderator post", async function (assert) { this.currentUser.set("moderator", true); this.set("args", { canManage: true }); this.set("togglePostType", () => (this.toggled = true)); await render(hbs` `); await click(".post-menu-area .show-post-admin-menu"); await click(".post-admin-menu .toggle-post-type"); assert.ok(this.toggled); assert.ok(!exists(".post-admin-menu"), "also hides the menu"); }); test("toggle moderator post", async function (assert) { this.currentUser.set("moderator", true); this.set("args", { canManage: true }); this.set("togglePostType", () => (this.toggled = true)); await render(hbs` `); await click(".post-menu-area .show-post-admin-menu"); await click(".post-admin-menu .toggle-post-type"); assert.ok(this.toggled); assert.ok(!exists(".post-admin-menu"), "also hides the menu"); }); test("rebake post", async function (assert) { this.set("args", { canManage: true }); this.set("rebakePost", () => (this.baked = true)); await render(hbs` `); await click(".post-menu-area .show-post-admin-menu"); await click(".post-admin-menu .rebuild-html"); assert.ok(this.baked); assert.ok(!exists(".post-admin-menu"), "also hides the menu"); }); test("unhide post", async function (assert) { this.currentUser.admin = true; this.set("args", { canManage: true, hidden: true }); this.set("unhidePost", () => (this.unhidden = true)); await render(hbs` `); await click(".post-menu-area .show-post-admin-menu"); await click(".post-admin-menu .unhide-post"); assert.ok(this.unhidden); assert.ok(!exists(".post-admin-menu"), "also hides the menu"); }); test("change owner", async function (assert) { this.currentUser.admin = true; this.set("args", { canManage: true }); this.set("changePostOwner", () => (this.owned = true)); await render(hbs` `); await click(".post-menu-area .show-post-admin-menu"); await click(".post-admin-menu .change-owner"); assert.ok(this.owned); assert.ok(!exists(".post-admin-menu"), "also hides the menu"); }); test("reply", async function (assert) { this.set("args", { canCreatePost: true }); this.set("replyToPost", () => (this.replied = true)); await render(hbs` `); await click(".post-controls .create"); assert.ok(this.replied); }); test("reply - without permissions", async function (assert) { this.set("args", { canCreatePost: false }); await render(hbs``); assert.ok(!exists(".post-controls .create")); }); test("replies - no replies", async function (assert) { this.set("args", { replyCount: 0 }); await render(hbs``); assert.ok(!exists("button.show-replies")); }); test("replies - multiple replies", async function (assert) { this.siteSettings.suppress_reply_directly_below = true; this.set("args", { replyCount: 2, replyDirectlyBelow: true }); await render(hbs``); assert.strictEqual(count("button.show-replies"), 1); }); test("replies - one below, suppressed", async function (assert) { this.siteSettings.suppress_reply_directly_below = true; this.set("args", { replyCount: 1, replyDirectlyBelow: true }); await render(hbs``); assert.ok(!exists("button.show-replies")); }); test("replies - one below, not suppressed", async function (assert) { this.siteSettings.suppress_reply_directly_below = false; this.set("args", { id: 6654, replyCount: 1, replyDirectlyBelow: true }); await render(hbs``); await click("button.show-replies"); assert.strictEqual(count("section.embedded-posts.bottom .cooked"), 1); assert.strictEqual(count("section.embedded-posts .d-icon-arrow-down"), 1); }); test("topic map not shown", async function (assert) { this.set("args", { showTopicMap: false }); await render(hbs``); assert.ok(!exists(".topic-map")); }); test("topic map - few posts", async function (assert) { this.set("args", { showTopicMap: true, topicPostsCount: 2, participants: [{ username: "eviltrout" }, { username: "codinghorror" }], }); await render(hbs``); assert.ok( !exists("li.avatars a.poster"), "shows no participants when collapsed" ); await click("nav.buttons button"); assert.strictEqual( count(".topic-map-expanded a.poster"), 2, "shows all when expanded" ); }); test("topic map - participants", async function (assert) { this.set("args", { showTopicMap: true, topicPostsCount: 10, participants: [ { username: "eviltrout" }, { username: "codinghorror" }, { username: "sam" }, { username: "ZogStrIP" }, ], userFilters: ["sam", "codinghorror"], }); await render(hbs``); assert.strictEqual( count("li.avatars a.poster"), 3, "limits to three participants" ); await click("nav.buttons button"); assert.ok(!exists("li.avatars a.poster")); assert.strictEqual( count(".topic-map-expanded a.poster"), 4, "shows all when expanded" ); assert.strictEqual(count("a.poster.toggled"), 2, "two are toggled"); }); test("topic map - links", async function (assert) { this.set("args", { showTopicMap: true, topicLinks: [ { url: "http://link1.example.com", clicks: 0 }, { url: "http://link2.example.com", clicks: 0 }, { url: "http://link3.example.com", clicks: 0 }, { url: "http://link4.example.com", clicks: 0 }, { url: "http://link5.example.com", clicks: 0 }, { url: "http://link6.example.com", clicks: 0 }, ], }); await render(hbs``); assert.strictEqual(count(".topic-map"), 1); assert.strictEqual(count(".map.map-collapsed"), 1); assert.ok(!exists(".topic-map-expanded")); await click("nav.buttons button"); assert.ok(!exists(".map.map-collapsed")); assert.strictEqual(count(".topic-map .d-icon-chevron-up"), 1); assert.strictEqual(count(".topic-map-expanded"), 1); assert.strictEqual( count(".topic-map-expanded .topic-link"), 5, "it limits the links displayed" ); await click(".link-summary button"); assert.strictEqual( count(".topic-map-expanded .topic-link"), 6, "all links now shown" ); }); test("topic map - no summary", async function (assert) { this.set("args", { showTopicMap: true }); await render(hbs``); assert.ok(!exists(".toggle-summary")); }); test("topic map - has summary", async function (assert) { this.set("args", { showTopicMap: true, hasTopicSummary: true }); this.set("showSummary", () => (this.summaryToggled = true)); await render( hbs`` ); assert.strictEqual(count(".toggle-summary"), 1); await click(".toggle-summary button"); assert.ok(this.summaryToggled); }); test("pm map", async function (assert) { this.set("args", { showTopicMap: true, showPMMap: true, allowedGroups: [], allowedUsers: [EmberObject.create({ username: "eviltrout" })], }); await render(hbs``); assert.strictEqual(count(".private-message-map"), 1); assert.strictEqual(count(".private-message-map .user"), 1); }); test("post notice - with username", async function (assert) { const twoDaysAgo = new Date(); twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); this.siteSettings.display_name_on_posts = false; this.siteSettings.prioritize_username_in_ux = true; this.siteSettings.old_post_notice_days = 14; this.set("args", { username: "codinghorror", name: "Jeff", created_at: new Date(), notice: { type: "returning_user", lastPostedAt: twoDaysAgo, }, }); await render(hbs``); assert.strictEqual( query(".post-notice.returning-user:not(.old)").innerText.trim(), I18n.t("post.notice.returning_user", { user: "codinghorror", time: "2 days ago", }) ); }); test("post notice - with name", async function (assert) { this.siteSettings.display_name_on_posts = true; this.siteSettings.prioritize_username_in_ux = false; this.siteSettings.old_post_notice_days = 14; this.set("args", { username: "codinghorror", name: "Jeff", created_at: new Date(2019, 0, 1), notice: { type: "new_user" }, }); await render(hbs``); assert.strictEqual( query(".post-notice.old.new-user").innerText.trim(), I18n.t("post.notice.new_user", { user: "Jeff", time: "Jan '10" }) ); }); test("show group request in post", async function (assert) { this.set("args", { username: "foo", requestedGroupName: "testGroup", }); await render(hbs``); const link = query(".group-request a"); assert.strictEqual(link.innerText.trim(), I18n.t("groups.requests.handle")); assert.strictEqual( link.getAttribute("href"), "/g/testGroup/requests?filter=foo" ); }); test("shows user status if enabled in site settings", async function (assert) { this.siteSettings.enable_user_status = true; const status = { emoji: "tooth", description: "off to dentist", }; const user = User.create({ status }); this.set("args", { user }); await render(hbs``); assert.ok(exists(".user-status-message")); }); test("doesn't show user status if disabled in site settings", async function (assert) { this.siteSettings.enable_user_status = false; const status = { emoji: "tooth", description: "off to dentist", }; const user = User.create({ status }); this.set("args", { user }); await render(hbs``); assert.notOk(exists(".user-status-message")); }); });