+ {{#if content}}
+
+ {{else}}
+ {{i18n notifications.none}}
+ {{/if}}
+
diff --git a/app/assets/javascripts/discourse/views/header_view.js b/app/assets/javascripts/discourse/views/header_view.js
index 6ecc9fea3dd..b2e3e2271a6 100644
--- a/app/assets/javascripts/discourse/views/header_view.js
+++ b/app/assets/javascripts/discourse/views/header_view.js
@@ -48,19 +48,12 @@ Discourse.HeaderView = Discourse.View.extend({
return false;
},
+ showDropdownBySelector: function(selector) {
+ this.showDropdown($(selector));
+ },
+
showNotifications: function() {
-
- var headerView = this;
- Discourse.ajax('/notifications').then(function(result) {
- headerView.set('notifications', result.map(function(n) {
- return Discourse.Notification.create(n);
- }));
-
- // We've seen all the notifications now
- Discourse.User.current().set('unread_notifications', 0);
- headerView.showDropdown($('#user-notifications'));
- });
- return false;
+ this.get("controller").send("showNotifications", this);
},
examineDockHeader: function() {
@@ -106,7 +99,8 @@ Discourse.HeaderView = Discourse.View.extend({
return headerView.showDropdown($(e.currentTarget));
});
this.$('a.unread-private-messages, a.unread-notifications, a[data-notifications]').on('click.notifications', function(e) {
- return headerView.showNotifications(e);
+ headerView.showNotifications(e);
+ return false;
});
$(window).bind('scroll.discourse-dock', function() {
headerView.examineDockHeader();
diff --git a/test/javascripts/controllers/header_controller_test.js b/test/javascripts/controllers/header_controller_test.js
new file mode 100644
index 00000000000..9753012eadc
--- /dev/null
+++ b/test/javascripts/controllers/header_controller_test.js
@@ -0,0 +1,36 @@
+var server;
+
+module("Discourse.HeaderController", {
+ setup: function() {
+ server = sinon.fakeServer.create();
+ },
+
+ teardown: function() {
+ server.restore();
+ }
+});
+
+test("showNotifications action", function() {
+ var controller = Discourse.HeaderController.create();
+ var viewSpy = {
+ showDropdownBySelector: sinon.spy()
+ };
+ Discourse.User.current().set("unread_notifications", 1);
+ server.respondWith("/notifications", [200, { "Content-Type": "application/json" }, '["notification"]']);
+
+
+ Ember.run(function() {
+ controller.send("showNotifications", viewSpy);
+ });
+
+ equal(controller.get("notifications"), null, "notifications are null before data has finished loading");
+ equal(Discourse.User.current().get("unread_notifications"), 1, "current user's unread notifications count is not zeroed before data has finished loading");
+ ok(viewSpy.showDropdownBySelector.notCalled, "dropdown with notifications is not shown before data has finished loading");
+
+
+ server.respond();
+
+ deepEqual(controller.get("notifications"), ["notification"], "notifications are set correctly after data has finished loading");
+ equal(Discourse.User.current().get("unread_notifications"), 0, "current user's unread notifications count is zeroed after data has finished loading");
+ ok(viewSpy.showDropdownBySelector.calledWith("#user-notifications"), "dropdown with notifications is shown after data has finished loading");
+});
diff --git a/test/javascripts/controllers/notification_controller_test.js b/test/javascripts/controllers/notification_controller_test.js
new file mode 100644
index 00000000000..2628a98036a
--- /dev/null
+++ b/test/javascripts/controllers/notification_controller_test.js
@@ -0,0 +1,51 @@
+var controller;
+var notificationFixture = {
+ notification_type: 1, //mentioned
+ post_number: 1,
+ topic_id: 1234,
+ slug: "a-slug",
+ data: {
+ topic_title: "some title",
+ display_username: "velesin"
+ }
+};
+var postUrlStub = "post-url-stub";
+
+module("Discourse.NotificationController", {
+ setup: function() {
+ sinon.stub(Discourse.Utilities, "postUrl").returns(postUrlStub);
+
+ controller = Discourse.NotificationController.create({
+ content: notificationFixture
+ });
+ },
+
+ teardown: function() {
+ Discourse.Utilities.postUrl.restore();
+ }
+});
+
+test("scope property is correct", function() {
+ equal(controller.get("scope"), "notifications.mentioned");
+});
+
+test("username property is correct", function() {
+ equal(controller.get("username"), "velesin");
+});
+
+test("link property returns empty string when there is no topic title", function() {
+ var fixtureWithEmptyTopicTitle = _.extend({}, notificationFixture, {data: {topic_title: ""}});
+ Ember.run(function() {
+ controller.set("content", fixtureWithEmptyTopicTitle);
+ });
+
+ equal(controller.get("link"), "");
+});
+
+test("link property returns correctly built link when there is a topic title", function() {
+ var $link = $(controller.get("link"));
+
+ ok(Discourse.Utilities.postUrl.calledWithExactly("a-slug", 1234, 1), "URL is generated with the correct slug, topic ID and post number");
+ equal($link.attr("href"), postUrlStub, "generated link points to a correct URL");
+ equal($link.text(), "some title", "generated link has correct text");
+});
diff --git a/test/javascripts/controllers/notifications_controller_test.js b/test/javascripts/controllers/notifications_controller_test.js
new file mode 100644
index 00000000000..26ceeb1cdb9
--- /dev/null
+++ b/test/javascripts/controllers/notifications_controller_test.js
@@ -0,0 +1,82 @@
+var controller, view;
+
+var appendView = function() {
+ Ember.run(function() {
+ view.appendTo(fixture());
+ });
+};
+
+var noItemsMessageSelector = "div.none";
+var itemListSelector = "ul";
+var itemSelector = "li";
+
+module("Discourse.NotificationsController", {
+ setup: function() {
+ sinon.stub(I18n, "t", function (scope, options) {
+ options = options || {};
+ return [scope, options.username, options.link].join(" ").trim();
+ });
+
+ controller = Discourse.NotificationsController.create();
+
+ view = Ember.View.create({
+ controller: controller,
+ templateName: "notifications"
+ });
+ },
+
+ teardown: function() {
+ I18n.t.restore();
+ }
+});
+
+test("mixes in HasCurrentUser", function() {
+ ok(Discourse.HasCurrentUser.detect(controller));
+});
+
+test("by default uses NotificationController as its item controller", function() {
+ equal(controller.get("itemController"), "notification");
+});
+
+test("shows proper info when there are no notifications", function() {
+ controller.set("content", null);
+
+ appendView();
+
+ ok(exists(fixture(noItemsMessageSelector)), "special 'no notifications' message is displayed");
+ equal(fixture(noItemsMessageSelector).text(), "notifications.none", "'no notifications' message contains proper internationalized text");
+ equal(count(fixture(itemListSelector)), 0, "a list of notifications is not displayed");
+});
+
+test("displays a list of notifications and a 'more' link when there are notifications", function() {
+ controller.set("itemController", null);
+ controller.set("content", [
+ {
+ read: false,
+ scope: "scope_1",
+ username: "username_1",
+ link: "link_1"
+ },
+ {
+ read: true,
+ scope: "scope_2",
+ username: "username_2",
+ link: "link_2"
+ }
+ ]);
+
+ appendView();
+
+ var items = fixture(itemSelector);
+ equal(count(items), 3, "number of list items is correct");
+
+ equal(items.eq(0).attr("class"), "", "first (unread) item has proper class");
+ equal(items.eq(0).text(), "scope_1 username_1 link_1", "first item has correct content");
+
+ equal(items.eq(1).attr("class"), "read", "second (read) item has proper class");
+ equal(items.eq(1).text(), "scope_2 username_2 link_2", "second item has correct content");
+
+ var moreLink = items.eq(2).find("> a");
+ equal(moreLink.attr("href"), Discourse.User.current().get("path"), "'more' link points to a correct URL");
+ equal(moreLink.text(), "notifications.more …", "'more' link has correct text");
+});
diff --git a/test/javascripts/integration/header_test.js b/test/javascripts/integration/header_test.js
index 215dcab6c62..f7ce4f336ab 100644
--- a/test/javascripts/integration/header_test.js
+++ b/test/javascripts/integration/header_test.js
@@ -144,7 +144,7 @@ test("notifications: content", function() {
equal(notificationsDropdown().find("li").eq(1).attr("class"), "read", "list item for read notification has correct class");
equal(notificationsDropdown().find("li").eq(1).html(), 'notifications.replied velesin