diff --git a/public/javascripts/stream.js b/public/javascripts/stream.js
index b0bd6e8c8697ebf9f645294c51abeda5f1a6e09c..c45964d2d08892c7dcdd40710129a8359c2f9d19 100644
--- a/public/javascripts/stream.js
+++ b/public/javascripts/stream.js
@@ -1,112 +1,115 @@
 /*   Copyright (c) 2010, Diaspora Inc.  This file is
-*   licensed under the Affero General Public License version 3 or later.  See
-*   the COPYRIGHT file.
-*/
+ *   licensed under the Affero General Public License version 3 or later.  See
+ *   the COPYRIGHT file.
+ */
 
+var Stream = {
+  initialize: function() {
+    var $stream = $(".stream");
+    var $publisher = $("#publisher");
 
-$(document).ready(function(){
-  var $stream = $(".stream");
-  var $publisher = $("#publisher");
+    $stream.not(".show").delegate("a.show_post_comments", "click", Stream.toggleComments);
 
-  // comment toggle action
-  $stream.not(".show").delegate("a.show_post_comments", "click", function(evt) {
+    // comment submit action
+    $stream.delegate("a.comment_submit", "click", function(evt) {
+      $(this).closest("form").children(".comment_box").attr("rows", 1);
+    });
+
+    $stream.delegate("textarea.comment_box", "focus", function(evt) {
+      var commentBox = $(this);
+      commentBox.attr("rows", 2)
+        .closest("form").find(".comment_submit").fadeIn(200);
+    });
+
+    $stream.delegate("textarea.comment_box", "blur", function(evt) {
+      var commentBox = $(this);
+      if (!commentBox.val()) {
+        commentBox.attr("rows", 1)
+          .closest("form").find(".comment_submit").hide();
+      }
+    });
+
+    // reshare button action
+    $stream.delegate(".reshare_button", "click", function(evt) {
+      evt.preventDefault();
+      button = $(this)
+      box = button.siblings(".reshare_box");
+      if (box.length > 0) {
+        button.toggleClass("active");
+        box.toggle();
+      }
+    });
+
+    $stream.delegate("a.video-link", "click", function(evt) {
+      evt.preventDefault();
+
+      var $this = $(this),
+        container = document.createElement("div"),
+        $container = $(container).attr("class", "video-container"),
+        $videoContainer = $this.parent().siblings("div.video-container");
+
+      if ($videoContainer.length > 0) {
+        $videoContainer.slideUp('fast', function () {
+          $videoContainer.detach();
+        });
+        return;
+      }
+
+      if ($("div.video-container").length > 0) {
+        $("div.video-container").slideUp("fast", function() {
+          $(this).detach();
+        });
+      }
+
+      if ($this.data("host") === "youtube.com") {
+        $container.html(
+          '<a href="//www.youtube.com/watch?v=' + $this.data("video-id") + '" target="_blank">Watch this video on Youtube</a><br />' +
+            '<iframe class="youtube-player" type="text/html" src="http://www.youtube.com/embed/' + $this.data("video-id") + '"></iframe>'
+          );
+      } else {
+        $container.html('Invalid videotype <i>' + $this.data("host") + '</i> (ID: ' + $this.data("video-id") + ')');
+      }
+
+      $container.hide()
+        .insertAfter($this.parent())
+        .slideDown('fast');
+
+      $this.click(function() {
+        $container.slideUp('fast', function() {
+          $(this).detach();
+        });
+      });
+    });
+
+    $(".new_status_message").bind('ajax:success', function(data, json, xhr) {
+      json = $.parseJSON(json);
+      WebSocketReceiver.addPostToStream(json['post_id'], json['html']);
+    });
+    $(".new_status_message").bind('ajax:failure', function(data, html, xhr) {
+      alert('failed to post message!');
+    });
+
+    $(".new_comment").live('ajax:success', function(data, json, xhr) {
+      json = $.parseJSON(json);
+      WebSocketReceiver.processComment(json['post_id'], json['comment_id'], json['html'], false);
+    });
+    $(".new_comment").live('ajax:failure', function(data, html, xhr) {
+      alert('failed to post message!');
+    });
+  },
+
+  toggleComments: function(evt) {
     evt.preventDefault();
     var $this = $(this),
       text = $this.html(),
       commentBlock = $this.closest("li").find("ul.comments", ".content"),
       show = (text.indexOf("show") != -1);
 
-    commentBlock.fadeToggle(150, function(){
+    commentBlock.fadeToggle(150, function() {
       commentBlock.toggleClass("hidden");
     });
     $this.html(text.replace((show) ? "show" : "hide", (show) ? "hide" : "show"));
-  });
-
-  // comment submit action
-  $stream.delegate("a.comment_submit", "click", function(evt){
-    $(this).closest("form").children(".comment_box").attr("rows", 1);
-  });
-
-  $stream.delegate("textarea.comment_box", "focus", function(evt){
-    var commentBox = $(this);
-    commentBox.attr("rows", 2)
-              .closest("form").find(".comment_submit").fadeIn(200);
-  });
-
-  $stream.delegate("textarea.comment_box", "blur", function(evt){
-    var commentBox = $(this);
-    if( !commentBox.val() ) {
-      commentBox.attr("rows", 1)
-                .closest("form").find(".comment_submit").hide();
-    }
-  });
-
-  // reshare button action
-  $stream.delegate(".reshare_button", "click", function(evt){
-    evt.preventDefault();
-    button = $(this)
-    box = button.siblings(".reshare_box");
-    if(box.length > 0){
-      button.toggleClass("active");
-      box.toggle();
-    }
-  });
-  
-  $stream.delegate("a.video-link", "click", function(evt) {
-    evt.preventDefault();
-    
-    var $this = $(this),
-      container = document.createElement("div"),
-      $container = $(container).attr("class", "video-container"),
-      $videoContainer = $this.parent().siblings("div.video-container");
+  }
+};
 
-    if($videoContainer.length > 0) {
-      $videoContainer.slideUp('fast', function () {
-        $videoContainer.detach();
-      });
-      return;
-    }
-    
-    if($("div.video-container").length > 0) {
-      $("div.video-container").slideUp("fast", function() { 
-        $(this).detach();
-      });
-    }
-
-    if($this.data("host") === "youtube.com") {
-      $container.html(
-        '<a href="//www.youtube.com/watch?v=' + $this.data("video-id") + '" target="_blank">Watch this video on Youtube</a><br />' +
-        '<iframe class="youtube-player" type="text/html" src="http://www.youtube.com/embed/' + $this.data("video-id")+ '"></iframe>'
-      );
-    } else {
-      $container.html('Invalid videotype <i>'+$this.data("host")+'</i> (ID: '+$this.data("video-id")+')');
-    }
- 
-    $container.hide()
-      .insertAfter($this.parent())
-      .slideDown('fast');
-
-    $this.click(function() {
-      $container.slideUp('fast', function() {
-        $(this).detach();
-      });
-    });
-  });
-
-  $(".new_status_message").bind('ajax:success', function(data, json, xhr){
-    json = $.parseJSON(json); 
-    WebSocketReceiver.addPostToStream(json['post_id'],json['html']);
-  });
-  $(".new_status_message").bind('ajax:failure', function(data, html, xhr){
-    alert('failed to post message!');
-  });
-
-  $(".new_comment").live('ajax:success', function(data, json, xhr){
-    json = $.parseJSON(json); 
-    WebSocketReceiver.processComment(json['post_id'],json['comment_id'],json['html'],false);
-  });
-  $(".new_comment").live('ajax:failure', function(data, html, xhr){
-    alert('failed to post message!');
-  });
-
-});
+$(document).ready(Stream.initialize);
diff --git a/spec/javascripts/stream-spec.js b/spec/javascripts/stream-spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a23aa2a9ef7a9fe771d828d165028c5ee9a0c7bd
--- /dev/null
+++ b/spec/javascripts/stream-spec.js
@@ -0,0 +1,58 @@
+/*   Copyright (c) 2010, Diaspora Inc.  This file is
+ *   licensed under the Affero General Public License version 3 or later.  See
+ *   the COPYRIGHT file.
+ */
+
+describe("Stream", function() {
+
+  describe("initialize", function() {
+    it("attaches a click event to show_post_comments links", function() {
+      spyOn($.fn, "delegate");
+      Stream.initialize();
+      expect($.fn.delegate).toHaveBeenCalledWith(
+        "a.show_post_comments", "click", Stream.toggleComments);
+    });
+  });
+
+  describe("toggleComments", function() {
+
+    beforeEach(function() {
+      $('#jasmine_content').html(
+        '<li class="message" data-guid="4ceef7ba2367bc2e4d0001e9">' +
+          '<div class="content">' +
+            '<div class="info">' +
+              '<a href="#" class="show_post_comments">show comments (0)</a>' +
+            '</div>' +
+            '<ul class="comments hidden" id="4ceef7ba2367bc2e4d0001e9">' +
+              '<li class="comment show">' +
+                '<form accept-charset="UTF-8" action="/comments" class="new_comment" data-remote="true" id="new_comment_on_4ceef7ba2367bc2e4d0001e9" method="post">' +
+                  '<div style="margin:0;padding:0;display:inline">' +
+                    '<p>' +
+                      '<label for="comment_text_on_4ceef7ba2367bc2e4d0001e9">Comment</label>' +
+                      '<textarea class="comment_box" id="comment_text_on_4ceef7ba2367bc2e4d0001e9" name="text" rows="1"></textarea>' +
+                    '</p>' +
+                    '<input id="post_id_on_4ceef7ba2367bc2e4d0001e9" name="post_id" type="hidden" value="4ceef7ba2367bc2e4d0001e9">' +
+                    '<input class="comment_submit button" data-disable-with="Commenting..." id="comment_submit_4ceef7ba2367bc2e4d0001e9"  name="commit" type="submit" value="Comment">' +
+                  '</div>' +
+                '</form>' +
+              '</li>' +
+            '</ul>' +
+          '</div>' +
+        '</li>'
+      );
+    });
+    it("toggles class hidden on the comment block", function () {
+      expect($('ul.comments')).toHaveClass("hidden");
+      $("a.show_post_comments").click();
+      setTimeout(function() {
+        expect($('ul.comments')).not.toHaveClass("hidden");
+      }, 250);
+    });
+    it("changes the text on the show comments link", function() {
+      $("a.show_post_comments").click();
+      setTimeout(function() {
+        expect($("a.show_post_comments").text()).toEqual("hide comments (0)");
+      }, 250);
+    })
+  });
+});
diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml
index d94c9a5ee9ccadcd4605153ec056be1e17d17782..61c9341fee535b3fcdcc87ed43d5df01eb20f997 100644
--- a/spec/javascripts/support/jasmine.yml
+++ b/spec/javascripts/support/jasmine.yml
@@ -19,6 +19,7 @@ src_files:
   - public/javascripts/aspect-edit.js
   - public/javascripts/aspect-contacts.js
   - public/javascripts/web-socket-receiver.js
+  - public/javascripts/stream.js
 
 # stylesheets
 #