Some refactoring on mdrender
parent
3c4696962f
commit
c7ee910647
|
@ -3,6 +3,7 @@
|
||||||
# Tested on Markdown 2.3.1
|
# Tested on Markdown 2.3.1
|
||||||
#
|
#
|
||||||
# Copyright (c) 2014, Esteban Castro Borsani
|
# Copyright (c) 2014, Esteban Castro Borsani
|
||||||
|
# Copyright (c) 2014, Jesús Espino García
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
@ -171,7 +172,7 @@ class EmojifyPreprocessor(Preprocessor):
|
||||||
|
|
||||||
url = emojis_path + emoji + u'.png'
|
url = emojis_path + emoji + u'.png'
|
||||||
|
|
||||||
return u's)' % {'emoji': emoji, 'url': url}
|
return ''.format(emoji=emoji, url=url)
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if line.strip():
|
if line.strip():
|
|
@ -28,41 +28,44 @@ import re
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from markdown.extensions import Extension
|
from markdown.extensions import Extension
|
||||||
from markdown.preprocessors import Preprocessor
|
from markdown.inlinepatterns import Pattern
|
||||||
|
from markdown.util import etree
|
||||||
|
|
||||||
|
from taiga.users.models import User
|
||||||
|
|
||||||
|
|
||||||
class MentionsExtension(Extension):
|
class MentionsExtension(Extension):
|
||||||
|
|
||||||
def extendMarkdown(self, md, md_globals):
|
def extendMarkdown(self, md, md_globals):
|
||||||
md.registerExtension(self)
|
MENTION_RE = r'(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-]+)'
|
||||||
md.preprocessors.add('emojify',
|
mentionsPattern = MentionsPattern(MENTION_RE)
|
||||||
MentionsPreprocessor(md),
|
mentionsPattern.md = md
|
||||||
'_end')
|
md.inlinePatterns.add('mentions',
|
||||||
|
mentionsPattern,
|
||||||
|
'_begin')
|
||||||
|
|
||||||
|
|
||||||
class MentionsPreprocessor(Preprocessor):
|
class MentionsPattern(Pattern):
|
||||||
|
def handleMatch(self, m):
|
||||||
|
if m.group(2).strip():
|
||||||
|
username = m.group(2)
|
||||||
|
|
||||||
def run(self, lines):
|
try:
|
||||||
new_lines = []
|
user = User.objects.get(username=username)
|
||||||
pattern = re.compile('(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9]+)')
|
except User.DoesNotExist:
|
||||||
|
return "@{}".format(username)
|
||||||
|
|
||||||
def make_mention_link(m):
|
url = "/#/profile/{}".format(username)
|
||||||
name = m.group(1)
|
|
||||||
|
|
||||||
if not User.objects.filter(username=name):
|
link_text = "@{}".format(username)
|
||||||
return "@{name}".format(name=name)
|
|
||||||
|
|
||||||
tpl = ('[@{name}](/#/profile/{name} "@{name}")')
|
|
||||||
return tpl.format(name=name)
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
if line.strip():
|
|
||||||
line = pattern.sub(make_mention_link, line)
|
|
||||||
|
|
||||||
new_lines.append(line)
|
|
||||||
|
|
||||||
return new_lines
|
|
||||||
|
|
||||||
|
a = etree.Element('a')
|
||||||
|
a.text = link_text
|
||||||
|
a.set('href', url)
|
||||||
|
a.set('alt', user.get_full_name())
|
||||||
|
a.set('title', user.get_full_name())
|
||||||
|
a.set('class', "mention")
|
||||||
|
return a
|
||||||
|
return ''
|
||||||
|
|
||||||
def makeExtension(configs=None):
|
def makeExtension(configs=None):
|
||||||
return MentionsExtension(configs=configs)
|
return MentionsExtension(configs=configs)
|
|
@ -0,0 +1,96 @@
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Tested on Markdown 2.3.1
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014, Esteban Castro Borsani
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
|
from markdown.extensions import Extension
|
||||||
|
from markdown.inlinepatterns import Pattern
|
||||||
|
from markdown.util import etree
|
||||||
|
|
||||||
|
from taiga.projects.references.services import get_instance_by_ref
|
||||||
|
|
||||||
|
|
||||||
|
class TaigaReferencesExtension(Extension):
|
||||||
|
def __init__(self, project, *args, **kwargs):
|
||||||
|
self.project = project
|
||||||
|
return super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def extendMarkdown(self, md, md_globals):
|
||||||
|
TAIGA_REFERENCE_RE = r'(?<=^|(?<=[^a-zA-Z0-9-\[]))#(\d+)'
|
||||||
|
referencesPattern = TaigaReferencesPattern(TAIGA_REFERENCE_RE, self.project)
|
||||||
|
referencesPattern.md = md
|
||||||
|
md.inlinePatterns.add('taiga-references',
|
||||||
|
referencesPattern,
|
||||||
|
'_begin')
|
||||||
|
|
||||||
|
class TaigaReferencesPattern(Pattern):
|
||||||
|
def __init__(self, pattern, project):
|
||||||
|
self.project = project
|
||||||
|
super().__init__(pattern)
|
||||||
|
|
||||||
|
def handleMatch(self, m):
|
||||||
|
if m.group(2).strip():
|
||||||
|
obj_ref = m.group(2)
|
||||||
|
|
||||||
|
instance = get_instance_by_ref(self.project.id, obj_ref)
|
||||||
|
if instance is None:
|
||||||
|
return "#{}".format(obj_ref)
|
||||||
|
|
||||||
|
subject = instance.content_object.subject
|
||||||
|
|
||||||
|
if instance.content_type.model == "userstory":
|
||||||
|
obj_section = "user-story"
|
||||||
|
html_classes = "reference user-story"
|
||||||
|
elif instance.content_type.model == "task":
|
||||||
|
obj_section = "tasks"
|
||||||
|
html_classes = "reference task"
|
||||||
|
elif instance.content_type.model == "issue":
|
||||||
|
obj_section = "issues"
|
||||||
|
html_classes = "reference issue"
|
||||||
|
else:
|
||||||
|
return "#{}".format(obj_ref)
|
||||||
|
|
||||||
|
|
||||||
|
url = "/#/project/{}/{}/{}".format(
|
||||||
|
self.project.slug,
|
||||||
|
obj_section,
|
||||||
|
obj_ref
|
||||||
|
)
|
||||||
|
link_text = "#{}".format(obj_ref)
|
||||||
|
|
||||||
|
a = etree.Element('a')
|
||||||
|
a.text = link_text
|
||||||
|
a.set('href', url)
|
||||||
|
a.set('alt', subject)
|
||||||
|
a.set('title', subject)
|
||||||
|
a.set('class', html_classes)
|
||||||
|
return a
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
def makeExtension(configs=None):
|
||||||
|
return TaigaReferencesExtension(configs=configs)
|
|
@ -1,23 +0,0 @@
|
||||||
# Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
||||||
# for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
# BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
from . import autolink
|
|
||||||
from . import automail
|
|
||||||
from . import hidden_hilite
|
|
||||||
from . import semi_sane_lists
|
|
||||||
from . import spaced_link
|
|
||||||
from . import strikethrough
|
|
||||||
from . import wikilinks
|
|
||||||
from . import emojify
|
|
||||||
from . import mentions
|
|
||||||
|
|
||||||
AutolinkExtension = autolink.AutolinkExtension
|
|
||||||
AutomailExtension = automail.AutomailExtension
|
|
||||||
HiddenHiliteExtension = hidden_hilite.HiddenHiliteExtension
|
|
||||||
SemiSaneListExtension = semi_sane_lists.SemiSaneListExtension
|
|
||||||
SpacedLinkExtension = spaced_link.SpacedLinkExtension
|
|
||||||
StrikethroughExtension = strikethrough.StrikethroughExtension
|
|
||||||
WikiLinkExtension = wikilinks.WikiLinkExtension
|
|
||||||
EmojifyExtension = emojify.EmojifyExtension
|
|
||||||
MentionsExtension = mentions.MentionsExtension
|
|
|
@ -1,74 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright (c) 2012, lepture.com
|
|
||||||
# Copyright (c) 2014, taiga.io
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions
|
|
||||||
# are met:
|
|
||||||
#
|
|
||||||
# * Redistributions of source code must retain the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
# * Redistributions in binary form must reproduce the above
|
|
||||||
# copyright notice, this list of conditions and the following
|
|
||||||
# disclaimer in the documentation and/or other materials provided
|
|
||||||
# with the distribution.
|
|
||||||
# * Neither the name of the author nor the names of its contributors
|
|
||||||
# may be used to endorse or promote products derived from this
|
|
||||||
# software without specific prior written permission.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from taiga.projects.userstories.models import UserStory
|
|
||||||
from taiga.projects.issues.models import Issue
|
|
||||||
from taiga.projects.tasks.models import Task
|
|
||||||
|
|
||||||
|
|
||||||
def references(project, text):
|
|
||||||
pattern = re.compile('(?<=^|(?<=[^a-zA-Z0-9-]))#(us|issue|task)(\d+)')
|
|
||||||
|
|
||||||
def make_reference_link(m):
|
|
||||||
obj_type = m.group(1)
|
|
||||||
obj_ref = m.group(2)
|
|
||||||
|
|
||||||
if obj_type == "us":
|
|
||||||
model = UserStory
|
|
||||||
obj_section = "user-story"
|
|
||||||
elif obj_type == "issue":
|
|
||||||
model = Issue
|
|
||||||
obj_section = "issues"
|
|
||||||
elif obj_type == "task":
|
|
||||||
model = Task
|
|
||||||
obj_section = "tasks"
|
|
||||||
|
|
||||||
instances = model.objects.filter(project_id=project.id, ref=obj_ref)
|
|
||||||
if not instances:
|
|
||||||
return "#{type}{ref}".format(type=obj_type, ref=obj_ref)
|
|
||||||
|
|
||||||
subject = instances[0].subject
|
|
||||||
|
|
||||||
return '[#{type}{ref}](/#/project/{project_slug}/{section}/{ref} "{subject}")'.format(
|
|
||||||
type=obj_type,
|
|
||||||
section=obj_section,
|
|
||||||
ref=obj_ref,
|
|
||||||
project_slug=project.slug,
|
|
||||||
subject=subject
|
|
||||||
)
|
|
||||||
|
|
||||||
text = pattern.sub(make_reference_link, text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
__all__ = ['references']
|
|
|
@ -7,20 +7,19 @@ from django.utils.encoding import force_bytes
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
from fn import F
|
from fn import F
|
||||||
|
|
||||||
from .gfm import AutolinkExtension
|
from .extensions.autolink import AutolinkExtension
|
||||||
from .gfm import AutomailExtension
|
from .extensions.automail import AutomailExtension
|
||||||
from .gfm import HiddenHiliteExtension
|
from .extensions.hidden_hilite import HiddenHiliteExtension
|
||||||
from .gfm import SemiSaneListExtension
|
from .extensions.semi_sane_lists import SemiSaneListExtension
|
||||||
from .gfm import SpacedLinkExtension
|
from .extensions.spaced_link import SpacedLinkExtension
|
||||||
from .gfm import StrikethroughExtension
|
from .extensions.strikethrough import StrikethroughExtension
|
||||||
from .gfm import WikiLinkExtension
|
from .extensions.wikilinks import WikiLinkExtension
|
||||||
from .gfm import EmojifyExtension
|
from .extensions.emojify import EmojifyExtension
|
||||||
from .gfm import MentionsExtension
|
from .extensions.mentions import MentionsExtension
|
||||||
|
from .extensions.references import TaigaReferencesExtension
|
||||||
from .processors.references import references
|
|
||||||
|
|
||||||
|
|
||||||
def _make_extensions_list(wikilinks_config=None):
|
def _make_extensions_list(wikilinks_config=None, project=None):
|
||||||
return [AutolinkExtension(),
|
return [AutolinkExtension(),
|
||||||
AutomailExtension(),
|
AutomailExtension(),
|
||||||
SemiSaneListExtension(),
|
SemiSaneListExtension(),
|
||||||
|
@ -29,6 +28,7 @@ def _make_extensions_list(wikilinks_config=None):
|
||||||
WikiLinkExtension(wikilinks_config),
|
WikiLinkExtension(wikilinks_config),
|
||||||
EmojifyExtension(),
|
EmojifyExtension(),
|
||||||
MentionsExtension(),
|
MentionsExtension(),
|
||||||
|
TaigaReferencesExtension(project),
|
||||||
"extra",
|
"extra",
|
||||||
"codehilite"]
|
"codehilite"]
|
||||||
|
|
||||||
|
@ -54,22 +54,13 @@ def cache_by_sha(func):
|
||||||
return _decorator
|
return _decorator
|
||||||
|
|
||||||
|
|
||||||
def _render_markdown(project, text):
|
|
||||||
wikilinks_config = {"base_url": "#/project/{}/wiki/".format(project.slug),
|
|
||||||
"end_url": ""}
|
|
||||||
extensions = _make_extensions_list(wikilinks_config=wikilinks_config)
|
|
||||||
return markdown(text, extensions=extensions)
|
|
||||||
|
|
||||||
|
|
||||||
def _preprocessors(project, text):
|
|
||||||
pre = F() >> F(references, project)
|
|
||||||
return pre(text)
|
|
||||||
|
|
||||||
|
|
||||||
#@cache_by_sha
|
#@cache_by_sha
|
||||||
def render(project, text):
|
def render(project, text):
|
||||||
renderer = F() >> F(_preprocessors, project) >> F(_render_markdown, project)
|
wikilinks_config = {"base_url": "#/project/{}/wiki/".format(project.slug),
|
||||||
return renderer(text)
|
"end_url": ""}
|
||||||
|
extensions = _make_extensions_list(wikilinks_config=wikilinks_config, project=project)
|
||||||
|
return markdown(text, extensions=extensions)
|
||||||
|
|
||||||
|
|
||||||
class DiffMatchPatch(diff_match_patch.diff_match_patch):
|
class DiffMatchPatch(diff_match_patch.diff_match_patch):
|
||||||
def diff_pretty_html(self, diffs):
|
def diff_pretty_html(self, diffs):
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
from .models import Reference
|
||||||
|
|
||||||
|
def get_instance_by_ref(project_id, obj_ref):
|
||||||
|
try:
|
||||||
|
instance = Reference.objects.get(project_id=project_id, ref=obj_ref)
|
||||||
|
except Reference.DoesNotExist:
|
||||||
|
instance = None
|
||||||
|
|
||||||
|
return instance
|
|
@ -1,17 +1,17 @@
|
||||||
from unittest import mock
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import taiga.base
|
import taiga.base
|
||||||
from taiga.mdrender.gfm import mentions
|
from taiga.mdrender.extensions import mentions
|
||||||
from taiga.mdrender.gfm import emojify
|
from taiga.mdrender.extensions import emojify
|
||||||
from taiga.mdrender.processors import references
|
|
||||||
from taiga.mdrender.service import render
|
from taiga.mdrender.service import render
|
||||||
|
|
||||||
class DummyClass:
|
from taiga.projects.references import services
|
||||||
pass
|
|
||||||
|
|
||||||
dummy_project = DummyClass()
|
from taiga.users.models import User
|
||||||
|
|
||||||
|
dummy_project = MagicMock()
|
||||||
dummy_project.id = 1
|
dummy_project.id = 1
|
||||||
dummy_project.slug = "test"
|
dummy_project.slug = "test"
|
||||||
|
|
||||||
|
@ -24,119 +24,52 @@ def test_proccessor_invalid_emoji():
|
||||||
assert result == ["**:notvalidemoji:**"]
|
assert result == ["**:notvalidemoji:**"]
|
||||||
|
|
||||||
def test_proccessor_valid_user_mention():
|
def test_proccessor_valid_user_mention():
|
||||||
DummyModel = DummyClass()
|
with patch("taiga.mdrender.extensions.mentions.User") as mock:
|
||||||
DummyModel.objects = DummyClass()
|
instance = mock.objects.get.return_value
|
||||||
DummyModel.objects.filter = lambda username: ["test"]
|
instance.get_full_name.return_value = "test name"
|
||||||
|
result = render(dummy_project, "**@user1**")
|
||||||
mentions.User = DummyModel
|
expected_result = "<p><strong><a alt=\"test name\" class=\"mention\" href=\"/#/profile/user1\" title=\"test name\">@user1</a></strong></p>"
|
||||||
|
assert result == expected_result
|
||||||
result = mentions.MentionsPreprocessor().run(["**@user1**"])
|
|
||||||
assert result == ["**[@user1](/#/profile/user1 \"@user1\")**"]
|
|
||||||
|
|
||||||
def test_proccessor_invalid_user_mention():
|
def test_proccessor_invalid_user_mention():
|
||||||
DummyModel = DummyClass()
|
with patch("taiga.mdrender.extensions.mentions.User") as mock:
|
||||||
DummyModel.objects = DummyClass()
|
mock.DoesNotExist = User.DoesNotExist
|
||||||
DummyModel.objects.filter = lambda username: []
|
mock.objects.get.side_effect = User.DoesNotExist
|
||||||
|
result = render(dummy_project, "**@notvaliduser**")
|
||||||
mentions.User = DummyModel
|
assert result == '<p><strong>@notvaliduser</strong></p>'
|
||||||
|
|
||||||
result = mentions.MentionsPreprocessor().run(["**@notvaliduser**"])
|
|
||||||
assert result == ['**@notvaliduser**']
|
|
||||||
|
|
||||||
|
|
||||||
def test_proccessor_valid_us_reference():
|
def test_proccessor_valid_us_reference():
|
||||||
class MockModelWithInstance:
|
with patch("taiga.mdrender.extensions.references.get_instance_by_ref") as mock:
|
||||||
class objects:
|
instance = mock.return_value
|
||||||
def filter(*args, **kwargs):
|
instance.content_type.model = "userstory"
|
||||||
dummy_instance = DummyClass()
|
instance.content_object.subject = "test"
|
||||||
dummy_instance.subject = "test-subject"
|
result = render(dummy_project, "**#1**")
|
||||||
return [dummy_instance]
|
expected_result = '<p><strong><a alt="test" class="reference user-story" href="/#/project/test/user-story/1" title="test">#1</a></strong></p>'
|
||||||
UserStoryBack = references.UserStory
|
assert result == expected_result
|
||||||
references.UserStory = MockModelWithInstance
|
|
||||||
|
|
||||||
result = references.references(dummy_project, "**#us1**")
|
|
||||||
assert result == '**[#us1](/#/project/test/user-story/1 "test-subject")**'
|
|
||||||
|
|
||||||
references.UserStory = UserStoryBack
|
|
||||||
|
|
||||||
|
|
||||||
def test_proccessor_invalid_us_reference():
|
|
||||||
class MockModelEmpty:
|
|
||||||
class objects:
|
|
||||||
def filter(*args, **kwargs):
|
|
||||||
return []
|
|
||||||
|
|
||||||
UserStoryBack = references.UserStory
|
|
||||||
references.UserStory = MockModelEmpty
|
|
||||||
|
|
||||||
result = references.references(dummy_project, "**#us1**")
|
|
||||||
assert result == "**#us1**"
|
|
||||||
|
|
||||||
references.UserStory = UserStoryBack
|
|
||||||
|
|
||||||
def test_proccessor_valid_issue_reference():
|
def test_proccessor_valid_issue_reference():
|
||||||
class MockModelWithInstance:
|
with patch("taiga.mdrender.extensions.references.get_instance_by_ref") as mock:
|
||||||
class objects:
|
instance = mock.return_value
|
||||||
def filter(*args, **kwargs):
|
instance.content_type.model = "issue"
|
||||||
dummy_instance = DummyClass()
|
instance.content_object.subject = "test"
|
||||||
dummy_instance.subject = "test-subject"
|
result = render(dummy_project, "**#1**")
|
||||||
return [dummy_instance]
|
expected_result = '<p><strong><a alt="test" class="reference issue" href="/#/project/test/issues/1" title="test">#1</a></strong></p>'
|
||||||
IssueBack = references.Issue
|
assert result == expected_result
|
||||||
references.Issue = MockModelWithInstance
|
|
||||||
|
|
||||||
result = references.references(dummy_project, "**#issue1**")
|
|
||||||
assert result == '**[#issue1](/#/project/test/issues/1 "test-subject")**'
|
|
||||||
|
|
||||||
references.Issue = IssueBack
|
|
||||||
|
|
||||||
|
|
||||||
def test_proccessor_invalid_issue_reference():
|
|
||||||
class MockModelEmpty:
|
|
||||||
class objects:
|
|
||||||
def filter(*args, **kwargs):
|
|
||||||
return []
|
|
||||||
|
|
||||||
IssueBack = references.Issue
|
|
||||||
references.Issue = MockModelEmpty
|
|
||||||
|
|
||||||
result = references.references(dummy_project, "**#issue1**")
|
|
||||||
assert result == "**#issue1**"
|
|
||||||
|
|
||||||
references.Issue = IssueBack
|
|
||||||
|
|
||||||
def test_proccessor_valid_task_reference():
|
def test_proccessor_valid_task_reference():
|
||||||
class MockModelWithInstance:
|
with patch("taiga.mdrender.extensions.references.get_instance_by_ref") as mock:
|
||||||
class objects:
|
instance = mock.return_value
|
||||||
def filter(*args, **kwargs):
|
instance.content_type.model = "task"
|
||||||
dummy_instance = DummyClass()
|
instance.content_object.subject = "test"
|
||||||
dummy_instance.subject = "test-subject"
|
result = render(dummy_project, "**#1**")
|
||||||
return [dummy_instance]
|
expected_result = '<p><strong><a alt="test" class="reference task" href="/#/project/test/tasks/1" title="test">#1</a></strong></p>'
|
||||||
TaskBack = references.Task
|
assert result == expected_result
|
||||||
references.Task = MockModelWithInstance
|
|
||||||
|
|
||||||
result = references.references(dummy_project, "**#task1**")
|
def test_proccessor_invalid_reference():
|
||||||
assert result == '**[#task1](/#/project/test/tasks/1 "test-subject")**'
|
with patch("taiga.mdrender.extensions.references.get_instance_by_ref") as mock:
|
||||||
|
mock.return_value = None
|
||||||
references.Task = TaskBack
|
result = render(dummy_project, "**#1**")
|
||||||
|
assert result == "<p><strong>#1</strong></p>"
|
||||||
|
|
||||||
def test_proccessor_invalid_task_reference():
|
|
||||||
class MockModelEmpty:
|
|
||||||
class objects:
|
|
||||||
def filter(*args, **kwargs):
|
|
||||||
return []
|
|
||||||
|
|
||||||
TaskBack = references.Task
|
|
||||||
references.Task = MockModelEmpty
|
|
||||||
|
|
||||||
result = references.references(dummy_project, "**#task1**")
|
|
||||||
assert result == "**#task1**"
|
|
||||||
|
|
||||||
references.Task = TaskBack
|
|
||||||
|
|
||||||
def test_proccessor_invalid_type_reference():
|
|
||||||
result = references.references(dummy_project, "**#invalid1**")
|
|
||||||
assert result == "**#invalid1**"
|
|
||||||
|
|
||||||
def test_render_wiki_strong():
|
def test_render_wiki_strong():
|
||||||
assert render(dummy_project, "**test**") == "<p><strong>test</strong></p>"
|
assert render(dummy_project, "**test**") == "<p><strong>test</strong></p>"
|
||||||
|
|
Loading…
Reference in New Issue