markdown: Fix stream link handler in corner cases.

* Fixes handling of multiple stream links and invalid stream names.
* Fixes text regex so it handle hash sign the right way.
* Adds tests for these stream link cases.
This commit is contained in:
paxapy 2016-11-10 22:20:40 +03:00 committed by Tim Abbott
parent 852bc6b491
commit ade3bda025
3 changed files with 57 additions and 20 deletions

View File

@ -1,6 +1,8 @@
/*global Dict */
var path = require('path');
var fs = require('fs');
var jsdom = require("jsdom");
var window = jsdom.jsdom().defaultView;
global.stub_out_jquery();
@ -30,6 +32,8 @@ add_dependencies({
marked: 'third/marked/lib/marked.js',
emoji: 'js/emoji.js',
people: 'js/people.js',
stream_data: 'js/stream_data.js',
hashchange: 'js/hashchange',
fenced_code: 'js/fenced_code.js'
});
@ -49,6 +53,9 @@ set_global('$', function (obj) {
set_global('feature_flags', {local_echo: true});
jsdom.changeURL(window, 'http://zulip.zulipdev.com');
set_global('window', window);
var people = global.people;
people.add({
@ -57,6 +64,25 @@ people.add({
email: 'cordelia@zulip.com'
});
var stream_data = global.stream_data;
var denmark = {
subscribed: false,
color: 'blue',
name: 'Denmark',
stream_id: 1,
in_home_view: false
};
var social = {
subscribed: true,
color: 'red',
name: 'social',
stream_id: 2,
in_home_view: true,
invite_only: true
};
stream_data.add_sub('Denmark', denmark);
stream_data.add_sub('social', social);
var echo = require('js/echo.js');
var bugdown_data = JSON.parse(fs.readFileSync(path.join(__dirname, '../../zerver/fixtures/bugdown-data.json'), 'utf8', 'r'));
@ -79,6 +105,7 @@ var bugdown_data = JSON.parse(fs.readFileSync(path.join(__dirname, '../../zerver
"User Mention @**leo**",
"User Mention @**leo f**",
"User Mention @**leo with some name**",
"Stream #**Verona**",
"This contains !gravatar(leo@zulip.com)",
"And an avatar !avatar(leo@zulip.com) is here"
];
@ -138,6 +165,12 @@ var bugdown_data = JSON.parse(fs.readFileSync(path.join(__dirname, '../../zerver
expected: '<blockquote>\n<p>quote this for me</p>\n</blockquote>\n<p>thanks</p>'},
{input: 'This is a @**Cordelia Lear** mention',
expected: '<p>This is a <span class="user-mention" data-user-email="cordelia@zulip.com">@Cordelia Lear</span> mention</p>'},
{input: 'This is a #**Denmark** stream link',
expected: '<p>This is a <a class="stream" data-stream-id="1" href="http://zulip.zulipdev.com/#narrow/stream/Denmark">#Denmark</a> stream link</p>'},
{input: 'This is #**Denmark** and #**social** stream links',
expected: '<p>This is <a class="stream" data-stream-id="1" href="http://zulip.zulipdev.com/#narrow/stream/Denmark">#Denmark</a> and <a class="stream" data-stream-id="2" href="http://zulip.zulipdev.com/#narrow/stream/social">#social</a> stream links</p>'},
{input: 'And this is a #**wrong** stream link',
expected: '<p>And this is a #**wrong** stream link</p>'},
{input: 'mmm...:burrito:s',
expected: '<p>mmm...<img alt=\":burrito:\" class=\"emoji\" src=\"/static/third/gemoji/images/emoji/burrito.png\" title=\":burrito:\">s</p>'},
{input: 'This is an :poop: message',

View File

@ -327,6 +327,18 @@ function handleUserMentions(username) {
}
}
function handleStream(streamName) {
var stream = stream_data.get_sub(streamName);
if (stream === undefined) {
return undefined;
}
return '<a class="stream" data-stream-id="' + stream.stream_id + '" ' +
'href="' + window.location.origin + '/#narrow/stream/' +
hashchange.encodeHashComponent(stream.name) + '"' +
'>' + '#' + stream.name + '</a>';
}
function handleRealmFilter(pattern, matches) {
var url = realm_filter_map[pattern];
@ -340,10 +352,6 @@ function handleRealmFilter(pattern, matches) {
return url;
}
function handleStreamLinks(stream_name) {
return '<a href="' + window.location.origin + '#narrow/stream/' + hashchange.encodeHashComponent(stream_name) + '">#' + stream_name + '</a>';
}
function python_to_js_filter(pattern, url) {
// Converts a python named-group regex to a javascript-compatible numbered
// group regex... with a regex!
@ -487,7 +495,7 @@ $(function () {
avatarHandler: handleAvatar,
unicodeEmojiHandler: handleUnicodeEmoji,
userMentionHandler: handleUserMentions,
streamLinkHandler: handleStreamLinks,
streamHandler: handleStream,
realmFilterHandler: handleRealmFilter,
renderer: r,
preprocessors: [preprocess_code_blocks]

View File

@ -477,6 +477,7 @@ var inline = {
emoji: noop,
unicodeemoji: noop,
usermention: noop,
stream: noop,
avatar: noop,
gravatar: noop,
realm_filters: [],
@ -537,13 +538,13 @@ inline.zulip = merge({}, inline.breaks, {
emoji: /^:([A-Za-z0-9_\-\+]+?):/,
unicodeemoji: /^(\ud83c[\udf00-\udfff]|\ud83d[\udc00-\ude4f]|\ud83d[\ude80-\udeff])/,
usermention: /^(@(?:\*\*([^\*]+)?\*\*|(\w+)?))/m, // Match multi-word string between @** ** or match any one-word
streamlink: /^(#\*\*([^\*]+)\*\*)/,
stream: /^#\*\*([^\*]+)\*\*/m,
avatar: /^!avatar\(([^)]+)\)/,
gravatar: /^!gravatar\(([^)]+)\)/,
realm_filters: [],
text: replace(inline.breaks.text)
('|', '|(\ud83c[\udf00-\udfff]|\ud83d[\udc00-\ude4f]|\ud83d[\ude80-\udeff])|')
(']|', '@:]|')
(']|', '#@:]|')
()
});
@ -725,10 +726,11 @@ InlineLexer.prototype.output = function(src) {
out += this.usermention(cap[2] || cap[3], cap[1]);
continue;
}
// streamlink (zulip)
if (cap = this.rules.streamlink.exec(src)) {
// stream (zulip)
if (cap = this.rules.stream.exec(src)) {
src = src.substring(cap[0].length);
out += this.streamlink(cap[2], cap[1]);
out += this.stream(cap[1], cap[0]);
continue;
}
@ -870,22 +872,16 @@ InlineLexer.prototype.usermention = function (username, orig) {
return orig;
};
InlineLexer.prototype.streamlink = function(stream_name, orig) {
if (stream_data.get_sub(stream_name) === undefined) {
InlineLexer.prototype.stream = function (streamName, orig) {
if (typeof this.options.streamHandler !== 'function')
return orig;
}
if (typeof this.options.streamLinkHandler !== 'function')
{
return orig;
}
var handled = this.options.streamLinkHandler(stream_name);
var handled = this.options.streamHandler(streamName);
if (handled !== undefined) {
return handled;
}
return orig;
}
};
/**
* Smartypants Transformations