From 8eef779d389270d2195397d83b0e6d6b8a9bdfde Mon Sep 17 00:00:00 2001
From: Wojciech Zawistowski <wojciech.zawistowski@gmail.com>
Date: Wed, 27 Nov 2013 21:58:36 +0100
Subject: [PATCH] refactors site map

---
 .../controllers/header_controller.js          |  16 -
 .../site_map_category_controller.js           |   5 +
 .../controllers/site_map_controller.js        |  33 +++
 .../discourse/helpers/application_helpers.js  |  14 -
 .../header-category-info.js.handlebars        |  13 -
 .../discourse/templates/header.js.handlebars  |  44 +--
 .../templates/site_map.js.handlebars          |  25 ++
 .../site_map/_admin_link.js.handlebars        |   1 +
 .../_all_categories_link.js.handlebars        |   1 +
 .../site_map/_category.js.handlebars          |  11 +
 .../site_map/_faq_link.js.handlebars          |   1 +
 .../_flagged_posts_links.js.handlebars        |   4 +
 .../_latest_topics_link.js.handlebars         |   1 +
 .../_mobile_toggle_link.js.handlebars         |   1 +
 .../site_map_category_controller_test.js      |  17 ++
 .../controllers/site_map_controller_test.js   |  83 ++++++
 test/javascripts/templates/site_map_test.js   | 273 ++++++++++++++++++
 17 files changed, 457 insertions(+), 86 deletions(-)
 create mode 100644 app/assets/javascripts/discourse/controllers/site_map_category_controller.js
 create mode 100644 app/assets/javascripts/discourse/controllers/site_map_controller.js
 delete mode 100644 app/assets/javascripts/discourse/templates/components/header-category-info.js.handlebars
 create mode 100644 app/assets/javascripts/discourse/templates/site_map.js.handlebars
 create mode 100644 app/assets/javascripts/discourse/templates/site_map/_admin_link.js.handlebars
 create mode 100644 app/assets/javascripts/discourse/templates/site_map/_all_categories_link.js.handlebars
 create mode 100644 app/assets/javascripts/discourse/templates/site_map/_category.js.handlebars
 create mode 100644 app/assets/javascripts/discourse/templates/site_map/_faq_link.js.handlebars
 create mode 100644 app/assets/javascripts/discourse/templates/site_map/_flagged_posts_links.js.handlebars
 create mode 100644 app/assets/javascripts/discourse/templates/site_map/_latest_topics_link.js.handlebars
 create mode 100644 app/assets/javascripts/discourse/templates/site_map/_mobile_toggle_link.js.handlebars
 create mode 100644 test/javascripts/controllers/site_map_category_controller_test.js
 create mode 100644 test/javascripts/controllers/site_map_controller_test.js
 create mode 100644 test/javascripts/templates/site_map_test.js

