This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/tests/acceptance/topic-test.js
Martin Brennan ac7bf98ad1
DEV: Load client site settings YML into JS tests (#18413)
Our method of loading a subset of client settings into tests via
tests/helpers/site-settings.js can be improved upon. Currently we have a
hardcoded subset of the client settings, which may get out of date and not have
the correct defaults. As well as this plugins do not get their settings into the
tests, so whenever you need a setting from a plugin, even if it has a default,
you have to do needs.setting({ ... }) which is inconvenient.

This commit introduces an ember CLI build step to take the site_settings.yml and
all the plugin settings.yml files, pull out the client settings, and dump them
into a variable in a single JS file we can load in our tests, so we have the
correct selection of settings and default values in our JS tests. It also fixes
many, many tests that were operating under incorrect assumptions or old
settings.

Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
2022-11-08 09:17:43 +10:00

779 lines
23 KiB
JavaScript

import {
acceptance,
chromeTest,
count,
exists,
publishToMessageBus,
query,
selectText,
visible,
} from "discourse/tests/helpers/qunit-helpers";
import {
click,
currentURL,
fillIn,
triggerKeyEvent,
visit,
} from "@ember/test-helpers";
import I18n from "I18n";
import selectKit from "discourse/tests/helpers/select-kit-helper";
import { test } from "qunit";
import { withPluginApi } from "discourse/lib/plugin-api";
import topicFixtures from "discourse/tests/fixtures/topic";
import { cloneJSON } from "discourse-common/lib/object";
import CategoryFixtures from "discourse/tests/fixtures/category-fixtures";
acceptance("Topic", function (needs) {
needs.user();
needs.pretender((server, helper) => {
server.get("/c/2/visible_groups.json", () =>
helper.response(200, {
groups: [],
})
);
server.get("/c/feature/find_by_slug.json", () => {
return helper.response(200, CategoryFixtures["/c/1/show.json"]);
});
server.put("/posts/398/wiki", () => {
return helper.response({});
});
});
test("Reply as new topic", async function (assert) {
await visit("/t/internationalization-localization/280");
await click("button.share:nth-of-type(1)");
await click("button.new-topic");
assert.ok(exists(".d-editor-input"), "the composer input is visible");
assert.strictEqual(
query(".d-editor-input").value.trim(),
`Continuing the discussion from [Internationalization / localization](${window.location.origin}/t/internationalization-localization/280):`,
"it fills composer with the ring string"
);
assert.strictEqual(
selectKit(".category-chooser").header().value(),
"2",
"it fills category selector with the right category"
);
});
test("Reply as new message", async function (assert) {
await visit("/t/pm-for-testing/12");
await click("button.share:nth-of-type(1)");
await click("button.new-topic");
assert.ok(exists(".d-editor-input"), "the composer input is visible");
assert.strictEqual(
query(".d-editor-input").value.trim(),
`Continuing the discussion from [PM for testing](${window.location.origin}/t/pm-for-testing/12):`,
"it fills composer with the ring string"
);
const privateMessageUsers = selectKit("#private-message-users");
assert.strictEqual(
privateMessageUsers.header().value(),
"someguy,test,Group",
"it fills up the composer correctly"
);
});
test("Share Modal", async function (assert) {
await visit("/t/internationalization-localization/280");
await click(".topic-post:first-child button.share");
assert.ok(exists(".share-topic-modal"), "it shows the share modal");
});
test("Showing and hiding the edit controls", async function (assert) {
await visit("/t/internationalization-localization/280");
await click("#topic-title .d-icon-pencil-alt");
assert.ok(exists("#edit-title"), "it shows the editing controls");
assert.ok(
!exists(".title-wrapper .remove-featured-link"),
"link to remove featured link is not shown"
);
await fillIn("#edit-title", "this is the new title");
await click("#topic-title .cancel-edit");
assert.ok(!exists("#edit-title"), "it hides the editing controls");
});
test("Updating the topic title and category", async function (assert) {
const categoryChooser = selectKit(".title-wrapper .category-chooser");
await visit("/t/internationalization-localization/280");
await click("#topic-title .d-icon-pencil-alt");
await fillIn("#edit-title", "this is the new title");
await categoryChooser.expand();
await categoryChooser.selectRowByValue(4);
await click("#topic-title .submit-edit");
assert.strictEqual(
query("#topic-title .badge-category").innerText,
"faq",
"it displays the new category"
);
assert.strictEqual(
query(".fancy-title").innerText.trim(),
"this is the new title",
"it displays the new title"
);
});
test("Marking a topic as wiki", async function (assert) {
await visit("/t/internationalization-localization/280");
assert.ok(!exists("a.wiki"), "it does not show the wiki icon");
await click(".topic-post:nth-of-type(1) button.show-more-actions");
await click(".topic-post:nth-of-type(1) button.show-post-admin-menu");
await click(".btn.wiki");
assert.strictEqual(count("button.wiki"), 1, "it shows the wiki icon");
});
test("Visit topic routes", async function (assert) {
await visit("/t/12");
assert.strictEqual(
query(".fancy-title").innerText.trim(),
"PM for testing",
"it routes to the right topic"
);
await visit("/t/280/20");
assert.strictEqual(
query(".fancy-title").innerText.trim(),
"Internationalization / localization",
"it routes to the right topic"
);
});
test("Updating the topic title with emojis", async function (assert) {
await visit("/t/internationalization-localization/280");
await click("#topic-title .d-icon-pencil-alt");
await fillIn("#edit-title", "emojis title :bike: :blonde_woman:t6:");
await click("#topic-title .submit-edit");
assert.ok(
query(".fancy-title").innerHTML.trim().includes("bike.png"),
"it displays the new title with emojis"
);
});
test("Updating the topic title with unicode emojis", async function (assert) {
await visit("/t/internationalization-localization/280");
await click("#topic-title .d-icon-pencil-alt");
await fillIn("#edit-title", "emojis title 👨‍🌾🙏");
await click("#topic-title .submit-edit");
assert.ok(
query(".fancy-title").innerHTML.trim().includes("man_farmer.png"),
"it displays the new title with emojis"
);
});
test("Updating the topic title with unicode emojis without whitespace", async function (assert) {
this.siteSettings.enable_inline_emoji_translation = true;
await visit("/t/internationalization-localization/280");
await click("#topic-title .d-icon-pencil-alt");
await fillIn("#edit-title", "Test🙂Title");
await click("#topic-title .submit-edit");
assert.ok(
query(".fancy-title")
.innerHTML.trim()
.includes("slightly_smiling_face.png"),
"it displays the new title with emojis"
);
});
test("Suggested topics", async function (assert) {
await visit("/t/internationalization-localization/280");
assert.strictEqual(
query("#suggested-topics .suggested-topics-title").innerText.trim(),
I18n.t("suggested_topics.title")
);
});
test("Deleting a topic", async function (assert) {
this.siteSettings.min_topic_views_for_delete_confirm = 10000;
await visit("/t/internationalization-localization/280");
await click(".topic-post:nth-of-type(1) button.show-more-actions");
await click(".widget-button.delete");
await click(".toggle-admin-menu");
assert.ok(exists(".topic-admin-recover"), "it shows the recover button");
});
test("Deleting a popular topic displays confirmation modal", async function (assert) {
this.siteSettings.min_topic_views_for_delete_confirm = 10;
await visit("/t/internationalization-localization/280");
await click(".topic-post:nth-of-type(1) button.show-more-actions");
await click(".widget-button.delete");
assert.ok(
visible(".delete-topic-confirm-modal"),
"it shows the delete confirmation modal"
);
await click(".delete-topic-confirm-modal .btn-primary");
assert.ok(
!visible(".delete-topic-confirm-modal"),
"it hides the delete confirmation modal"
);
await click(".widget-button.delete");
await click(".delete-topic-confirm-modal .btn-danger");
await click(".toggle-admin-menu");
assert.ok(exists(".topic-admin-recover"), "it shows the recover button");
});
test("Group category moderator posts", async function (assert) {
await visit("/t/topic-for-group-moderators/2480");
assert.ok(exists(".category-moderator"), "it has a class applied");
assert.ok(exists(".d-icon-shield-alt"), "it shows an icon");
});
test("Suspended user posts", async function (assert) {
await visit("/t/topic-from-suspended-user/54077");
assert.ok(
exists(".topic-post.user-suspended > #post_1"),
"it has a class applied"
);
});
});
acceptance("Topic featured links", function (needs) {
needs.user();
needs.settings({
topic_featured_link_enabled: true,
max_topic_title_length: 80,
exclude_rel_nofollow_domains: "example.com",
display_name_on_posts: false,
prioritize_username_in_ux: true,
});
test("remove nofollow attribute", async function (assert) {
await visit("/t/-/299/1");
const link = query(".title-wrapper .topic-featured-link");
assert.strictEqual(link.innerText, " example.com");
assert.strictEqual(link.getAttribute("rel"), "ugc");
});
test("remove featured link", async function (assert) {
await visit("/t/-/299/1");
assert.ok(
exists(".title-wrapper .topic-featured-link"),
"link is shown with topic title"
);
await click(".title-wrapper .edit-topic");
assert.ok(
exists(".title-wrapper .remove-featured-link"),
"link to remove featured link"
);
// TODO: decide if we want to test this, test is flaky so it
// was commented out.
// If not fixed by May 2021, delete this code block
//
//await click(".title-wrapper .remove-featured-link");
//await click(".title-wrapper .submit-edit");
//assert.ok(!exists(".title-wrapper .topic-featured-link"), "link is gone");
});
test("Converting to a public topic", async function (assert) {
await visit("/t/test-pm/34");
assert.ok(exists(".private_message"));
await click(".toggle-admin-menu");
await click(".topic-admin-convert button");
let categoryChooser = selectKit(
".convert-to-public-topic .category-chooser"
);
await categoryChooser.expand();
await categoryChooser.selectRowByValue(21);
await click(".convert-to-public-topic .btn-primary");
assert.ok(!exists(".private_message"));
});
test("Unpinning unlisted topic", async function (assert) {
await visit("/t/internationalization-localization/280");
await click(".toggle-admin-menu");
await click(".topic-admin-pin .btn");
await click(".make-banner");
await click(".toggle-admin-menu");
await click(".topic-admin-visible .btn");
await click(".toggle-admin-menu");
assert.ok(
exists(".topic-admin-pin"),
"it should show the multi select menu"
);
});
test("selecting posts", async function (assert) {
await visit("/t/internationalization-localization/280");
await click(".toggle-admin-menu");
await click(".topic-admin-multi-select .btn");
assert.ok(
exists(".selected-posts:not(.hidden)"),
"it should show the multi select menu"
);
assert.ok(
exists(".select-all"),
"it should allow users to select all the posts"
);
});
test("select below", async function (assert) {
await visit("/t/internationalization-localization/280");
await click(".toggle-admin-menu");
await click(".topic-admin-multi-select .btn");
await click("#post_3 .select-below");
assert.ok(
query(".selected-posts").innerHTML.includes(
I18n.t("topic.multi_select.description", { count: 18 })
),
"it should select the right number of posts"
);
await click("#post_2 .select-below");
assert.ok(
query(".selected-posts").innerHTML.includes(
I18n.t("topic.multi_select.description", { count: 19 })
),
"it should select the right number of posts"
);
});
test("View Hidden Replies", async function (assert) {
await visit("/t/internationalization-localization/280");
await click(".gap");
assert.ok(!exists(".gap"), "it hides gap");
});
chromeTest(
"Quoting a quote keeps the original poster name",
async function (assert) {
await visit("/t/internationalization-localization/280");
await selectText("#post_5 blockquote");
await click(".quote-button .insert-quote");
assert.ok(
query(".d-editor-input").value.includes(
'quote="codinghorror said, post:3, topic:280"'
)
);
}
);
chromeTest(
"Quoting a quote of a different topic keeps the original topic title",
async function (assert) {
await visit("/t/internationalization-localization/280");
await selectText("#post_9 blockquote");
await click(".quote-button .insert-quote");
assert.ok(
query(".d-editor-input").value.includes(
'quote="A new topic with a link to another topic, post:3, topic:62"'
)
);
}
);
chromeTest(
"Quoting a quote with the Reply button keeps the original poster name",
async function (assert) {
await visit("/t/internationalization-localization/280");
await selectText("#post_5 blockquote");
await click(".reply");
assert.ok(
query(".d-editor-input").value.includes(
'quote="codinghorror said, post:3, topic:280"'
)
);
}
);
// Using J/K on Firefox clean the text selection, so this won't work there
chromeTest(
"Quoting a quote with replyAsNewTopic keeps the original poster name",
async function (assert) {
await visit("/t/internationalization-localization/280");
await selectText("#post_5 blockquote");
await triggerKeyEvent(document, "keypress", "J");
await triggerKeyEvent(document, "keypress", "T");
assert.ok(
query(".d-editor-input").value.includes(
'quote="codinghorror said, post:3, topic:280"'
)
);
}
);
test("Quoting by selecting text can mark the quote as full", async function (assert) {
await visit("/t/internationalization-localization/280");
await selectText("#post_5 .cooked");
await click(".quote-button .insert-quote");
assert.ok(
query(".d-editor-input").value.includes(
'quote="pekka, post:5, topic:280, full:true"'
)
);
});
});
acceptance("Topic featured links", function (needs) {
needs.user();
needs.settings({
topic_featured_link_enabled: true,
max_topic_title_length: 80,
});
test("remove featured link", async function (assert) {
await visit("/t/-/299/1");
assert.ok(
exists(".title-wrapper .topic-featured-link"),
"link is shown with topic title"
);
await click(".title-wrapper .edit-topic");
assert.ok(
exists(".title-wrapper .remove-featured-link"),
"link to remove featured link"
);
});
});
acceptance("Topic with title decorated", function (needs) {
needs.user();
needs.hooks.beforeEach(() => {
withPluginApi("0.8.40", (api) => {
api.decorateTopicTitle((topic, node, topicTitleType) => {
node.innerText = `${node.innerText}-${topic.id}-${topicTitleType}`;
});
});
});
test("Decorate topic title", async function (assert) {
await visit("/t/internationalization-localization/280");
assert.ok(
query(".fancy-title").innerText.endsWith("-280-topic-title"),
"it decorates topic title"
);
assert.ok(
query(".raw-topic-link:nth-child(1)").innerText.endsWith(
"-27331-topic-list-item-title"
),
"it decorates topic list item title"
);
});
});
acceptance("Topic pinning/unpinning as an admin", function (needs) {
needs.user({ admin: true });
test("Admin pinning topic", async function (assert) {
await visit("/t/topic-for-group-moderators/2480");
await click(".toggle-admin-menu");
await click(".topic-admin-pin .btn");
assert.ok(
exists(".feature-topic .btn-primary"),
"it should show the 'Pin Topic' button"
);
assert.ok(
exists(".make-banner"),
"it should show the 'Banner Topic' button"
);
});
});
acceptance("Topic pinning/unpinning as a staff member", function (needs) {
needs.user({ moderator: true, admin: false, trust_level: 2 });
test("Staff pinning topic", async function (assert) {
await visit("/t/topic-for-group-moderators/2480");
await click(".toggle-admin-menu");
await click(".topic-admin-pin .btn");
assert.ok(
exists(".feature-topic .btn-primary"),
"it should show the 'Pin Topic' button"
);
assert.ok(
exists(".make-banner"),
"it should show the 'Banner Topic' button"
);
});
});
acceptance("Topic pinning/unpinning as a group moderator", function (needs) {
needs.user({ moderator: false, admin: false, trust_level: 1 });
test("Group category moderator pinning topic", async function (assert) {
await visit("/t/topic-for-group-moderators/2480");
await click(".toggle-admin-menu");
await click(".topic-admin-pin .btn");
assert.ok(
exists(".feature-topic .btn-primary"),
"it should show the 'Pin Topic' button"
);
assert.ok(
!exists(".make-banner"),
"it should not show the 'Banner Topic' button"
);
});
});
acceptance("Topic last visit line", function (needs) {
needs.user({ moderator: false, admin: false, trust_level: 1 });
test("visit topic", async function (assert) {
await visit("/t/-/280");
assert.ok(
exists(".topic-post-visited-line.post-10"),
"shows the last visited line on the right post"
);
await visit("/t/-/9");
assert.ok(
!exists(".topic-post-visited-line"),
"does not show last visited line if post is the last post"
);
});
});
acceptance("Topic filter replies to post number", function (needs) {
needs.settings({
enable_filtered_replies_view: true,
});
test("visit topic", async function (assert) {
await visit("/t/-/280");
assert.equal(
query("#post_3 .show-replies").title,
I18n.t("post.filtered_replies_hint", { count: 3 }),
"it displays the right title for filtering by replies"
);
await visit("/");
await visit("/t/-/280?replies_to_post_number=3");
assert.equal(
query("#post_3 .show-replies").title,
I18n.t("post.view_all_posts"),
"it displays the right title when filtered by replies"
);
});
});
acceptance("Navigating between topics", function (needs) {
needs.pretender((server, helper) => {
const topicResponse = cloneJSON(topicFixtures["/t/280/1.json"]);
const firstPost = topicResponse.post_stream.posts[0];
firstPost.cooked += `\n<a class='same-topic-slugless' href='/t/280'>Link 1</a>`;
firstPost.cooked += `\n<a class='same-topic-slugless-post' href='/t/280/3'>Link 2</a>`;
firstPost.cooked += `\n<a class='diff-topic-slugless' href='/t/28830'>Link 3</a>`;
firstPost.cooked += `\n<a class='diff-topic-slugless-post' href='/t/28830/1'>Link 4</a>`;
firstPost.cooked += `\n<a class='by-post-id' href='/p/${firstPost.id}'>Link to Post</a>`;
server.get("/t/280.json", () => helper.response(topicResponse));
server.get("/t/280/:post_number.json", () =>
helper.response(topicResponse)
);
});
test("clicking slug-less URLs within the same topic", async function (assert) {
await visit("/t/-/280");
await click("a.same-topic-slugless");
assert.ok(currentURL().includes("/280"));
await click("a.same-topic-slugless-post");
assert.ok(currentURL().includes("/280"));
});
test("clicking slug-less URLs to a different topic", async function (assert) {
await visit("/t/-/280");
await click("a.diff-topic-slugless");
assert.ok(currentURL().includes("/28830"));
await visit("/t/-/280");
await click("a.diff-topic-slugless-post");
assert.ok(currentURL().includes("/28830"));
});
test("clicking post URLs", async function (assert) {
await visit("/t/-/280");
await click("a.by-post-id");
assert.ok(currentURL().includes("/280"));
});
});
acceptance("Topic stats update automatically", function () {
test("Likes count updates automatically", async function (assert) {
await visit("/t/internationalization-localization/280");
const likesDisplay = query("#post_1 .topic-map .likes .number");
const oldLikes = likesDisplay.textContent;
const likesChangedFixture = {
id: 280,
type: "stats",
like_count: 999,
};
// simulate the topic like_count being changed
await publishToMessageBus("/topic/280", likesChangedFixture);
const newLikes = likesDisplay.textContent;
assert.notEqual(
oldLikes,
newLikes,
"it updates the like count on the topic stats"
);
assert.equal(
newLikes,
likesChangedFixture.like_count,
"it updates the like count with the expected value"
);
});
const postsChangedFixture = {
id: 280,
type: "stats",
posts_count: 999,
last_posted_at: "2022-06-20T21:01:45.844Z",
last_poster: {
id: 1,
username: "test",
name: "Mr. Tester",
avatar_template: "/images/d-logo-sketch-small.png",
},
};
test("Replies count updates automatically", async function (assert) {
await visit("/t/internationalization-localization/280");
const repliesDisplay = query("#post_1 .topic-map .replies .number");
const oldReplies = repliesDisplay.textContent;
// simulate the topic posts_count being changed
await publishToMessageBus("/topic/280", postsChangedFixture);
const newLikes = repliesDisplay.textContent;
assert.notEqual(
oldReplies,
newLikes,
"it updates the replies count on the topic stats"
);
assert.equal(
newLikes,
postsChangedFixture.posts_count - 1, // replies = posts_count - 1
"it updates the replies count with the expected value"
);
});
test("Last replier avatar updates automatically", async function (assert) {
await visit("/t/internationalization-localization/280");
const avatarImg = query("#post_1 .topic-map .last-reply .avatar");
const oldAvatarTitle = avatarImg.title;
const oldAvatarSrc = avatarImg.src;
// simulate the topic posts_count being changed
await publishToMessageBus("/topic/280", postsChangedFixture);
const newAvatarTitle = avatarImg.title;
const newAvatarSrc = avatarImg.src;
assert.notEqual(
oldAvatarTitle,
newAvatarTitle,
"it updates the last poster avatar title on the topic stats"
);
assert.equal(
newAvatarTitle,
postsChangedFixture.last_poster.name,
"it updates the last poster avatar title with the expected value"
);
assert.notEqual(
oldAvatarSrc,
newAvatarSrc,
"it updates the last poster avatar src on the topic stats"
);
assert.equal(
newAvatarSrc,
`${document.location.origin}${postsChangedFixture.last_poster.avatar_template}`,
"it updates the last poster avatar src with the expected value"
);
});
test("Last replied at updates automatically", async function (assert) {
await visit("/t/internationalization-localization/280");
const lastRepliedAtDisplay = query(
"#post_1 .topic-map .last-reply .relative-date"
);
const oldTime = lastRepliedAtDisplay.dataset.time;
// simulate the topic posts_count being changed
await publishToMessageBus("/topic/280", postsChangedFixture);
const newTime = lastRepliedAtDisplay.dataset.time;
assert.notEqual(
oldTime,
newTime,
"it updates the last posted time on the topic stats"
);
assert.equal(
newTime,
new Date(postsChangedFixture.last_posted_at).getTime(),
"it updates the last posted time with the expected value"
);
});
});