From b1d60d7c9a890c664fa80f2abeb7cc5aa828e3d1 Mon Sep 17 00:00:00 2001 From: augier <christophe@c-henry.fr> Date: Mon, 23 Nov 2015 19:58:51 +0100 Subject: [PATCH] Jasmine tests --- .../app/views/publisher/mention_view.js | 104 +++--- .../javascripts/app/views/search_base_view.js | 17 +- app/assets/stylesheets/publisher.scss | 7 +- .../app/views/publisher_mention_view_spec.js | 322 ++++++++++++++++++ .../app/views/search_base_view_spec.js | 183 ++++++++++ .../javascripts/app/views/search_view_spec.js | 95 +++--- 6 files changed, 628 insertions(+), 100 deletions(-) create mode 100644 spec/javascripts/app/views/publisher_mention_view_spec.js create mode 100644 spec/javascripts/app/views/search_base_view_spec.js diff --git a/app/assets/javascripts/app/views/publisher/mention_view.js b/app/assets/javascripts/app/views/publisher/mention_view.js index 270e14161b..4383da759c 100644 --- a/app/assets/javascripts/app/views/publisher/mention_view.js +++ b/app/assets/javascripts/app/views/publisher/mention_view.js @@ -8,8 +8,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({ KEYS: { - BACKSPACE: 8, TAB: 9, RETURN: 13, ESC: 27, LEFT: 37, UP: 38, - RIGHT: 39, DOWN: 40, COMMA: 188, SPACE: 32, HOME: 36, END: 35 + PASTE: 118, BACKSPACE: 8, TAB: 9, RETURN: 13, ESC: 27, LEFT: 37, + UP: 38, RIGHT: 39, DOWN: 40, COMMA: 188, SPACE: 32, HOME: 36, END: 35 }, settings: { @@ -23,33 +23,13 @@ app.views.PublisherMention = app.views.SearchBase.extend({ } }, - utils: { - setCaretPosition: function(domNode, caretPos){ - if(domNode.createTextRange){ - var range = domNode.createTextRange(); - range.move("character", caretPos); - range.select(); - } else{ - if(domNode.selectionStart){ - domNode.focus(); - domNode.setSelectionRange(caretPos, caretPos); - } else{ - domNode.focus(); - } - } - }, - - rtrim: function(string){ - return string.replace(/\s+$/, ""); - } - }, - events: { "keydown #status_message_fake_text": "onInputBoxKeyDown", "keypress #status_message_fake_text": "onInputBoxKeyPress", "input #status_message_fake_text": "onInputBoxInput", "click #status_message_fake_text": "onInputBoxClick", - "blur #status_message_fake_text": "onInputBoxBlur" + "blur #status_message_fake_text": "onInputBoxBlur", + "paste #status_message_fake_text": "onInputBoxPaste" }, initialize: function(){ @@ -66,21 +46,24 @@ app.views.PublisherMention = app.views.SearchBase.extend({ this.elmMentionsOverlay = $(this.settings.templates.mentionsOverlay()); this.elmMentionsOverlay.prependTo(this.elmWrapperBox); + this.bindMentionningEvents(); + this.completeSetup(this.getTypeaheadInput()); + + this.$el.find(".twitter-typeahead").css({position: "absolute", left: "-1px"}); + this.$el.find(".twitter-typeahead .tt-menu").css("margin-top", 0); + }, + + bindMentionningEvents: function(){ var self = this; - this.getSearchInput().on("typeahead:select", function(evt, datum){ + this.getTypeaheadInput().on("typeahead:select", function(evt, datum){ self.processMention(datum); self.resetMentionBox(); self.addToFilteredResults(datum); }); - this.getSearchInput().on("typeahead:render", function(){ + this.getTypeaheadInput().on("typeahead:render", function(){ self.select(self.$(".tt-menu .tt-suggestion").first()); }); - - this.completeSetup(this.getSearchInput()); - - this.$el.find(".twitter-typeahead").css({position: "absolute", left: "-1px"}); - this.$el.find(".twitter-typeahead .tt-menu").css("margin-top", 0); }, clearBuffer: function(){ @@ -110,12 +93,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({ processMention: function(mention){ var currentMessage = this.getInputBoxValue(); - // Using a regex to figure out positions - var regex = new RegExp("\\" + this.settings.triggerChar + this.currentDataQuery, "gi"); - regex.exec(currentMessage); - - var startCaretPosition = regex.lastIndex - this.currentDataQuery.length - 1; - var currentCaretPosition = regex.lastIndex; + var currentCaretPosition = this.getCaretPosition(); + var startCaretPosition = currentCaretPosition - (this.currentDataQuery.length + 1); var start = currentMessage.substr(0, startCaretPosition); var end = currentMessage.substr(currentCaretPosition, currentMessage.length); @@ -135,7 +114,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({ // Set correct focus and selection this.elmInputBox.focus(); - this.utils.setCaretPosition(this.elmInputBox[0], startEndIndex); + this.setCaretPosition(startEndIndex); }, updateValues: function(){ @@ -153,7 +132,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({ var textSyntax = self.settings.templates.mentionItemSyntax(mention); syntaxMessage = syntaxMessage.replace(mentionVal, textSyntax); - var textHighlight = self.settings.templates.mentionItemHighlight({ name: _.escape(mention.name) }); + var textHighlight = self.settings.templates.mentionItemHighlight({name: _.escape(mention.name)}); mentionText = mentionText.replace(mentionVal, textHighlight); }); @@ -174,7 +153,11 @@ app.views.PublisherMention = app.views.SearchBase.extend({ _.each(persons, function(person){ self.addMention(person); self.addToFilteredResults(person); - self.elmInputBox.val(self.mentionChar + person.name); + var text = self.mentionChar + person.name; + if(self.elmInputBox.val().length !== 0){ + text = self.elmInputBox.val() + " " + text; + } + self.elmInputBox.val(text); self.updateValues(); }); }, @@ -210,7 +193,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({ }, onInputBoxKeyPress: function(e){ - if(e.keyCode !== this.KEYS.BACKSPACE){ + // Excluding ctrl+v from key press event in firefox + if(!((e.which === this.KEYS.PASTE && e.ctrlKey) || (e.keyCode === this.KEYS.BACKSPACE))){ var typedValue = String.fromCharCode(e.which || e.keyCode); this.inputBuffer.push(typedValue); } @@ -223,7 +207,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({ var triggerCharIndex = _.lastIndexOf(this.inputBuffer, this.settings.triggerChar); if(triggerCharIndex > -1){ this.currentDataQuery = this.inputBuffer.slice(triggerCharIndex + 1).join(""); - this.currentDataQuery = this.utils.rtrim(this.currentDataQuery); + this.currentDataQuery = this.rtrim(this.currentDataQuery); this.showMentionBox(); } @@ -232,7 +216,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({ onInputBoxKeyDown: function(e){ // This also matches HOME/END on OSX which is CMD+LEFT, CMD+RIGHT if(e.keyCode === this.KEYS.LEFT || e.keyCode === this.KEYS.RIGHT || - e.keyCode === this.KEYS.HOME || e.keyCode === this.KEYS.END){ + e.keyCode === this.KEYS.HOME || e.keyCode === this.KEYS.END){ _.defer(this.clearBuffer); // IE9 doesn't fire the oninput event when backspace or delete is pressed. This causes the highlighting @@ -268,7 +252,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({ case this.KEYS.RETURN: case this.KEYS.TAB: if(this.getSelected().size() === 1){ - this.getSelected().click(); + this.getSelected().click(); return false; } break; @@ -284,6 +268,15 @@ app.views.PublisherMention = app.views.SearchBase.extend({ this.resetMentionBox(); }, + onInputBoxPaste: function(evt){ + var pastedData = evt.originalEvent.clipboardData.getData("text/plain"); + var dataArray = pastedData.split(""); + var self = this; + _.each(dataArray, function(value){ + self.inputBuffer.push(value); + }); + }, + reset: function(){ this.elmInputBox.val(""); this.mentionsCollection.length = 0; @@ -292,13 +285,13 @@ app.views.PublisherMention = app.views.SearchBase.extend({ }, showMentionBox: function(){ - this.getSearchInput().typeahead("val", this.currentDataQuery); - this.getSearchInput().typeahead("open"); + this.getTypeaheadInput().typeahead("val", this.currentDataQuery); + this.getTypeaheadInput().typeahead("open"); }, resetMentionBox: function(){ - this.getSearchInput().typeahead("val", ""); - this.getSearchInput().typeahead("close"); + this.getTypeaheadInput().typeahead("val", ""); + this.getTypeaheadInput().typeahead("close"); }, getInputBoxValue: function(){ @@ -309,7 +302,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({ return this.$el.find(".tt-menu").is(":visible"); }, - getSearchInput: function(){ + getTypeaheadInput: function(){ if(this.$el.find(".typeahead-mention-box").length === 0){ this.elmInputBox.after("<input class='typeahead-mention-box hidden' type='text'/>"); } @@ -318,5 +311,18 @@ app.views.PublisherMention = app.views.SearchBase.extend({ getTextForSubmit: function(){ return this.mentionsCollection.length ? this.elmInputBox.data("messageText") : this.getInputBoxValue(); + }, + + setCaretPosition: function(caretPos){ + this.elmInputBox[0].focus(); + this.elmInputBox[0].setSelectionRange(caretPos, caretPos); + }, + + getCaretPosition: function(){ + return this.elmInputBox[0].selectionStart; + }, + + rtrim: function(string){ + return string.replace(/\s+$/, ""); } }); diff --git a/app/assets/javascripts/app/views/search_base_view.js b/app/assets/javascripts/app/views/search_base_view.js index af79141229..65bf83fcd7 100644 --- a/app/assets/javascripts/app/views/search_base_view.js +++ b/app/assets/javascripts/app/views/search_base_view.js @@ -98,18 +98,19 @@ app.views.SearchBase = app.views.Base.extend({ */ bindSelectionEvents: function(){ var self = this; - var onover = function(evt){ - var isSuggestion = $(evt.target).is(".tt-suggestion"); - var suggestion = isSuggestion ? $(evt.target) : $(evt.target).parent(".tt-suggestion"); - if(suggestion){ + var onover = function(suggestion){ + return function(){ self.select(suggestion); - } + }; }; this.typeaheadElement.on("typeahead:render", function(){ - self.$(".tt-menu *").off("mouseover", onover); - self.$(".tt-menu .tt-suggestion").on("mouseover", onover); - self.$(".tt-menu .tt-suggestion *").on("mouseover", onover); + self.$(".tt-menu *").off("mouseover"); + self.$(".tt-menu .tt-suggestion").each(function(){ + var $suggestion = $(this); + $suggestion.on("mouseover", onover($suggestion)); + $suggestion.find("*").on("mouseover", onover($suggestion)); + }); }); }, diff --git a/app/assets/stylesheets/publisher.scss b/app/assets/stylesheets/publisher.scss index 1a2ddaec13..f5b874445e 100644 --- a/app/assets/stylesheets/publisher.scss +++ b/app/assets/stylesheets/publisher.scss @@ -17,7 +17,12 @@ } .container-fluid{ padding: 0; } - .mentions-autocomplete-list ul { width: 100% !important; } + + .twitter-typeahead { + width: calc(100% + 2px); + + .tt-menu { width: 100%; } + } form { margin: 0; diff --git a/spec/javascripts/app/views/publisher_mention_view_spec.js b/spec/javascripts/app/views/publisher_mention_view_spec.js new file mode 100644 index 0000000000..f104704942 --- /dev/null +++ b/spec/javascripts/app/views/publisher_mention_view_spec.js @@ -0,0 +1,322 @@ +describe("app.views.PublisherMention", function(){ + beforeEach(function(){ + spec.content().html( + "<div id='publisher'>" + + "<textarea id='status_message_fake_text'></textarea>" + + "</div>"); + }); + + describe("initialize", function(){ + beforeEach(function(){ + spyOn(app.views.PublisherMention.prototype, "completeSetup").and.callThrough(); + spyOn(app.views.PublisherMention.prototype, "bindMentionningEvents").and.callThrough(); + this.view = new app.views.PublisherMention({ el: "#publisher" }); + }); + + it("initializes object properties", function(){ + expect(this.view.mentionsCollection).toEqual([]); + expect(this.view.inputBuffer).toEqual([]); + expect(this.view.currentDataQuery).toBe(""); + expect(this.view.mentionChar).toBe("\u200B"); + }); + + it("calls completeSetup", function(){ + expect(app.views.PublisherMention.prototype.completeSetup).toHaveBeenCalledWith(this.view.getTypeaheadInput()); + expect(app.views.PublisherMention.prototype.bindMentionningEvents).toHaveBeenCalled(); + }); + + it("initializes html elements", function(){ + expect(this.view.$(".typeahead-mention-box").length).toBe(1); + expect(this.view.$(".mentions-input-box").length).toBe(1); + expect(this.view.$(".mentions-box").length).toBe(1); + expect(this.view.$(".mentions").length).toBe(1); + }); + }); + + describe("bindMentionningEvents", function(){ + beforeEach(function(){ + spyOn(app.views.PublisherMention.prototype, "processMention"); + spyOn(app.views.PublisherMention.prototype, "resetMentionBox"); + spyOn(app.views.PublisherMention.prototype, "addToFilteredResults"); + this.view = new app.views.PublisherMention({ el: "#publisher" }); + this.view.bloodhound.add([ + {"person": true, "name":"user1", "handle":"user1@pod.tld"}, + {"person": true, "name":"user2", "handle":"user2@pod.tld"} + ]); + }); + + it("highlights the first item when rendering results", function(){ + this.view.getTypeaheadInput().typeahead("val", "user"); + this.view.getTypeaheadInput().typeahead("open"); + expect(this.view.$(".tt-suggestion").first()).toHaveClass("tt-cursor"); + }); + + it("process mention when clicking a result", function(){ + this.view.getTypeaheadInput().typeahead("val", "user"); + this.view.getTypeaheadInput().typeahead("open"); + this.view.$(".tt-suggestion").first().click(); + expect(app.views.PublisherMention.prototype.processMention).toHaveBeenCalled(); + expect(app.views.PublisherMention.prototype.resetMentionBox).toHaveBeenCalled(); + expect(app.views.PublisherMention.prototype.addToFilteredResults).toHaveBeenCalled(); + }); + }); + + describe("updateMentionsCollection", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + }); + + it("removes person from mention collection if not mentionned anymore", function(){ + this.view.mentionsCollection.push({name: "user1"}); + expect(this.view.mentionsCollection.length).toBe(1); + this.view.updateMentionsCollection(); + expect(this.view.mentionsCollection.length).toBe(0); + }); + + it("removes item from mention collection if not a person", function(){ + this.view.mentionsCollection.push({}); + expect(this.view.mentionsCollection.length).toBe(1); + this.view.updateMentionsCollection(); + expect(this.view.mentionsCollection.length).toBe(0); + }); + }); + + describe("addMention", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + }); + + it("add person to mentionned people", function(){ + expect(this.view.mentionsCollection.length).toBe(0); + this.view.addMention({"name":"user1", "handle":"user1@pod.tld"}); + expect(this.view.mentionsCollection.length).toBe(1); + expect(this.view.mentionsCollection[0]).toEqual({ + /* jshint camelcase: false */ + "name":"user1", "handle":"user1@pod.tld", diaspora_id: "user1@pod.tld"}); + /* jshint camelcase: true */ + }); + + it("does not add mention if not a person", function(){ + expect(this.view.mentionsCollection.length).toBe(0); + this.view.addMention(); + expect(this.view.mentionsCollection.length).toBe(0); + this.view.addMention({}); + expect(this.view.mentionsCollection.length).toBe(0); + this.view.addMention({"name": "user1"}); + expect(this.view.mentionsCollection.length).toBe(0); + this.view.addMention({"handle":"user1@pod.tld"}); + expect(this.view.mentionsCollection.length).toBe(0); + }); + }); + + describe("getTypeaheadInput", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + }); + + it("inserts typeahead input if it does not already exist", function(){ + this.view.getTypeaheadInput().remove(); + expect(this.view.$(".typeahead-mention-box").length).toBe(0); + this.view.getTypeaheadInput(); + expect(this.view.$(".typeahead-mention-box").length).toBe(1); + }); + }); + + describe("processMention", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + this.view.elmInputBox.val("@user1 Text before @user1 text after"); + this.view.currentDataQuery = "user1"; + this.view.elmInputBox[0].setSelectionRange(25, 25); + }); + + it("add person to mentionned people", function(){ + spyOn(this.view, "addMention"); + this.view.processMention({"name":"user1", "handle":"user1@pod.tld"}); + expect(this.view.addMention).toHaveBeenCalledWith({"name":"user1", "handle":"user1@pod.tld"}); + }); + + it("cleans buffers", function(){ + spyOn(this.view, "clearBuffer"); + spyOn(this.view, "resetMentionBox"); + this.view.processMention({"name":"user1", "handle":"user1@pod.tld"}); + expect(this.view.clearBuffer).toHaveBeenCalled(); + expect(this.view.resetMentionBox).toHaveBeenCalled(); + expect(this.view.currentDataQuery).toBe(""); + }); + + it("correctly formats the text", function(){ + spyOn(this.view, "updateValues"); + this.view.processMention({"name":"user1", "handle":"user1@pod.tld"}); + expect(this.view.updateValues).toHaveBeenCalled(); + expect(this.view.getInputBoxValue()).toBe("@user1 Text before " + this.view.mentionChar + "user1 text after"); + }); + + it("places the caret at the right position", function(){ + this.view.processMention({"name":"user1WithLongName", "handle":"user1@pod.tld"}); + var expectedCaretPosition = ("@user1 Text before " + this.view.mentionChar + "user1WithLongName").length; + expect(this.view.elmInputBox[0].selectionStart).toBe(expectedCaretPosition); + }); + }); + + describe("updateValues", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + this.view.elmInputBox.val("@user1 Text before " + this.view.mentionChar + "user1\ntext after"); + this.view.mentionsCollection.push({"name":"user1", "handle":"user1@pod.tld"}); + }); + + it("filters mention from future results", function(){ + spyOn(this.view, "clearFilteredResults"); + spyOn(this.view, "addToFilteredResults"); + this.view.updateValues(); + expect(this.view.clearFilteredResults).toHaveBeenCalled(); + expect(this.view.addToFilteredResults).toHaveBeenCalledWith({"name":"user1", "handle":"user1@pod.tld"}); + }); + + it("formats message text data with correct mentionning syntax", function(){ + this.view.updateValues(); + expect(this.view.elmInputBox.data("messageText")).toBe("@user1 Text before @{user1 ; user1@pod.tld}\ntext after"); + }); + + it("formats overlay text to HTML", function(){ + this.view.updateValues(); + expect(this.view.elmMentionsOverlay.find("div > div").html()) + .toBe("@user1 Text before <strong><span>user1</span></strong><br>text after"); + }); + }); + + describe("prefillMention", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + spyOn(this.view, "addMention"); + spyOn(this.view, "addToFilteredResults"); + spyOn(this.view, "updateValues"); + }); + + it("prefills one mention", function(){ + this.view.prefillMention([{"name":"user1", "handle":"user1@pod.tld"}]); + + expect(this.view.addMention).toHaveBeenCalledWith({"name":"user1", "handle":"user1@pod.tld"}); + expect(this.view.addToFilteredResults) + .toHaveBeenCalledWith({"name":"user1", "handle":"user1@pod.tld"}); + expect(this.view.updateValues).toHaveBeenCalled(); + expect(this.view.getInputBoxValue()).toBe(this.view.mentionChar + "user1"); + }); + + it("prefills multiple mentions", function(){ + this.view.prefillMention([ + {"name":"user1", "handle":"user1@pod.tld"}, + {"name":"user2", "handle":"user2@pod.tld"} + ]); + + expect(this.view.addMention).toHaveBeenCalledWith({"name":"user1", "handle":"user1@pod.tld"}); + expect(this.view.addMention).toHaveBeenCalledWith({"name":"user2", "handle":"user2@pod.tld"}); + expect(this.view.addToFilteredResults).toHaveBeenCalledWith({"name":"user1", "handle":"user1@pod.tld"}); + expect(this.view.addToFilteredResults).toHaveBeenCalledWith({"name":"user2", "handle":"user2@pod.tld"}); + expect(this.view.updateValues).toHaveBeenCalled(); + expect(this.view.getInputBoxValue()).toBe(this.view.mentionChar + "user1 " + this.view.mentionChar + "user2"); + }); + }); + + describe("onInputBoxPaste", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + }); + + it("add person to mentionned people", function(){ + var pasteEvent = {originalEvent: {clipboardData: {getData: function(){ + return "Pasted text"; + }}}}; + + this.view.onInputBoxPaste(pasteEvent); + expect(this.view.inputBuffer).toEqual(["P", "a", "s", "t", "e", "d", " ", "t", "e", "x", "t"]); + }); + }); + + describe("reset", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + spyOn(this.view, "clearFilteredResults"); + spyOn(this.view, "updateValues"); + }); + + it("resets the mention box", function(){ + this.view.reset(); + expect(this.view.elmInputBox.val()).toBe(""); + expect(this.view.mentionsCollection.length).toBe(0); + expect(this.view.clearFilteredResults).toHaveBeenCalled(); + expect(this.view.updateValues).toHaveBeenCalled(); + }); + }); + + describe("showMentionBox", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + this.view.bloodhound.add([ + {"person": true, "name":"user1", "handle":"user1@pod.tld"} + ]); + this.view.currentDataQuery = "user1"; + }); + + it("shows the mention box", function(){ + expect(this.view.$(".tt-menu").is(":visible")).toBe(false); + expect(this.view.$(".tt-menu .tt-suggestion").length).toBe(0); + this.view.showMentionBox(); + expect(this.view.$(".tt-menu").is(":visible")).toBe(true); + expect(this.view.$(".tt-menu .tt-suggestion").length).toBe(1); + }); + }); + + describe("resetMentionBox", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + this.view.bloodhound.add([ + {"person": true, "name":"user1", "handle":"user1@pod.tld"} + ]); + }); + + it("resets results and closes mention box", function(){ + this.view.getTypeaheadInput().typeahead("val", "user"); + this.view.getTypeaheadInput().typeahead("open"); + expect(this.view.$(".tt-menu").is(":visible")).toBe(true); + expect(this.view.$(".tt-menu .tt-suggestion").length >= 1).toBe(true); + this.view.resetMentionBox(); + expect(this.view.$(".tt-menu").is(":visible")).toBe(false); + expect(this.view.$(".tt-menu .tt-suggestion").length).toBe(0); + }); + }); + + describe("getInputBoxValue", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + }); + + it("returns trimmed text", function(){ + this.view.elmInputBox.val("Text with trailing spaces "); + expect(this.view.getInputBoxValue()).toBe("Text with trailing spaces"); + }); + }); + + describe("getTextForSubmit", function(){ + beforeEach(function(){ + this.view = new app.views.PublisherMention({ el: "#publisher" }); + this.view.bloodhound.add([ + {"person": true, "name":"user1", "handle":"user1@pod.tld"} + ]); + }); + + it("returns text with mention syntax if someone is mentionned", function(){ + this.view.getTypeaheadInput().typeahead("val", "user"); + this.view.getTypeaheadInput().typeahead("open"); + this.view.$(".tt-suggestion").first().click(); + expect(this.view.getTextForSubmit()).toBe("@{user1 ; user1@pod.tld}"); + }); + + it("returns normal text if nobody is mentionned", function(){ + this.view.elmInputBox.data("messageText", "Bad text"); + this.view.elmInputBox.val("Good text"); + expect(this.view.getTextForSubmit()).toBe("Good text"); + }); + }); +}); diff --git a/spec/javascripts/app/views/search_base_view_spec.js b/spec/javascripts/app/views/search_base_view_spec.js new file mode 100644 index 0000000000..36bd65912b --- /dev/null +++ b/spec/javascripts/app/views/search_base_view_spec.js @@ -0,0 +1,183 @@ +describe("app.views.SearchBase", function() { + beforeEach(function(){ + spec.content().html( + "<form action='/search' id='search_people_form'><input id='q' name='q' type='search'></input></form>" + ); + }); + + describe("completeSetup", function(){ + it("calls setupBloodhound", function(){ + spyOn(app.views.SearchBase.prototype, "setupBloodhound").and.callThrough(); + var view = new app.views.SearchBase({el: "#search_people_form"}); + view.completeSetup(); + expect(app.views.SearchBase.prototype.setupBloodhound).toHaveBeenCalled(); + }); + + it("calls setupTypeahead", function(){ + spyOn(app.views.SearchBase.prototype, "setupTypeahead"); + var view = new app.views.SearchBase({el: "#search_people_form"}); + view.completeSetup(); + expect(app.views.SearchBase.prototype.setupTypeahead).toHaveBeenCalled(); + }); + + it("calls bindSelectionEvents", function(){ + spyOn(app.views.SearchBase.prototype, "bindSelectionEvents"); + var view = new app.views.SearchBase({el: "#search_people_form"}); + view.completeSetup(); + expect(app.views.SearchBase.prototype.bindSelectionEvents).toHaveBeenCalled(); + }); + + it("initializes the results to filter", function(){ + spyOn(app.views.SearchBase.prototype, "bindSelectionEvents"); + var view = new app.views.SearchBase({el: "#search_people_form"}); + view.completeSetup(); + expect(view.resultsTofilter.length).toBe(0); + }); + }); + + describe("setupBloodhound", function(){ + beforeEach(function(){ + this.view = new app.views.SearchBase({el: "#search_people_form"}); + this.syncCallback = function(){}; + this.asyncCallback = function(){}; + }); + + context("when performing a local search with 1 filtered result", function(){ + beforeEach(function(){ + this.view.completeSetup(this.view.$("#q")); + this.view.bloodhound.add([ + {"id":1,"guid":"1","name":"user1","handle":"user1@pod.tld","url":"/people/1"}, + {"id":2,"guid":"2","name":"user2","handle":"user2@pod.tld","url":"/people/2"} + ]); + }); + + it("should not return the filtered result", function(){ + spyOn(this, "syncCallback"); + spyOn(this, "asyncCallback"); + + this.view.bloodhound.customSearch("user", this.syncCallback, this.asyncCallback); + expect(this.syncCallback).toHaveBeenCalledWith([ + {"id":1,"guid":"1","name":"user1","handle":"user1@pod.tld","url":"/people/1"}, + {"id":2,"guid":"2","name":"user2","handle":"user2@pod.tld","url":"/people/2"} + ]); + expect(this.asyncCallback).not.toHaveBeenCalled(); + + this.view.addToFilteredResults({"id":1,"guid":"1","name":"user1","handle":"user1@pod.tld","url":"/people/1"}); + this.view.bloodhound.customSearch("user", this.syncCallback, this.asyncCallback); + expect(this.syncCallback).toHaveBeenCalledWith( + [{"id":2,"guid":"2","name":"user2","handle":"user2@pod.tld","url":"/people/2"}]); + expect(this.asyncCallback).not.toHaveBeenCalled(); + }); + }); + }); + + describe("transformBloodhoundResponse", function() { + beforeEach(function() { + this.view = new app.views.SearchBase({ el: "#search_people_form" }); + }); + + context("with persons", function() { + beforeEach(function() { + this.response = [{name: "Person", handle: "person@pod.tld"},{name: "User", handle: "user@pod.tld"}]; + }); + + it("sets data.person to true", function() { + expect(this.view.transformBloodhoundResponse(this.response)).toEqual([ + {name: "Person", handle: "person@pod.tld", person: true}, + {name: "User", handle: "user@pod.tld", person: true} + ]); + }); + }); + + context("with hashtags", function() { + beforeEach(function() { + this.response = [{name: "#tag"}, {name: "#hashTag"}]; + }); + + it("sets data.hashtag to true and adds the correct URL", function() { + expect(this.view.transformBloodhoundResponse(this.response)).toEqual([ + {name: "#tag", hashtag: true, url: Routes.tag("tag")}, + {name: "#hashTag", hashtag: true, url: Routes.tag("hashTag")} + ]); + }); + }); + }); + + describe("bindSelectionEvents", function(){ + beforeEach(function() { + this.view = new app.views.SearchBase({ el: "#search_people_form" }); + this.view.completeSetup(this.view.$("#q")); + this.view.bloodhound.add([ + {"person": true, "name":"user1", "handle":"user1@pod.tld"}, + {"person": true, "name":"user2", "handle":"user2@pod.tld"} + ]); + }); + + context("bind over events", function(){ + it("binds over event only once", function(){ + this.view.$("#q").trigger("focusin"); + this.view.$("#q").val("user"); + this.view.$("#q").trigger("keypress"); + this.view.$("#q").trigger("input"); + this.view.$("#q").trigger("focus"); + var numBindedEvents = $._data(this.view.$(".tt-menu .tt-suggestion")[0], "events").mouseover.length; + expect(numBindedEvents).toBe(1); + this.view.$("#q").trigger("focusout"); + this.view.$("#q").trigger("focusin"); + this.view.$("#q").val("user"); + this.view.$("#q").trigger("keypress"); + this.view.$("#q").trigger("input"); + this.view.$("#q").trigger("focus"); + numBindedEvents = $._data(this.view.$(".tt-menu .tt-suggestion")[0], "events").mouseover.length; + expect(numBindedEvents).toBe(1); + }); + + it("highlights the result when overing it", function(){ + this.view.$("#q").trigger("focusin"); + this.view.$("#q").val("user"); + this.view.$("#q").trigger("keypress"); + this.view.$("#q").trigger("input"); + this.view.$("#q").trigger("focus"); + this.view.$(".tt-menu .tt-suggestion").first().trigger("mouseover"); + expect(this.view.$(".tt-menu .tt-suggestion").first()).toHaveClass("tt-cursor"); + }); + }); + }); + + describe("addToFilteredResults", function(){ + beforeEach(function() { + this.view = new app.views.SearchBase({ el: "#search_people_form" }); + this.view.completeSetup(this.view.$("#q")); + }); + + context("when item is a person", function(){ + it("add the item to filtered results", function(){ + this.view.addToFilteredResults({handle: "user@pod.tld"}); + expect(this.view.resultsTofilter.length).toBe(1); + }); + }); + + context("when item is not a person", function(){ + it("does not add the item to filtered results", function(){ + this.view.addToFilteredResults({}); + expect(this.view.resultsTofilter.length).toBe(0); + }); + }); + }); + + describe("clearFilteredResults", function(){ + beforeEach(function() { + this.view = new app.views.SearchBase({ el: "#search_people_form" }); + this.view.completeSetup(this.view.$("#q")); + }); + + context("clear filtered results", function(){ + it("clears the filtered results list", function(){ + this.view.addToFilteredResults({handle: "user@pod.tld"}); + expect(this.view.resultsTofilter.length).toBe(1); + this.view.clearFilteredResults(); + expect(this.view.resultsTofilter.length).toBe(0); + }); + }); + }); +}); diff --git a/spec/javascripts/app/views/search_view_spec.js b/spec/javascripts/app/views/search_view_spec.js index 19fc0ccfb2..70525ae3bb 100644 --- a/spec/javascripts/app/views/search_view_spec.js +++ b/spec/javascripts/app/views/search_view_spec.js @@ -1,21 +1,63 @@ -describe("app.views.Search", function() { +describe("app.views.Search", function(){ beforeEach(function(){ spec.content().html( - "<form action='/search' id='search_people_form'><input id='q' name='q' type='search'></input></form>" + "<form action='/search' id='search_people_form'><input id='q' name='q' type='search'/></form>" ); }); - describe("initialize", function() { - it("calls setupBloodhound", function() { - spyOn(app.views.Search.prototype, "setupBloodhound").and.callThrough(); - new app.views.Search({ el: "#search_people_form" }); - expect(app.views.Search.prototype.setupBloodhound).toHaveBeenCalled(); + describe("initialize", function(){ + it("calls completeSetup", function(){ + spyOn(app.views.Search.prototype, "completeSetup").and.callThrough(); + var view = new app.views.Search({el: "#search_people_form"}); + expect(app.views.Search.prototype.completeSetup).toHaveBeenCalledWith(view.getTypeaheadElement()); }); - it("calls setupTypeahead", function() { - spyOn(app.views.Search.prototype, "setupTypeahead"); - new app.views.Search({ el: "#search_people_form" }); - expect(app.views.Search.prototype.setupTypeahead).toHaveBeenCalled(); + it("calls bindMoreSelectionEvents", function(){ + spyOn(app.views.Search.prototype, "bindMoreSelectionEvents").and.callThrough(); + new app.views.Search({el: "#search_people_form"}); + expect(app.views.Search.prototype.bindMoreSelectionEvents).toHaveBeenCalled(); + }); + }); + + describe("bindMoreSelectionEvents", function(){ + beforeEach(function() { + this.view = new app.views.Search({ el: "#search_people_form" }); + this.view.bloodhound.add([ + {"person": true, "name":"user1", "handle":"user1@pod.tld"}, + {"person": true, "name":"user2", "handle":"user2@pod.tld"} + ]); + }); + + context("bind mouseleave event", function(){ + it("binds mouseleave event only once", function(){ + this.view.$("#q").trigger("focusin"); + this.view.$("#q").val("user"); + this.view.$("#q").trigger("keypress"); + this.view.$("#q").trigger("input"); + this.view.$("#q").trigger("focus"); + var numBindedEvents = $._data(this.view.$(".tt-menu")[0], "events").mouseout.length; + expect(numBindedEvents).toBe(1); + this.view.$("#q").trigger("focusout"); + this.view.$("#q").trigger("focusin"); + this.view.$("#q").val("user"); + this.view.$("#q").trigger("keypress"); + this.view.$("#q").trigger("input"); + this.view.$("#q").trigger("focus"); + numBindedEvents = $._data(this.view.$(".tt-menu")[0], "events").mouseout.length; + expect(numBindedEvents).toBe(1); + }); + + it("remove result highlight when leaving results list", function(){ + this.view.$("#q").trigger("focusin"); + this.view.$("#q").val("user"); + this.view.$("#q").trigger("keypress"); + this.view.$("#q").trigger("input"); + this.view.$("#q").trigger("focus"); + this.view.$(".tt-menu .tt-suggestion").first().trigger("mouseover"); + expect(this.view.$(".tt-menu .tt-suggestion").first()).toHaveClass("tt-cursor"); + this.view.$(".tt-menu").first().trigger("mouseleave"); + expect(this.view.$(".tt-menu .tt-cursor").length).toBe(0); + }); }); }); @@ -44,35 +86,4 @@ describe("app.views.Search", function() { }); }); }); - - describe("transformBloodhoundResponse" , function() { - beforeEach(function() { - this.view = new app.views.Search({ el: "#search_people_form" }); - }); - context("with persons", function() { - beforeEach(function() { - this.response = [{name: "Person", handle: "person@pod.tld"},{name: "User", handle: "user@pod.tld"}]; - }); - - it("sets data.person to true", function() { - expect(this.view.transformBloodhoundResponse(this.response)).toEqual([ - {name: "Person", handle: "person@pod.tld", person: true}, - {name: "User", handle: "user@pod.tld", person: true} - ]); - }); - }); - - context("with hashtags", function() { - beforeEach(function() { - this.response = [{name: "#tag"}, {name: "#hashTag"}]; - }); - - it("sets data.hashtag to true and adds the correct URL", function() { - expect(this.view.transformBloodhoundResponse(this.response)).toEqual([ - {name: "#tag", hashtag: true, url: Routes.tag("tag")}, - {name: "#hashTag", hashtag: true, url: Routes.tag("hashTag")} - ]); - }); - }); - }); }); -- GitLab