diff --git a/app/assets/javascripts/discourse/controllers/header_controller.js b/app/assets/javascripts/discourse/controllers/header_controller.js
index f5a37fe10..54a845de2 100644
--- a/app/assets/javascripts/discourse/controllers/header_controller.js
+++ b/app/assets/javascripts/discourse/controllers/header_controller.js
@@ -11,22 +11,10 @@ Discourse.HeaderController = Discourse.Controller.extend({
   showExtraInfo: null,
   notifications: null,
 
-  categories: function() {
-    return Discourse.Category.list();
-  }.property(),
-
   showFavoriteButton: function() {
     return Discourse.User.current() && !this.get('topic.isPrivateMessage');
   }.property('topic.isPrivateMessage'),
 
-  mobileView: function() {
-    return Discourse.Mobile.mobileView;
-  }.property(),
-
-  showMobileToggle: function() {
-    return Discourse.SiteSettings.enable_mobile_theme;
-  }.property(),
-
   actions: {
     toggleStar: function() {
       var topic = this.get('topic');
@@ -34,10 +22,6 @@ Discourse.HeaderController = Discourse.Controller.extend({
       return false;
     },
 
-    toggleMobileView: function() {
-      Discourse.Mobile.toggleMobileView();
-    },
-
     showNotifications: function(headerView) {
       var self = this;
 
diff --git a/app/assets/javascripts/discourse/controllers/site_map_category_controller.js b/app/assets/javascripts/discourse/controllers/site_map_category_controller.js
new file mode 100644
index 000000000..47cb886f4
--- /dev/null
+++ b/app/assets/javascripts/discourse/controllers/site_map_category_controller.js
@@ -0,0 +1,5 @@
+Discourse.SiteMapCategoryController = Ember.ObjectController.extend({
+  showBadges: function() {
+    return !!Discourse.User.current();
+  }.property().volatile()
+});
diff --git a/app/assets/javascripts/discourse/controllers/site_map_controller.js b/app/assets/javascripts/discourse/controllers/site_map_controller.js
new file mode 100644
index 000000000..f8ca13fc0
--- /dev/null
+++ b/app/assets/javascripts/discourse/controllers/site_map_controller.js
@@ -0,0 +1,33 @@
+Discourse.SiteMapController = Ember.ArrayController.extend(Discourse.HasCurrentUser, {
+  itemController: "siteMapCategory",
+
+  showAdminLinks: function() {
+    return this.get("currentUser.staff");
+  }.property("currentUser.staff"),
+
+  flaggedPostsCount: function() {
+    return this.get("currentUser.site_flagged_posts_count");
+  }.property("currentUser.site_flagged_posts_count"),
+
+  faqUrl: function() {
+    return Discourse.SiteSettings.faq_url ? Discourse.SiteSettings.faq_url : Discourse.getURL('/faq');
+  }.property(),
+
+  showMobileToggle: function() {
+    return Discourse.SiteSettings.enable_mobile_theme;
+  }.property(),
+
+  mobileViewLinkTextKey: function() {
+    return Discourse.Mobile.mobileView ? "desktop_view" : "mobile_view";
+  }.property(),
+
+  categories: function() {
+    return Discourse.Category.list();
+  }.property(),
+
+  actions: {
+    toggleMobileView: function() {
+      Discourse.Mobile.toggleMobileView();
+    }
+  }
+});
diff --git a/app/assets/javascripts/discourse/helpers/application_helpers.js b/app/assets/javascripts/discourse/helpers/application_helpers.js
index adb5cc575..132fbbff3 100644
--- a/app/assets/javascripts/discourse/helpers/application_helpers.js
+++ b/app/assets/javascripts/discourse/helpers/application_helpers.js
@@ -317,17 +317,3 @@ Handlebars.registerHelper('date', function(property, options) {
   }
 
 });
-
-/**
-  Produces a link to the FAQ
-
-  @method faqLink
-  @for Handlebars
-**/
-Handlebars.registerHelper('faqLink', function(property, options) {
-  return new Handlebars.SafeString(
-    "<a href='" +
-    (Discourse.SiteSettings.faq_url.length > 0 ? Discourse.SiteSettings.faq_url : Discourse.getURL('/faq')) +
-    "' class='faq-link'>" + I18n.t('faq') + "</a>"
-  );
-});
diff --git a/app/assets/javascripts/discourse/templates/components/header-category-info.js.handlebars b/app/assets/javascripts/discourse/templates/components/header-category-info.js.handlebars
deleted file mode 100644
index feec6de8f..000000000
--- a/app/assets/javascripts/discourse/templates/components/header-category-info.js.handlebars
+++ /dev/null
@@ -1,13 +0,0 @@
-{{categoryLink category allowUncategorized=true}}
-{{#if currentUser}}
-  {{#with view.category}}
-    {{#if unreadTopics}}
-      <a href={{unbound unreadUrl}} class='badge unread-posts badge-notification' title='{{i18n topic.unread_topics count="unreadTopics"}}'>{{unreadTopics}}</a>
-    {{/if}}
-    {{#if newTopics}}
-      <a href={{unbound newUrl}} class='badge new-posts badge-notification' title='{{i18n topic.new_topics count="newTopics"}}'>{{newTopics}} <i class='icon icon-asterisk'></i></a>
-    {{/if}}
-  {{/with}}
-{{else}}
-<b class="topics-count">{{unbound category.topic_count}}</b>
-{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/header.js.handlebars b/app/assets/javascripts/discourse/templates/header.js.handlebars
index a41630353..506d98d71 100644
--- a/app/assets/javascripts/discourse/templates/header.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/header.js.handlebars
@@ -99,49 +99,7 @@
       {{render notifications notifications}}
 
       {{#if view.renderSiteMap}}
-        <section class='d-dropdown' id='site-map-dropdown'>
-          <ul class="location-links">
-            {{#if currentUser.staff}}
-              <li><a href="/admin" class="admin-link"><i class='icon icon-wrench'></i>{{i18n admin_title}}</a></li>
-              <li>
-                <a href="/admin/flags/active" class="flagged-posts-link"><i class='icon icon-flag'></i>{{i18n flags_title}}</a>
-                {{#if currentUser.site_flagged_posts_count}}
-                  <a href='/admin/flags/active' title='{{i18n notifications.total_flagged}}' class='badge-notification flagged-posts'>{{currentUser.site_flagged_posts_count}}</a>
-                {{/if}}
-              </li>
-            {{/if}}
-            <li>
-              {{#titledLinkTo "list.latest" titleKey="filters.latest.help" class="latest-topics-link"}}{{i18n filters.latest.title}}{{/titledLinkTo}}
-            </li>
-            <li>{{faqLink}}</li>
-            {{#if showMobileToggle}}
-              <li>
-                <a href="#" class="mobile-toggle-link" {{action toggleMobileView}}>
-                  {{#if mobileView}}
-                    {{i18n desktop_view}}
-                  {{else}}
-                    {{i18n mobile_view}}
-                  {{/if}}
-                </a>
-              </li>
-            {{/if}}
-          </ul>
-
-          {{#if categories}}
-            <ul class="category-links">
-              <li class='heading' title="{{i18n filters.categories.help}}">
-                {{#link-to "list.categories"}}{{i18n filters.categories.title}}{{/link-to}}
-              </li>
-
-              {{#each categories}}
-                <li class='category'>
-                  {{header-category-info category=this currentUser=controller.currentUser}}
-                </li>
-              {{/each}}
-            </ul>
-          {{/if}}
-
-        </section>
+        {{render siteMap}}
       {{/if}}
 
     </div>
diff --git a/app/assets/javascripts/discourse/templates/site_map.js.handlebars b/app/assets/javascripts/discourse/templates/site_map.js.handlebars
new file mode 100644
index 000000000..04cf99eab
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/site_map.js.handlebars
@@ -0,0 +1,25 @@
+<section class="d-dropdown" id="site-map-dropdown">
+  <ul class="location-links">
+    {{#if showAdminLinks}}
+      <li>{{partial "siteMap/adminLink"}}</li>
+      <li>{{partial "siteMap/flaggedPostsLinks"}}</li>
+    {{/if}}
+    <li>{{partial "siteMap/latestTopicsLink"}}</li>
+    <li>{{partial "siteMap/faqLink"}}</li>
+    {{#if showMobileToggle}}
+      <li>{{partial "siteMap/mobileToggleLink"}}</li>
+    {{/if}}
+  </ul>
+
+  {{#if categories}}
+    <ul class="category-links">
+      <li class="heading" title="{{i18n filters.categories.help}}">
+        {{partial "siteMap/allCategoriesLink"}}
+      </li>
+
+      {{#each categories itemController=itemController}}
+        <li class="category">{{partial "siteMap/category"}}</li>
+      {{/each}}
+    </ul>
+  {{/if}}
+</section>
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/templates/site_map/_admin_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_admin_link.js.handlebars
new file mode 100644
index 000000000..3dd4760d0
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/site_map/_admin_link.js.handlebars
@@ -0,0 +1 @@
+<a href="/admin" class="admin-link"><i class='icon icon-wrench'></i>{{i18n admin_title}}</a>
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/templates/site_map/_all_categories_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_all_categories_link.js.handlebars
new file mode 100644
index 000000000..237f84204
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/site_map/_all_categories_link.js.handlebars
@@ -0,0 +1 @@
+{{#link-to "list.categories"}}{{i18n filters.categories.title}}{{/link-to}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/templates/site_map/_category.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_category.js.handlebars
new file mode 100644
index 000000000..4ce98c6c0
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/site_map/_category.js.handlebars
@@ -0,0 +1,11 @@
+{{categoryLink this allowUncategorized=true}}
+{{#if showBadges}}
+  {{#if unreadTopics}}
+    <a href={{unbound unreadUrl}} class='badge unread-posts badge-notification' title='{{i18n topic.unread_topics count="unreadTopics"}}'>{{unreadTopics}}</a>
+  {{/if}}
+  {{#if newTopics}}
+    <a href={{unbound newUrl}} class='badge new-posts badge-notification' title='{{i18n topic.new_topics count="newTopics"}}'>{{newTopics}} <i class='icon icon-asterisk'></i></a>
+  {{/if}}
+{{else}}
+  <b class="topics-count">{{unbound topic_count}}</b>
+{{/if}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/templates/site_map/_faq_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_faq_link.js.handlebars
new file mode 100644
index 000000000..be58ebbbb
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/site_map/_faq_link.js.handlebars
@@ -0,0 +1 @@
+<a href="{{unbound faqUrl}}" class="faq-link">{{i18n faq}}</a>
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/templates/site_map/_flagged_posts_links.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_flagged_posts_links.js.handlebars
new file mode 100644
index 000000000..6222ac2e7
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/site_map/_flagged_posts_links.js.handlebars
@@ -0,0 +1,4 @@
+<a href="/admin/flags/active" class="flagged-posts-link"><i class='icon icon-flag'></i>{{i18n flags_title}}</a>
+{{#if flaggedPostsCount}}
+  <a href='/admin/flags/active' title='{{i18n notifications.total_flagged}}' class='badge-notification flagged-posts'>{{flaggedPostsCount}}</a>
+{{/if}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/templates/site_map/_latest_topics_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_latest_topics_link.js.handlebars
new file mode 100644
index 000000000..b99291c32
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/site_map/_latest_topics_link.js.handlebars
@@ -0,0 +1 @@
+{{#titledLinkTo "list.latest" titleKey="filters.latest.help" class="latest-topics-link"}}{{i18n filters.latest.title}}{{/titledLinkTo}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/templates/site_map/_mobile_toggle_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_mobile_toggle_link.js.handlebars
new file mode 100644
index 000000000..f230716bb
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/site_map/_mobile_toggle_link.js.handlebars
@@ -0,0 +1 @@
+<a href="#" class="mobile-toggle-link" {{action toggleMobileView}}>{{boundI18n mobileViewLinkTextKey}}</a>
\ No newline at end of file
diff --git a/test/javascripts/controllers/site_map_category_controller_test.js b/test/javascripts/controllers/site_map_category_controller_test.js
new file mode 100644
index 000000000..63e5e92dc
--- /dev/null
+++ b/test/javascripts/controllers/site_map_category_controller_test.js
@@ -0,0 +1,17 @@
+var controller;
+
+module("Discourse.SiteMapCategoryController", {
+  setup: function() {
+    controller = Discourse.SiteMapCategoryController.create();
+  }
+});
+
+test("showBadges", function() {
+  this.stub(Discourse.User, "current");
+
+  Discourse.User.current.returns(null);
+  ok(!controller.get("showBadges"), "returns false when no user is logged in");
+
+  Discourse.User.current.returns({});
+  ok(controller.get("showBadges"), "returns true when an user is logged in");
+});
diff --git a/test/javascripts/controllers/site_map_controller_test.js b/test/javascripts/controllers/site_map_controller_test.js
new file mode 100644
index 000000000..b8cce189f
--- /dev/null
+++ b/test/javascripts/controllers/site_map_controller_test.js
@@ -0,0 +1,83 @@
+var controller, oldMobileView;
+
+module("Discourse.SiteMapController", {
+  setup: function() {
+    oldMobileView = Discourse.Mobile.mobileView;
+
+    controller = Discourse.SiteMapController.create();
+  },
+
+  teardown: function() {
+    Discourse.Mobile.mobileView = oldMobileView;
+  }
+});
+
+test("itemController", function() {
+  equal(controller.get("itemController"), "siteMapCategory", "defaults to SiteMapCategoryController");
+});
+
+test("showAdminLinks", function() {
+  var currentUserStub = Ember.Object.create();
+  this.stub(Discourse.User, "current").returns(currentUserStub);
+
+  currentUserStub.set("staff", true);
+  equal(controller.get("showAdminLinks"), true, "is true when current user is a staff member");
+
+  currentUserStub.set("staff", false);
+  equal(controller.get("showAdminLinks"), false, "is false when current user is not a staff member");
+});
+
+test("flaggedPostsCount", function() {
+  var currentUserStub = Ember.Object.create();
+  this.stub(Discourse.User, "current").returns(currentUserStub);
+
+  currentUserStub.set("site_flagged_posts_count", 5);
+  equal(controller.get("flaggedPostsCount"), 5, "returns current user's flagged posts count");
+
+  currentUserStub.set("site_flagged_posts_count", 0);
+  equal(controller.get("flaggedPostsCount"), 0, "is bound (reacts to change of current user's flagged posts count)");
+});
+
+test("faqUrl returns faq url configured in site settings if it is set", function() {
+  Discourse.SiteSettings.faq_url = "faq-url";
+  equal(controller.get("faqUrl"), "faq-url");
+});
+
+test("faqUrl returns default '/faq' url when there is no corresponding site setting set", function() {
+  Discourse.SiteSettings.faq_url = null;
+  equal(controller.get("faqUrl"), "/faq");
+});
+
+test("showMoblieToggle returns true when mobile theme is enabled in site settings", function() {
+  Discourse.SiteSettings.enable_mobile_theme = true;
+  equal(controller.get("showMobileToggle"), true);
+});
+
+test("showMoblieToggle returns false when mobile theme is disabled in site settings", function() {
+  Discourse.SiteSettings.enable_mobile_theme = false;
+  equal(controller.get("showMobileToggle"), false);
+});
+
+test("mobileViewLinkTextKey returns translation key for a desktop view if the current view is mobile view", function() {
+  Discourse.Mobile.mobileView = true;
+  equal(controller.get("mobileViewLinkTextKey"), "desktop_view");
+});
+
+test("mobileViewLinkTextKey returns translation key for a mobile view if the current view is desktop view", function() {
+  Discourse.Mobile.mobileView = false;
+  equal(controller.get("mobileViewLinkTextKey"), "mobile_view");
+});
+
+test("categories", function() {
+  var categoryListStub = ["category1", "category2"];
+  this.stub(Discourse.Category, "list").returns(categoryListStub);
+
+  equal(controller.get("categories"), categoryListStub, "returns the list of categories");
+});
+
+test("toggleMobleView", function() {
+  this.stub(Discourse.Mobile, "toggleMobileView");
+
+  controller.send("toggleMobileView");
+  ok(Discourse.Mobile.toggleMobileView.calledOnce, "switches between desktop and mobile views");
+});
diff --git a/test/javascripts/templates/site_map_test.js b/test/javascripts/templates/site_map_test.js
new file mode 100644
index 000000000..8c0f715a0
--- /dev/null
+++ b/test/javascripts/templates/site_map_test.js
@@ -0,0 +1,273 @@
+var controller;
+
+var setUpController = function(properties) {
+  Ember.run(function() {
+    controller.setProperties(properties);
+  });
+};
+
+var appendView = function() {
+  Ember.run(function() {
+    Discourse.advanceReadiness();
+    Ember.View.create({
+      container: Discourse.__container__,
+      controller: controller,
+      templateName: "siteMap"
+    }).appendTo(fixture());
+  });
+};
+
+var locationLinksSelector = ".location-links";
+var categoryLinksSelector = ".category-links";
+
+module("Template: site_map", {
+  setup: function() {
+    sinon.stub(I18n, "t", function(scope, options) {
+      if (options) {
+        if (options.count) {
+          return [scope, options.count].join(" ");
+        } else {
+          return [scope, options.username, options.link].join(" ").trim();
+        }
+      }
+      return scope;
+    });
+
+    controller = Ember.ArrayController.create({
+      container: Discourse.__container__
+    });
+  },
+
+  teardown: function() {
+    I18n.t.restore();
+  }
+});
+
+test("location links part is rendered correctly", function() {
+  setUpController({
+    showAdminLinks: true,
+    flaggedPostsCount: 2,
+    faqUrl: "faq-url",
+    showMobileToggle: true,
+    mobileViewLinkTextKey: "mobile_view_link_text_key"
+  });
+
+  appendView();
+
+  var $locationLinks = fixture(locationLinksSelector);
+
+  var $adminLink = $locationLinks.find(".admin-link");
+  ok(exists($adminLink), "a link to the admin section is present");
+  equal($adminLink.attr("href"), "/admin", "the link to the admin section points to a correct URL");
+  notEqual($adminLink.text().indexOf("admin_title"), -1, "the link to the admin section contains correct text");
+  ok(exists($adminLink.find(".icon-wrench")), "the link to the admin section contains correct icon");
+
+  var $flaggedPostsLink = $locationLinks.find(".flagged-posts-link");
+  ok(exists($flaggedPostsLink), "link to the flagged posts list is present");
+  equal($flaggedPostsLink.attr("href"), "/admin/flags/active", "the link to the flagged posts list points to a correct URL");
+  notEqual($flaggedPostsLink.text().indexOf("flags_title"), -1, "the link to the flagged posts list contains correct text");
+  ok(exists($flaggedPostsLink.find(".icon-flag")), "the link to the flagged posts list contains correct icon");
+
+  var $flaggedPostsBadge = $locationLinks.find(".flagged-posts.badge-notification");
+  ok(exists($flaggedPostsBadge), "a flagged posts badge is present");
+  equal($flaggedPostsBadge.attr("href"), "/admin/flags/active", "the flagged posts badge points to a correct URL");
+  equal($flaggedPostsBadge.attr("title"), "notifications.total_flagged", "the flagged posts badge has correct title attr");
+  equal($flaggedPostsBadge.text(), "2", "the flagged posts badge has correct text");
+
+  var $latestTopicsLink = $locationLinks.find(".latest-topics-link");
+  ok(exists($latestTopicsLink), "the latest topics link is present");
+  equal($latestTopicsLink.attr("href"), "/", "the latest topics link points to a correct URL");
+  equal($latestTopicsLink.attr("title"), "filters.latest.help", "the latest topics link has correct title attr");
+  equal($latestTopicsLink.text(), "filters.latest.title", "the latest topics link has correct text");
+
+  var $faqLink = $locationLinks.find(".faq-link");
+  ok(exists($faqLink), "the FAQ link is present");
+  equal($faqLink.attr("href"), "faq-url", "the FAQ link points to a correct URL");
+  equal($faqLink.text(), "faq", "the FAQ link has correct text");
+
+  var $mobileToggleLink = $locationLinks.find(".mobile-toggle-link");
+  ok(exists($mobileToggleLink), "the mobile theme toggle link is present");
+  equal($mobileToggleLink.text().trim(), "mobile_view_link_text_key", "the mobile theme toggle link has correct text");
+});
+
+test("binds mobile theme toggle link to the correct controller action", function() {
+  this.stub(Ember.Handlebars.helpers, "action", function(actionName) {
+    return new Handlebars.SafeString('data-test-stub-action-name="' + actionName + '"');
+  });
+
+  setUpController({
+    showMobileToggle: true
+  });
+
+  appendView();
+
+  equal(fixture(locationLinksSelector).find(".mobile-toggle-link").data("test-stub-action-name"), "toggleMobileView");
+});
+
+test("does not show flagged posts badge when there are no flagged posts", function() {
+  setUpController({
+    showAdminLinks: true,
+    flaggedPostsCount: 0
+  });
+
+  appendView();
+
+  var $locationLinks = fixture(locationLinksSelector);
+  ok(exists($locationLinks.find(".flagged-posts-link")), "primary link to flagged posts list is still shown");
+  ok(!exists($locationLinks.find(".flagged-posts.badge-notification")), "badge with the number of flagged posts is not shown");
+});
+
+test("does not show any admin links when current user is not a staff member", function() {
+  setUpController({
+    showAdminLinks: false,
+    flaggedPostsCount: 2
+  });
+
+  appendView();
+
+  var $locationLinks = fixture(locationLinksSelector);
+  ok(!exists($locationLinks.find(".admin-link")), "the link to the admin section is not shown");
+  ok(!exists($locationLinks.find(".flagged-posts-link")), "the link to flagged posts list is not shown");
+  ok(!exists($locationLinks.find(".flagged-posts.badge-notification")), "the badge with the number of flagged posts is not shown");
+});
+
+test("does not show mobile theme toggle link if mobile theme is disabled in configuration", function() {
+  setUpController({
+    showMobileToggle: false,
+    mobileViewLinkTextKey: "mobile_view_link_text_key"
+  });
+
+  appendView();
+
+  ok(!exists(fixture(locationLinksSelector).find(".mobile-toggle-link")));
+});
+
+var categoryFixture = {
+  showBadges: true,
+  name: "category name",
+  color: "ffffff",
+  text_color: "000000",
+  slug: "category-slug",
+  topic_count: 123,
+  description: "category description",
+  unreadTopics: 10,
+  newTopics: 20
+};
+
+test("category links part is rendered correctly", function() {
+  setUpController({
+    categories: [
+      Discourse.Category.create(categoryFixture),
+      Discourse.Category.create(categoryFixture)
+    ]
+  });
+
+  appendView();
+
+  var $categoryLinks = fixture(categoryLinksSelector);
+
+  var $heading = $categoryLinks.find(".heading");
+  ok(exists($heading), "a categories list heading exists");
+  equal($heading.attr("title"), "filters.categories.help", "categories list heading has correct title attr");
+
+  var $allCategoriesLink = $heading.find("a");
+  ok(exists($allCategoriesLink), "an 'all categories' link exists");
+  equal($allCategoriesLink.attr("href"), "/categories", "the 'all categories' link points to a correct URL");
+  equal($allCategoriesLink.text(), "filters.categories.title", "the 'all categories' link has correct text");
+
+  var $categories = $categoryLinks.find(".category");
+  equal(count($categories), 2, "the number of categories is correct");
+
+  var $firstCategoryLink = $categories.first().find(".badge-category");
+  ok(exists($firstCategoryLink), "a category item contains a category link");
+  equal($firstCategoryLink.attr("href"), "/category/category-slug", "the category link points to a correct URL");
+  equal($firstCategoryLink.attr("title"), "category description", "the category link has correct title attr");
+  equal($firstCategoryLink.css("color"), "rgb(0, 0, 0)", "the category link has correct color css rule set");
+  equal($firstCategoryLink.css("background-color"), "rgb(255, 255, 255)", "the category link has correct background-color css rule set");
+  equal($firstCategoryLink.text(), "category name", "the category link has correct text");
+
+  var $firstCategoryUnreadTopicsLink = $categories.first().find(".unread-posts");
+  ok(exists($firstCategoryUnreadTopicsLink), "a category item contains current user unread topics link");
+  equal($firstCategoryUnreadTopicsLink.attr("href"), "/category/category-slug/l/unread", "the unread topics link points to a correct URL");
+  ok($firstCategoryUnreadTopicsLink.hasClass("badge") && $firstCategoryUnreadTopicsLink.hasClass("badge-notification"), "the unread topics link has correct classes");
+  equal($firstCategoryUnreadTopicsLink.attr("title"), "topic.unread_topics 10", "the unread topics link has correct title");
+  equal($firstCategoryUnreadTopicsLink.text(), "10", "the unread topics link has correct text");
+
+  var $firstCategoryNewTopicsLink = $categories.first().find(".new-posts");
+  ok(exists($firstCategoryNewTopicsLink), "a category item contains current user new topics link");
+  equal($firstCategoryNewTopicsLink.attr("href"), "/category/category-slug/l/new", "the new topics link points to a correct URL");
+  ok($firstCategoryNewTopicsLink.hasClass("badge") && $firstCategoryNewTopicsLink.hasClass("badge-notification"), "the new topics link has correct classes");
+  equal($firstCategoryNewTopicsLink.attr("title"), "topic.new_topics 20", "the new topics link has correct title");
+  notEqual($firstCategoryNewTopicsLink.text().indexOf("20"), -1, "the new topics link contains correct text");
+  ok(exists($firstCategoryNewTopicsLink.find(".icon-asterisk")), "the new topics link contains correct icon");
+
+  var $firstCategoryAllTopicsCount = $categories.first().find(".topics-count");
+  ok(!exists($firstCategoryAllTopicsCount), "the count of all topics is not shown");
+});
+
+test("categories show the count of all topics instead of new and unread ones when user is not logged in", function() {
+  var categoryWithoutBadgesFixture = _.extend({}, categoryFixture, {
+    showBadges: false
+  });
+
+  setUpController({
+    categories: [
+      Discourse.Category.create(categoryWithoutBadgesFixture)
+    ]
+  });
+
+  appendView();
+
+  var $firstCategory = fixture(categoryLinksSelector).find(".category").first();
+
+  var $allTopicsCountTag = $firstCategory.find(".topics-count");
+  ok(exists($allTopicsCountTag), "the tag with all topics count is shown");
+  equal($allTopicsCountTag.text(), "123", "the tag with all topics count has correct text");
+
+  ok(!exists($firstCategory.find(".unread-posts")), "the unread posts link is not shown");
+  ok(!exists($firstCategory.find(".new-posts")), "the new posts link is not shown");
+});
+
+test("unread topics link is not shown when there are no unread topics", function() {
+  var categoryWithNoUnreadTopicsFixture = _.extend({}, categoryFixture, {
+    unreadTopics: 0
+  });
+
+  setUpController({
+    categories: [
+      Discourse.Category.create(categoryWithNoUnreadTopicsFixture)
+    ]
+  });
+
+  appendView();
+
+  var $firstCategory = fixture(categoryLinksSelector).find(".category").first();
+  ok(!exists($firstCategory.find(".unread-posts")));
+});
+
+test("new topics link are not shown when there are no new topics", function() {
+  var categoryWithNoNewTopicsFixture = _.extend({}, categoryFixture, {
+    newTopics: 0
+  });
+
+  setUpController({
+    categories: [
+      Discourse.Category.create(categoryWithNoNewTopicsFixture)
+    ]
+  });
+
+  appendView();
+
+  var $firstCategory = fixture(categoryLinksSelector).find(".category").first();
+  ok(!exists($firstCategory.find(".new-posts")));
+});
+
+test("the whole categories section is hidden if there are no categories", function() {
+  setUpController({
+    categories: []
+  });
+
+  appendView();
+
+  ok(!exists(fixture(categoryLinksSelector)));
+});