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/sidebar-plugin-api-test.js
Alan Guo Xiang Tan f71e3c07dd DEV: Experiment plugin api to add custom count to category section link
This commit introduces the experimental `registerUserCategorySectionLinkCountable`
and `refreshUserSidebarCategoriesSectionCounts` plugin APIs that allows
a plugin to register custom countables to category section links on top
of the defaults of unread and new.
2023-01-06 09:51:46 +08:00

748 lines
20 KiB
JavaScript

import { test } from "qunit";
import I18n from "I18n";
import { click, settled, visit } from "@ember/test-helpers";
import {
acceptance,
exists,
query,
queryAll,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import { withPluginApi } from "discourse/lib/plugin-api";
import Site from "discourse/models/site";
import { resetCustomCountables } from "discourse/lib/sidebar/user/categories-section/category-section-link";
import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sidebar";
import { bind } from "discourse-common/utils/decorators";
acceptance("Sidebar - Plugin API", function (needs) {
needs.user();
needs.settings({
navigation_menu: "sidebar",
});
needs.hooks.afterEach(() => {
linkDidInsert = undefined;
linkDestroy = undefined;
sectionDestroy = undefined;
});
let linkDidInsert, linkDestroy, sectionDestroy;
test("Multiple header actions and links", async function (assert) {
withPluginApi("1.3.0", (api) => {
api.addSidebarSection(
(BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
return class extends BaseCustomSidebarSection {
get name() {
return "test-chat-channels";
}
get text() {
return "chat channels text";
}
get actionsIcon() {
return "cog";
}
get actions() {
return [
{
id: "browseChannels",
title: "Browse channels",
action: () => {},
},
{
id: "settings",
title: "Settings",
action: () => {},
},
];
}
@bind
willDestroy() {
sectionDestroy = "section test";
}
get links() {
return [
new (class extends BaseCustomSidebarSectionLink {
get name() {
return "random-channel";
}
get classNames() {
return "my-class-name";
}
get route() {
return "topic";
}
get models() {
return ["some-slug", 1];
}
get title() {
return "random channel title";
}
get text() {
return "random channel text";
}
get prefixType() {
return "icon";
}
get prefixValue() {
return "hashtag";
}
get prefixColor() {
return "FF0000";
}
get prefixBadge() {
return "lock";
}
get suffixType() {
return "icon";
}
get suffixValue() {
return "circle";
}
get suffixCSSClass() {
return "unread";
}
@bind
didInsert() {
linkDidInsert = "link test";
}
@bind
willDestroy() {
linkDestroy = "link test";
}
})(),
new (class extends BaseCustomSidebarSectionLink {
get name() {
return "dev-channel";
}
get route() {
return "discovery.latest";
}
get title() {
return "dev channel title";
}
get text() {
return "dev channel text";
}
get prefixColor() {
return "alert";
}
get prefixType() {
return "text";
}
get prefixValue() {
return "test text";
}
})(),
new (class extends BaseCustomSidebarSectionLink {
get name() {
return "fun-channel";
}
get route() {
return "discovery.latest";
}
get title() {
return "fun channel title";
}
get text() {
return "fun channel text";
}
get prefixType() {
return "image";
}
get prefixValue() {
return "/test.png";
}
get hoverType() {
return "icon";
}
get hoverValue() {
return "times";
}
get hoverAction() {
return () => {};
}
get hoverTitle() {
return "hover button title attribute";
}
})(),
];
}
};
}
);
});
await visit("/");
assert.strictEqual(
linkDidInsert,
"link test",
"calls link didInsert function"
);
assert.strictEqual(
query(
".sidebar-section-test-chat-channels .sidebar-section-header-text"
).textContent.trim(),
"chat channels text",
"displays header with correct text"
);
await click(
".sidebar-section-test-chat-channels .sidebar-section-header-dropdown summary"
);
assert.strictEqual(
queryAll(
".sidebar-section-test-chat-channels .sidebar-section-header-dropdown .select-kit-collection li"
).length,
2,
"displays two actions"
);
const actions = queryAll(
".sidebar-section-test-chat-channels .sidebar-section-header-dropdown .select-kit-collection li"
);
assert.strictEqual(
actions[0].textContent.trim(),
"Browse channels",
"displays first header action with correct text"
);
assert.strictEqual(
actions[1].textContent.trim(),
"Settings",
"displays second header action with correct text"
);
const links = queryAll(
".sidebar-section-test-chat-channels .sidebar-section-link"
);
assert.strictEqual(
links[0].textContent.trim(),
"random channel text",
"displays first link with correct text"
);
assert.ok(
exists(".sidebar-section-link.my-class-name"),
"sets the custom class name for the section link"
);
assert.strictEqual(
links[0].title,
"random channel title",
"displays first link with correct title attribute"
);
assert.ok(
links[0].href.endsWith("/some-slug/1"),
"link has the correct href attribute"
);
assert.strictEqual(
links[0].children[0].style.color,
"rgb(255, 0, 0)",
"has correct prefix color"
);
assert.strictEqual(
links[0].children[0].children[0].classList.contains("d-icon-hashtag"),
true,
"displays prefix icon"
);
assert.strictEqual(
links[0].children[0].children[1].classList.contains("d-icon-lock"),
true,
"displays prefix icon badge"
);
assert.strictEqual(
links[0].children[2].children[0].classList.contains("d-icon-circle"),
true,
"displays suffix icon"
);
assert.strictEqual(
links[1].children[1].textContent.trim(),
"dev channel text",
"displays second link with correct text"
);
assert.strictEqual(
links[1].title,
"dev channel title",
"displays second link with correct title attribute"
);
assert.strictEqual(
links[1].children[0].style.color,
"",
"has no color style when value is invalid"
);
assert.strictEqual(
links[1].children[0].textContent.trim(),
"test text",
"displays prefix text"
);
assert.strictEqual(
links[2].children[1].textContent.trim(),
"fun channel text",
"displays third link with correct text"
);
assert.strictEqual(
links[2].title,
"fun channel title",
"displays third link with correct title attribute"
);
assert.strictEqual(
links[2].children[0].children[0].getAttribute("src"),
"/test.png",
"uses correct prefix image url"
);
assert.strictEqual(
query(".sidebar-section-link-hover button").title,
"hover button title attribute",
"displays hover button with correct title"
);
await click(".btn-sidebar-toggle");
assert.strictEqual(
linkDestroy,
"link test",
"calls link willDestroy function"
);
assert.strictEqual(
sectionDestroy,
"section test",
"calls section willDestroy function"
);
});
test("Single header action and no links", async function (assert) {
withPluginApi("1.3.0", (api) => {
api.addSidebarSection((BaseCustomSidebarSection) => {
return class extends BaseCustomSidebarSection {
get name() {
return "test-chat-channels";
}
get text() {
return "chat channels text";
}
get actionsIcon() {
return "cog";
}
get actions() {
return [
{
id: "browseChannels",
title: "Browse channels",
action: () => {},
},
];
}
get links() {
return [];
}
};
});
});
await visit("/");
assert.strictEqual(
query(
".sidebar-section-test-chat-channels .sidebar-section-header-text"
).textContent.trim(),
"chat channels text",
"displays header with correct text"
);
assert.ok(
exists("button.sidebar-section-header-button"),
"displays single header action button"
);
assert.ok(
!exists(".sidebar-section-test-chat-channels .sidebar-section-content a"),
"displays no links"
);
});
test("API bridge for decorating hamburger-menu widget with footer links", async function (assert) {
withPluginApi("1.3.0", (api) => {
api.decorateWidget("hamburger-menu:footerLinks", () => {
return {
route: "discovery.top",
rawLabel: "my top",
className: "my-custom-top",
};
});
});
await visit("/");
await click(
".sidebar-section-community .sidebar-more-section-links-details-summary"
);
const myCustomTopSectionLink = query(
".sidebar-section-community .sidebar-more-section-links-details-content-secondary .sidebar-section-link-my-custom-top"
);
assert.ok(
myCustomTopSectionLink,
"adds my custom top section link to community section under the secondary section in the More... links drawer"
);
assert.ok(
myCustomTopSectionLink.href.endsWith("/top"),
"sets the right href attribute for the my custom top section link"
);
assert.strictEqual(
myCustomTopSectionLink.textContent.trim(),
"my top",
"displays the right text for my custom top section link"
);
});
test("API bridge for decorating hamburger-menu widget with general links", async function (assert) {
withPluginApi("1.3.0", (api) => {
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
route: "discovery.latest",
label: "filters.latest.title",
};
});
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
route: "discovery.unread",
rawLabel: "my unreads",
};
});
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
route: "discovery.top",
rawLabel: "my top",
className: "my-custom-top",
};
});
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
href: "/c/bug?status=open",
rawLabel: "open bugs",
};
});
api.decorateWidget("hamburger-menu:generalLinks", () => {
return {
href: "/t/internationalization-localization/280",
rawLabel: "my favourite topic",
};
});
});
await visit("/");
const customLatestSectionLink = query(
".sidebar-section-community .sidebar-section-link-latest"
);
assert.ok(
customLatestSectionLink,
"adds custom latest section link to community section"
);
assert.ok(
customLatestSectionLink.href.endsWith("/latest"),
"sets the right href attribute for the custom latest section link"
);
assert.strictEqual(
customLatestSectionLink.textContent.trim(),
I18n.t("filters.latest.title"),
"displays the right text for custom latest section link"
);
await click(
".sidebar-section-community .sidebar-more-section-links-details-summary"
);
const customUnreadSectionLink = query(
".sidebar-section-community .sidebar-section-link-my-unreads"
);
assert.ok(
customUnreadSectionLink,
"adds custom unread section link to community section"
);
assert.ok(
customUnreadSectionLink.href.endsWith("/unread"),
"sets the right href attribute for the custom unread section link"
);
assert.strictEqual(
customUnreadSectionLink.textContent.trim(),
"my unreads",
"displays the right text for custom unread section link"
);
const customTopSectionLInk = query(
".sidebar-section-community .sidebar-section-link-my-custom-top"
);
assert.ok(
customTopSectionLInk,
"adds custom top section link to community section with right link class"
);
const openBugsSectionLink = query(
".sidebar-section-community .sidebar-section-link-open-bugs"
);
assert.ok(
openBugsSectionLink,
"adds custom open bugs section link to community section with right link class"
);
assert.ok(
openBugsSectionLink.href.endsWith("/c/bug?status=open"),
"sets the right href attribute for the custom open bugs section link"
);
// close more links
await click(
".sidebar-section-community .sidebar-more-section-links-details-summary"
);
await visit("/t/internationalization-localization/280");
assert.ok(
exists(
".sidebar-section-community .sidebar-section-link-my-favourite-topic.active"
),
"displays my favourite topic custom section link when current route matches the link's route"
);
await visit("/t/short-topic-with-two-posts/54077");
assert.notOk(
exists(
".sidebar-section-community .sidebar-section-link-my-favourite-topic.active"
),
"does not display my favourite topic custom section link when current route does not match the link's route"
);
});
test("Section that is not displayed via displaySection", async function (assert) {
withPluginApi("1.3.0", (api) => {
api.addSidebarSection((BaseCustomSidebarSection) => {
return class extends BaseCustomSidebarSection {
get name() {
return "test-chat-channels";
}
get text() {
return "chat channels text";
}
get actionsIcon() {
return "cog";
}
get actions() {
return [
{
id: "browseChannels",
title: "Browse channels",
action: () => {},
},
];
}
get links() {
return [];
}
get displaySection() {
return false;
}
};
});
});
await visit("/");
assert.notOk(
exists(".sidebar-section-test-chat-channels"),
"does not display the section"
);
});
test("Registering a custom countable for a section link in the user's sidebar categories section", async function (assert) {
try {
return await withPluginApi("1.6.0", async (api) => {
const categories = Site.current().categories;
const category1 = categories[0];
const category2 = categories[1];
updateCurrentUser({
sidebar_category_ids: [category1.id, category2.id],
});
// User has one unread topic
this.container.lookup("service:topic-tracking-state").loadStates([
{
topic_id: 2,
highest_post_number: 12,
last_read_post_number: 11,
created_at: "2020-02-09T09:40:02.672Z",
category_id: category1.id,
notification_level: 2,
created_in_new_period: false,
treat_as_new_topic_start_date: "2022-05-09T03:17:34.286Z",
},
]);
api.registerUserCategorySectionLinkCountable({
badgeTextFunction: (count) => {
return `some custom ${count}`;
},
route: "discovery.latestCategory",
routeQuery: { status: "open" },
shouldRegister: ({ category }) => {
if (category.name === category1.name) {
return true;
} else if (category.name === category2.name) {
return false;
}
},
refreshCountFunction: ({ category }) => {
return category.topic_count;
},
prioritizeOverDefaults: ({ category }) => {
return category.topic_count > 1000;
},
});
await visit("/");
assert.ok(
exists(
`.sidebar-section-link-${category1.name} .sidebar-section-link-suffix.unread`
),
"the right suffix is displayed when custom countable is active"
);
assert.strictEqual(
query(`.sidebar-section-link-${category1.name}`).pathname,
`/c/${category1.name}/${category1.id}`,
"does not use route configured for custom countable when user has elected not to show any counts in sidebar"
);
assert.notOk(
exists(
`.sidebar-section-link-${category2.name} .sidebar-section-link-suffix.unread`
),
"does not display suffix when custom countable is not registered"
);
updateCurrentUser({
sidebar_list_destination: UNREAD_LIST_DESTINATION,
});
assert.strictEqual(
query(
`.sidebar-section-link-${category1.name} .sidebar-section-link-content-badge`
).innerText.trim(),
I18n.t("sidebar.unread_count", { count: 1 }),
"displays the right badge text in section link when unread is present and custom countable is not prioritised over unread"
);
category1.set("topic_count", 2000);
api.refreshUserSidebarCategoriesSectionCounts();
await settled();
assert.strictEqual(
query(
`.sidebar-section-link-${category1.name} .sidebar-section-link-content-badge`
).innerText.trim(),
`some custom ${category1.topic_count}`,
"displays the right badge text in section link when unread is present but custom countable is prioritised over unread"
);
assert.strictEqual(
query(`.sidebar-section-link-${category1.name}`).pathname,
`/c/${category1.name}/${category1.id}/l/latest`,
"has the right pathname for section link"
);
assert.strictEqual(
query(`.sidebar-section-link-${category1.name}`).search,
"?status=open",
"has the right query params for section link"
);
});
} finally {
resetCustomCountables();
}
});
});