describe("app.helpers.textFormatter", function(){ beforeEach(function(){ this.statusMessage = factory.post(); this.formatter = app.helpers.textFormatter; }) describe("main", function(){ it("calls mentionify, hashtagify, and markdownify", function(){ spyOn(app.helpers.textFormatter, "mentionify") spyOn(app.helpers.textFormatter, "hashtagify") spyOn(app.helpers.textFormatter, "markdownify") app.helpers.textFormatter(this.statusMessage.get("text"), this.statusMessage) expect(app.helpers.textFormatter.mentionify).toHaveBeenCalled() expect(app.helpers.textFormatter.hashtagify).toHaveBeenCalled() expect(app.helpers.textFormatter.markdownify).toHaveBeenCalled() }) // A couple of complex (intergration) test cases here would be rad. }) describe(".markdownify", function(){ // NOTE: for some strange reason, links separated by just a whitespace character // will not be autolinked; thus we join our URLS here with (" and "). // This test will fail if our join is just (" ") -- an edge case that should be addressed. it("autolinks", function(){ var links = [ "http://google.com", "https://joindiaspora.com", "http://www.yahooligans.com", "http://obama.com", "http://japan.co.jp", "www.mygreat-example-website.de", "www.jenseitsderfenster.de", // from issue #3468 "www.google.com" ]; // The join that would make this particular test fail: // // var formattedText = this.formatter.markdownify(links.join(" ")) var formattedText = this.formatter.markdownify(links.join(" and ")); var wrapper = $("
").html(formattedText); _.each(links, function(link) { var linkElement = wrapper.find("a[href*='" + link + "']"); expect(linkElement.text()).toContain(link); expect(linkElement.attr("target")).toContain("_blank"); }) }); context("symbol conversion", function() { beforeEach(function() { this.input_strings = [ "->", "<-", "<->", "(c)", "(r)", "(tm)", "<3" ]; this.output_symbols = [ "→", "←", "↔", "©", "®", "™", "♥" ]; }); it("correctly converts the input strings to their corresponding output symbol", function() { _.each(this.input_strings, function(str, idx) { var text = this.formatter.markdownify(str); expect(text).toContain(this.output_symbols[idx]); }, this); }); it("converts all symbols at once", function() { var text = this.formatter.markdownify(this.input_strings.join(" ")); _.each(this.output_symbols, function(sym) { expect(text).toContain(sym); }); }); }); context("non-ascii url", function() { beforeEach(function() { this.evilUrls = [ "http://www.bürgerentscheid-krankenhäuser.de", // example from issue #2665 "http://bündnis-für-krankenhäuser.de/wp-content/uploads/2011/11/cropped-logohp.jpg", "http://موقع.وزارة-الاتصالات.مصر/", // example from #3082 "http:///scholar.google.com/citations?view_op=top_venues", "http://lyricstranslate.com/en/someone-you-നിന്നെ-പോലൊരാള്‍.html", // example from #3063, "http://de.wikipedia.org/wiki/Liste_der_Abkürzungen_(Netzjargon)" // #3645 ]; this.asciiUrls = [ "http://www.xn--brgerentscheid-krankenhuser-xkc78d.de", "http://xn--bndnis-fr-krankenhuser-i5b27cha.de/wp-content/uploads/2011/11/cropped-logohp.jpg", "http://xn--4gbrim.xn----ymcbaaajlc6dj7bxne2c.xn--wgbh1c/", "http:///scholar.google.com/citations?view_op=top_venues", "http://lyricstranslate.com/en/someone-you-%E0%B4%A8%E0%B4%BF%E0%B4%A8%E0%B5%8D%E0%B4%A8%E0%B5%86-%E0%B4%AA%E0%B5%8B%E0%B4%B2%E0%B5%8A%E0%B4%B0%E0%B4%BE%E0%B4%B3%E0%B5%8D%E2%80%8D.html", "http://de.wikipedia.org/wiki/Liste_der_Abk%C3%BCrzungen_%28Netzjargon%29" ]; }); it("correctly encodes to punycode", function() { _.each(this.evilUrls, function(url, num) { var text = this.formatter.markdownify( "<" + url + ">" ); expect(text).toContain(this.asciiUrls[num]); }, this); }); it("doesn't break link texts", function() { var linkText = "check out this awesome link!"; var text = this.formatter.markdownify( "["+linkText+"]("+this.evilUrls[0]+")" ); expect(text).toContain(this.asciiUrls[0]); expect(text).toContain(linkText); }); it("doesn't break reference style links", function() { var postContent = "blabla blab [my special link][1] bla blabla\n\n[1]: "+this.evilUrls[0]+" and an optional title)"; var text = this.formatter.markdownify(postContent); expect(text).not.toContain(this.evilUrls[0]); expect(text).toContain(this.asciiUrls[0]); }); it("can be used as img src", function() { var postContent = "![logo]("+ this.evilUrls[1] +")"; var niceImg = 'src="'+ this.asciiUrls[1] +'"'; // the "" are from src="" var text = this.formatter.markdownify(postContent); expect(text).toContain(niceImg); }); it("doesn't break linked images", function() { var postContent = "I am linking an image here [![some-alt-text]("+this.evilUrls[1]+")]("+this.evilUrls[3]+")"; var text = this.formatter.markdownify(postContent); var linked_image = 'src="'+this.asciiUrls[1]+'"'; var image_link = 'href="'+this.asciiUrls[3]+'"'; expect(text).toContain(linked_image); expect(text).toContain(image_link); }); }); context("misc breakage and/or other issues with weird urls", function(){ it("doesn't crash Chromium - RUN ME WITH CHROMIUM! (issue #3553)", function() { var text_part = 'Revert "rails admin is conflicting with client side validations: see https://github.com/sferik/rails_admin/issues/985"'; var link_part = 'https://github.com/diaspora/diaspora/commit/61f40fc6bfe6bb859c995023b5a17d22c9b5e6e5'; var content = '['+text_part+']('+link_part+')'; var parsed = this.formatter.markdownify(content); var link = 'href="' + link_part + '"'; var text = '>'+ text_part +'<'; expect(parsed).toContain(link); expect(parsed).toContain(text); }); it("tests a bunch of benchmark urls", function(){ var self = this; $.ajax({ async: false, cache: false, url: '/spec/fixtures/good_urls.txt', success: function(data) { self.url_list = data.split("\n"); } }); _.each(this.url_list, function(url) { // 'comments' if( url.match(/^#/) ) return; // regex.test is stupid, use match and boolean-ify it var result = !!url.match(Diaspora.url_regex); expect(result).toBeTruthy(); if( !result && console && console.log ) { console.log(url); } }); }); // TODO: try to match the 'bad_urls.txt' and have as few matches as possible }); }) describe(".hashtagify", function(){ context("changes hashtags to links", function(){ it("creates links to hashtags", function(){ var formattedText = this.formatter.hashtagify("I love #parties and #rockstars and #unicorns") var wrapper = $("
").html(formattedText); _.each(["parties", "rockstars", "unicorns"], function(tagName){ expect(wrapper.find("a[href='/tags/" + tagName + "']").text()).toContain(tagName) }) }) it("requires hashtags to be preceeded with a space", function(){ var formattedText = this.formatter.hashtagify("I love the#parties") expect(formattedText).not.toContain('/tags/parties') }) // NOTE THIS DIVERGES FROM GRUBER'S ORIGINAL DIALECT OF MARKDOWN. // We had to edit Markdown.Converter.js line 747 // // text = text.replace(/^(\#{1,6})[ \t]+(.+?)[ \t]*\#*\n+/gm, // [ \t]* changed to [ \t]+ // it("doesn't create a header tag if the first word is a hashtag", function(){ var formattedText = this.formatter.hashtagify("#parties, I love") var wrapper = $("
").html(formattedText); expect(wrapper.find("h1").length).toBe(0) expect(wrapper.find("a[href='/tags/parties']").text()).toContain("#parties") }) }) }) describe(".mentionify", function(){ context("changes mention markup to links", function(){ beforeEach(function(){ this.alice = factory.author({ name : "Alice Smith", diaspora_id : "alice@example.com", id : "555" }) this.bob = factory.author({ name : "Bob Grimm", diaspora_id : "bob@example.com", id : "666" }) this.statusMessage.set({text: "hey there @{Alice Smith; alice@example.com} and @{Bob Grimm; bob@example.com}"}) this.statusMessage.set({mentioned_people : [this.alice, this.bob]}) }) it("matches mentions", function(){ var formattedText = this.formatter.mentionify(this.statusMessage.get("text"), this.statusMessage.get("mentioned_people")) var wrapper = $("
").html(formattedText); _.each([this.alice, this.bob], function(person) { expect(wrapper.find("a[href='/people/" + person.guid + "']").text()).toContain(person.name) }) }); it("returns mentions for on posts that haven't been saved yet (framer posts)", function(){ var freshBob = factory.author({ name : "Bob Grimm", handle : "bob@example.com", url : 'googlebot.com', id : "666" }) this.statusMessage.set({'mentioned_people' : [freshBob] }) var formattedText = this.formatter.mentionify(this.statusMessage.get("text"), this.statusMessage.get("mentioned_people")) var wrapper = $("
").html(formattedText); expect(wrapper.find("a[href='googlebot.com']").text()).toContain(freshBob.name) }) it('returns the name of the mention if the mention does not exist in the array', function(){ var text = "hey there @{Chris Smith; chris@example.com}" var formattedText = this.formatter.mentionify(text, []) expect(formattedText.match(/\