mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Merge branch 'master' into mobile
This commit is contained in:
@@ -11,12 +11,15 @@ test('basic bbcode', function() {
|
||||
format("[i]emphasis[/i]", "<span class=\"bbcode-i\">emphasis</span>", "italics text");
|
||||
format("[u]underlined[/u]", "<span class=\"bbcode-u\">underlined</span>", "underlines text");
|
||||
format("[s]strikethrough[/s]", "<span class=\"bbcode-s\">strikethrough</span>", "strikes-through text");
|
||||
format("[code]\nx++\n[/code]", "<pre>\nx++<br/>\n</pre>", "makes code into pre");
|
||||
format("[code]\nx++\ny++\nz++\n[/code]", "<pre>\nx++<br/>\ny++<br/>\nz++<br/>\n</pre>", "makes code into pre");
|
||||
format("[code]\nx++\n[/code]", "<pre>\nx++</pre>", "makes code into pre");
|
||||
format("[code]\nx++\ny++\nz++\n[/code]", "<pre>\nx++\ny++\nz++</pre>", "makes code into pre");
|
||||
format("[spoiler]it's a sled[/spoiler]", "<span class=\"spoiler\">it's a sled</span>", "supports spoiler tags");
|
||||
format("[img]http://eviltrout.com/eviltrout.png[/img]", "<img src=\"http://eviltrout.com/eviltrout.png\"/>", "links images");
|
||||
format("[url]http://bettercallsaul.com[/url]", "<a href=\"http://bettercallsaul.com\">http://bettercallsaul.com</a>", "supports [url] without a title");
|
||||
format("[email]eviltrout@mailinator.com[/email]", "<a href=\"mailto:eviltrout@mailinator.com\">eviltrout@mailinator.com</a>", "supports [email] without a title");
|
||||
format("[b]evil [i]trout[/i][/b]",
|
||||
"<span class=\"bbcode-b\">evil <span class=\"bbcode-i\">trout</span></span>",
|
||||
"allows embedding of tags");
|
||||
});
|
||||
|
||||
test('lists', function() {
|
||||
@@ -28,7 +31,7 @@ test('color', function() {
|
||||
format("[color=#00f]blue[/color]", "<span style=\"color: #00f\">blue</span>", "supports [color=] with a short hex value");
|
||||
format("[color=#ffff00]yellow[/color]", "<span style=\"color: #ffff00\">yellow</span>", "supports [color=] with a long hex value");
|
||||
format("[color=red]red[/color]", "<span style=\"color: red\">red</span>", "supports [color=] with an html color");
|
||||
format("[color=javascript:alert('wat')]noop[/color]", "noop", "it performs a noop on invalid input");
|
||||
format("[color=javascript:alert('wat')]noop[/color]", "<span>noop</span>", "it performs a noop on invalid input");
|
||||
});
|
||||
|
||||
test('tags with arguments', function() {
|
||||
@@ -59,7 +62,6 @@ test("quotes", function() {
|
||||
|
||||
formatQuote("lorem", "[quote=\"eviltrout, post:1, topic:2\"]\nlorem\n[/quote]\n\n", "correctly formats quotes");
|
||||
|
||||
|
||||
formatQuote(" lorem \t ",
|
||||
"[quote=\"eviltrout, post:1, topic:2\"]\nlorem\n[/quote]\n\n",
|
||||
"trims white spaces before & after the quoted contents");
|
||||
@@ -72,6 +74,9 @@ test("quotes", function() {
|
||||
"[quote=\"eviltrout, post:1, topic:2, full:true\"]\n**lorem** ipsum\n[/quote]\n\n",
|
||||
"keeps BBCode formatting");
|
||||
|
||||
formatQuote("this is <not> a bug",
|
||||
"[quote=\"eviltrout, post:1, topic:2\"]\nthis is <not> a bug\n[/quote]\n\n",
|
||||
"it escapes the contents of the quote");
|
||||
});
|
||||
|
||||
test("quote formatting", function() {
|
||||
|
||||
@@ -199,5 +199,6 @@ test("breakUp", function(){
|
||||
equal(b("HeMans"), "He Mans");
|
||||
equal(b("he_man"), "he_ man");
|
||||
equal(b("he11111"), "he 11111");
|
||||
equal(b("HRCBob"), "HRC Bob");
|
||||
|
||||
});
|
||||
|
||||
@@ -17,7 +17,16 @@ var cookedOptions = function(input, opts, expected, text) {
|
||||
|
||||
test("basic cooking", function() {
|
||||
cooked("hello", "<p>hello</p>", "surrounds text with paragraphs");
|
||||
cooked("**evil**", "<p><strong>evil</strong></p>", "it bolds text.");
|
||||
cooked("__bold__", "<p><strong>bold</strong></p>", "it bolds text.");
|
||||
cooked("*trout*", "<p><em>trout</em></p>", "it italicizes text.");
|
||||
cooked("_trout_", "<p><em>trout</em></p>", "it italicizes text.");
|
||||
cooked("***hello***", "<p><strong><em>hello</em></strong></p>", "it can do bold and italics at once.");
|
||||
cooked("word_with_underscores", "<p>word_with_underscores</p>", "it doesn't do intraword italics");
|
||||
cooked("common/_special_font_face.html.erb", "<p>common/_special_font_face.html.erb</p>", "it doesn't intraword with a slash");
|
||||
cooked("hello \\*evil\\*", "<p>hello *evil*</p>", "it supports escaping of asterisks");
|
||||
cooked("hello \\_evil\\_", "<p>hello _evil_</p>", "it supports escaping of italics");
|
||||
cooked("brussel sproutes are *awful*.", "<p>brussel sproutes are <em>awful</em>.</p>", "it doesn't swallow periods.");
|
||||
});
|
||||
|
||||
test("Traditional Line Breaks", function() {
|
||||
@@ -95,6 +104,21 @@ test("Links", function() {
|
||||
"<a href=\"http://www.imdb.com/name/nm2225369\">http://www.imdb.com/name/nm2225369</a></p>",
|
||||
'allows multiple links on one line');
|
||||
|
||||
cooked("* [Evil Trout][1]\n [1]: http://eviltrout.com",
|
||||
"<ul><li><a href=\"http://eviltrout.com\">Evil Trout</a><br></li></ul>",
|
||||
"allows markdown link references in a list");
|
||||
|
||||
});
|
||||
|
||||
test("simple quotes", function() {
|
||||
cooked("> nice!", "<blockquote><p>nice!</p></blockquote>", "it supports simple quotes");
|
||||
cooked(" > nice!", "<blockquote><p>nice!</p></blockquote>", "it allows quotes with preceeding spaces");
|
||||
cooked("> level 1\n> > level 2",
|
||||
"<blockquote><p>level 1</p><blockquote><p>level 2</p></blockquote></blockquote>",
|
||||
"it allows nesting of blockquotes");
|
||||
cooked("> level 1\n> > level 2",
|
||||
"<blockquote><p>level 1</p><blockquote><p>level 2</p></blockquote></blockquote>",
|
||||
"it allows nesting of blockquotes with spaces");
|
||||
});
|
||||
|
||||
test("Quotes", function() {
|
||||
@@ -102,7 +126,7 @@ test("Quotes", function() {
|
||||
cookedOptions("[quote=\"eviltrout, post: 1\"]\na quote\n\nsecond line\n[/quote]",
|
||||
{ topicId: 2 },
|
||||
"<p><aside class=\"quote\" data-post=\"1\"><div class=\"title\"><div class=\"quote-controls\"></div>eviltrout said:</div><blockquote>" +
|
||||
"a quote<br/><br/>second line<br/></blockquote></aside></p>",
|
||||
"a quote<br/><br/>second line</blockquote></aside></p>",
|
||||
"works with multiple lines");
|
||||
|
||||
cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2",
|
||||
@@ -235,6 +259,12 @@ test("Code Blocks", function() {
|
||||
cooked("```ruby\nhello `eviltrout`\n```",
|
||||
"<p><pre><code class=\"ruby\">hello `eviltrout`</code></pre></p>",
|
||||
"it allows code with backticks in it");
|
||||
|
||||
|
||||
cooked("```[quote=\"sam, post:1, topic:9441, full:true\"]This is `<not>` a bug.[/quote]```",
|
||||
"<p><pre><code class=\"lang-auto\">[quote="sam, post:1, topic:9441, full:true"]This is `<not>` a bug.[/quote]</code></pre></p>",
|
||||
"it allows code with backticks in it");
|
||||
|
||||
});
|
||||
|
||||
test("SanitizeHTML", function() {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
var avatarSelector = Em.Object.create({
|
||||
use_uploaded_avatar: false,
|
||||
gravatar_template: "//www.gravatar.com/avatar/c6e17f2ae2a215e87ff9e878a4e63cd9.png?s={size}&r=pg&d=identicon",
|
||||
uploaded_avatar_template: "//cdn.discourse.org/uploads/meta_discourse/avatars/093/607/185cff113e/{size}.jpg"
|
||||
});
|
||||
|
||||
module("Discourse.AvatarSelectorController");
|
||||
|
||||
test("avatarTemplate", function() {
|
||||
var avatarSelectorController = testController(Discourse.AvatarSelectorController);
|
||||
avatarSelectorController.setProperties(avatarSelector);
|
||||
|
||||
equal(avatarSelectorController.get("avatarTemplate"),
|
||||
avatarSelector.get("gravatar_template"),
|
||||
"we are using gravatar by default");
|
||||
|
||||
avatarSelectorController.useUploadedAvatar();
|
||||
|
||||
equal(avatarSelectorController.get("avatarTemplate"),
|
||||
avatarSelector.get("uploaded_avatar_template"),
|
||||
"calling useUploadedAvatar switches to using the uploaded avatar");
|
||||
|
||||
avatarSelectorController.useGravatar();
|
||||
|
||||
equal(avatarSelectorController.get("avatarTemplate"),
|
||||
avatarSelector.get("gravatar_template"),
|
||||
"calling useGravatar switches to using gravatar");
|
||||
});
|
||||
@@ -16,7 +16,7 @@ var buildAdminUser = function(args) {
|
||||
module("Discourse.FlagController canDeleteSpammer");
|
||||
|
||||
test("canDeleteSpammer not staff", function(){
|
||||
var flagController = controllerFor('flag', buildPost());
|
||||
var flagController = testController(Discourse.FlagController, buildPost());
|
||||
this.stub(Discourse.User, 'currentProp').withArgs('staff').returns(false);
|
||||
flagController.set('selected', Discourse.PostActionType.create({name_key: 'spam'}));
|
||||
equal(flagController.get('canDeleteSpammer'), false, 'false if current user is not staff');
|
||||
@@ -29,7 +29,7 @@ var canDeleteSpammer = function(test, postActionType, expected, testName) {
|
||||
|
||||
test("canDeleteSpammer spam not selected", function(){
|
||||
this.stub(Discourse.User, 'currentProp').withArgs('staff').returns(true);
|
||||
this.flagController = controllerFor('flag', buildPost());
|
||||
this.flagController = testController(Discourse.FlagController, buildPost());
|
||||
this.flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: true}));
|
||||
canDeleteSpammer(this, 'off_topic', false, 'false if current user is staff, but selected is off_topic');
|
||||
canDeleteSpammer(this, 'inappropriate', false, 'false if current user is staff, but selected is inappropriate');
|
||||
@@ -39,7 +39,7 @@ test("canDeleteSpammer spam not selected", function(){
|
||||
|
||||
test("canDeleteSpammer spam selected", function(){
|
||||
this.stub(Discourse.User, 'currentProp').withArgs('staff').returns(true);
|
||||
this.flagController = controllerFor('flag', buildPost());
|
||||
this.flagController = testController(Discourse.FlagController, buildPost());
|
||||
|
||||
this.flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: true}));
|
||||
canDeleteSpammer(this, 'spam', true, 'true if current user is staff, selected is spam, posts and user can be deleted');
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
module("Discourse.TopicController");
|
||||
|
||||
var topic = Discourse.Topic.create({
|
||||
title: "Qunit Test Topic",
|
||||
participants: [
|
||||
{id: 1234,
|
||||
post_count: 4,
|
||||
username: "eviltrout"}
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
module("Discourse.TopicController", {
|
||||
setup: function() {
|
||||
this.topicController = controllerFor('topic', topic);
|
||||
}
|
||||
});
|
||||
var buildTopic = function() {
|
||||
return Discourse.Topic.create({
|
||||
title: "Qunit Test Topic",
|
||||
participants: [
|
||||
{id: 1234,
|
||||
post_count: 4,
|
||||
username: "eviltrout"}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
test("editingMode", function() {
|
||||
var topicController = this.topicController;
|
||||
var topic = buildTopic(),
|
||||
topicController = testController(Discourse.TopicController, topic);
|
||||
|
||||
ok(!topicController.get('editingTopic'), "we are not editing by default");
|
||||
|
||||
@@ -32,4 +29,89 @@ test("editingMode", function() {
|
||||
|
||||
topicController.cancelEditingTopic();
|
||||
ok(!topicController.get('editingTopic'), "cancelling edit mode reverts the property value");
|
||||
});
|
||||
});
|
||||
|
||||
test("toggledSelectedPost", function() {
|
||||
var tc = testController(Discourse.TopicController, buildTopic()),
|
||||
post = Discourse.Post.create({id: 123, post_number: 2}),
|
||||
postStream = tc.get('postStream');
|
||||
|
||||
postStream.appendPost(post);
|
||||
postStream.appendPost(Discourse.Post.create({id: 124, post_number: 3}));
|
||||
|
||||
blank(tc.get('selectedPosts'), "there are no selected posts by default");
|
||||
equal(tc.get('selectedPostsCount'), 0, "there is a selected post count of 0");
|
||||
ok(!tc.postSelected(post), "the post is not selected by default");
|
||||
|
||||
tc.toggledSelectedPost(post);
|
||||
present(tc.get('selectedPosts'), "there is a selectedPosts collection");
|
||||
equal(tc.get('selectedPostsCount'), 1, "there is a selected post now");
|
||||
ok(tc.postSelected(post), "the post is now selected");
|
||||
|
||||
tc.toggledSelectedPost(post);
|
||||
ok(!tc.postSelected(post), "the post is no longer selected");
|
||||
|
||||
});
|
||||
|
||||
test("selectAll", function() {
|
||||
var tc = testController(Discourse.TopicController, buildTopic()),
|
||||
post = Discourse.Post.create({id: 123, post_number: 2}),
|
||||
postStream = tc.get('postStream');
|
||||
|
||||
postStream.appendPost(post);
|
||||
|
||||
ok(!tc.postSelected(post), "the post is not selected by default");
|
||||
tc.selectAll();
|
||||
ok(tc.postSelected(post), "the post is now selected");
|
||||
ok(tc.get('allPostsSelected'), "all posts are selected");
|
||||
tc.deselectAll();
|
||||
ok(!tc.postSelected(post), "the post is deselected again");
|
||||
ok(!tc.get('allPostsSelected'), "all posts are not selected");
|
||||
|
||||
});
|
||||
|
||||
test("Automating setting of allPostsSelected", function() {
|
||||
var topic = buildTopic(),
|
||||
tc = testController(Discourse.TopicController, topic),
|
||||
post = Discourse.Post.create({id: 123, post_number: 2}),
|
||||
postStream = tc.get('postStream');
|
||||
|
||||
topic.set('posts_count', 1);
|
||||
postStream.appendPost(post);
|
||||
ok(!tc.get('allPostsSelected'), "all posts are not selected by default");
|
||||
|
||||
tc.toggledSelectedPost(post);
|
||||
ok(tc.get('allPostsSelected'), "all posts are selected if we select the only post");
|
||||
|
||||
tc.toggledSelectedPost(post);
|
||||
ok(!tc.get('allPostsSelected'), "the posts are no longer automatically selected");
|
||||
});
|
||||
|
||||
test("Select Replies when present", function() {
|
||||
var topic = buildTopic(),
|
||||
tc = testController(Discourse.TopicController, topic),
|
||||
p1 = Discourse.Post.create({id: 1, post_number: 1, reply_count: 1}),
|
||||
p2 = Discourse.Post.create({id: 2, post_number: 2}),
|
||||
p3 = Discourse.Post.create({id: 2, post_number: 3, reply_to_post_number: 1}),
|
||||
postStream = tc.get('postStream');
|
||||
|
||||
ok(!tc.postSelected(p3), "replies are not selected by default");
|
||||
tc.toggledSelectedPostReplies(p1);
|
||||
ok(tc.postSelected(p1), "it selects the post");
|
||||
ok(!tc.postSelected(p2), "it doesn't select a post that's not a reply");
|
||||
ok(tc.postSelected(p3), "it selects a post that is a reply");
|
||||
equal(tc.get('selectedPostsCount'), 2, "it has a selected posts count of two");
|
||||
|
||||
// If we deselected the post whose replies are selected...
|
||||
tc.toggledSelectedPost(p1);
|
||||
ok(!tc.postSelected(p1), "it deselects the post");
|
||||
ok(!tc.postSelected(p3), "it deselects the replies too");
|
||||
|
||||
// If we deselect a reply, it should deselect the parent's replies selected attribute. Weird but what else would make sense?
|
||||
tc.toggledSelectedPostReplies(p1);
|
||||
tc.toggledSelectedPost(p3);
|
||||
ok(tc.postSelected(p1), "the post stays selected");
|
||||
ok(!tc.postSelected(p3), "it deselects the replies too");
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ function integration(name) {
|
||||
});
|
||||
}
|
||||
|
||||
function testController(klass, model) {
|
||||
return klass.create({model: model, container: Discourse.__container__});
|
||||
}
|
||||
|
||||
function controllerFor(controller, model) {
|
||||
var controller = Discourse.__container__.lookup('controller:' + controller);
|
||||
if (model) { controller.set('model', model ); }
|
||||
|
||||
@@ -122,6 +122,7 @@ var jsHintOpts = {
|
||||
"console",
|
||||
"alert",
|
||||
"controllerFor",
|
||||
"testController",
|
||||
"containsInstance",
|
||||
"deepEqual",
|
||||
"notEqual",
|
||||
|
||||
Reference in New Issue
Block a user