import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
import { render, settled } from "@ember/test-helpers";
import { cloneJSON, deepMerge } from "discourse-common/lib/object";
import { NOTIFICATION_TYPES } from "discourse/tests/fixtures/concerns/notification-types";
import Notification from "discourse/models/notification";
import UserMenuReviewable from "discourse/models/user-menu-reviewable";
import { hbs } from "ember-cli-htmlbars";
import { withPluginApi } from "discourse/lib/plugin-api";
import UserMenuNotificationItem from "discourse/lib/user-menu/notification-item";
import UserMenuMessageItem from "discourse/lib/user-menu/message-item";
import UserMenuBookmarkItem from "discourse/lib/user-menu/bookmark-item";
import UserMenuReviewableItem from "discourse/lib/user-menu/reviewable-item";
import PrivateMessagesFixture from "discourse/tests/fixtures/private-messages-fixtures";
import I18n from "I18n";
function getNotification(currentUser, siteSettings, site, overrides = {}) {
const notification = Notification.create(
deepMerge(
{
id: 11,
user_id: 1,
notification_type: NOTIFICATION_TYPES.mentioned,
read: false,
high_priority: false,
created_at: "2022-07-01T06:00:32.173Z",
post_number: 113,
topic_id: 449,
fancy_title: "This is fancy title <a>!",
slug: "this-is-fancy-title",
data: {
topic_title: "this is title before it becomes fancy !",
display_username: "osama",
original_post_id: 1,
original_post_type: 1,
original_username: "velesin",
},
},
overrides
)
);
return new UserMenuNotificationItem({
notification,
currentUser,
siteSettings,
site,
});
}
module(
"Integration | Component | user-menu | menu-item | with notification items",
function (hooks) {
setupRenderingTest(hooks);
const template = hbs``;
test("pushes `read` to the classList if the notification is read and `unread` if it isn't", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site)
);
this.item.notification.read = false;
await render(template);
assert.notOk(exists("li.read"));
assert.ok(exists("li.unread"));
this.item.notification.read = true;
await settled();
assert.ok(
exists("li.read"),
"the item re-renders when the read property is updated"
);
assert.notOk(
exists("li.unread"),
"the item re-renders when the read property is updated"
);
});
test("pushes the notification type name to the classList", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site)
);
await render(template);
let item = query("li");
assert.ok(item.classList.contains("mentioned"));
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site, {
notification_type: NOTIFICATION_TYPES.private_message,
})
);
await settled();
assert.ok(
exists("li.private-message"),
"replaces underscores in type name with dashes"
);
});
test("pushes is-warning to the classList if the notification originates from a warning PM", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site, {
is_warning: true,
})
);
await render(template);
assert.ok(exists("li.is-warning"));
});
test("doesn't push is-warning to the classList if the notification doesn't originate from a warning PM", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site)
);
await render(template);
assert.ok(!exists("li.is-warning"));
assert.ok(exists("li"));
});
test("the item's href links to the topic that the notification originates from", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site)
);
await render(template);
const link = query("li a");
assert.ok(link.href.endsWith("/t/this-is-fancy-title/449/113"));
});
test("the item's href links to the group messages if the notification is for a group messages", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site, {
topic_id: null,
post_number: null,
slug: null,
data: {
group_id: 33,
group_name: "grouperss",
username: "ossaama",
},
})
);
await render(template);
const link = query("li a");
assert.ok(link.href.endsWith("/u/ossaama/messages/grouperss"));
});
test("the item's link has a title for accessibility", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site)
);
await render(template);
const link = query("li a");
assert.strictEqual(link.title, I18n.t("notifications.titles.mentioned"));
});
test("has elements for label and description", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site)
);
await render(template);
const label = query("li a .item-label");
const description = query("li a .item-description");
assert.strictEqual(
label.textContent.trim(),
"osama",
"the label's content is the username by default"
);
assert.strictEqual(
description.textContent.trim(),
"This is fancy title !",
"the description defaults to the fancy_title"
);
});
test("the description falls back to topic_title from data if fancy_title is absent", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site, {
fancy_title: null,
})
);
await render(template);
const description = query("li a .item-description");
assert.strictEqual(
description.textContent.trim(),
"this is title before it becomes fancy !",
"topic_title from data is rendered safely"
);
});
test("fancy_title is emoji-unescaped", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site, {
fancy_title: "title with emoji :phone:",
})
);
await render(template);
assert.ok(
exists("li a .item-description img.emoji"),
"emojis are unescaped when fancy_title is used for description"
);
});
test("topic_title from data is emoji-unescaped safely", async function (assert) {
this.set(
"item",
getNotification(this.currentUser, this.siteSettings, this.site, {
fancy_title: null,
data: {
topic_title: "unsafe title with unescaped emoji :phone:",
},
})
);
await render(template);
const description = query("li a .item-description");
assert.strictEqual(
description.textContent.trim(),
"unsafe title with unescaped emoji",
"topic_title is rendered safely"
);
assert.ok(
exists(".item-description img.emoji"),
"emoji is rendered correctly"
);
});
test("various aspects can be customized according to the notification's render director", async function (assert) {
withPluginApi("0.1", (api) => {
api.registerNotificationTypeRenderer(
"linked",
(NotificationTypeBase) => {
return class extends NotificationTypeBase {
get classNames() {
return ["additional", "classes"];
}
get linkHref() {
return "/somewhere/awesome";
}
get linkTitle() {
return "hello world this is unsafe '\"";
}
get icon() {
return "wrench";
}
get label() {
return "notification label 666 ";
}
get description() {
return "notification description 123