##// END OF EJS Templates
tooltips: small fixes/tests fixes.
marcink -
r4033:f294c7c5 default
parent child Browse files
Show More
@@ -1,141 +1,127 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import pytest
23 23
24 24 import rhodecode
25 25 from rhodecode.model.db import Repository
26 26 from rhodecode.model.meta import Session
27 27 from rhodecode.model.repo import RepoModel
28 28 from rhodecode.model.repo_group import RepoGroupModel
29 29 from rhodecode.model.settings import SettingsModel
30 30 from rhodecode.tests import TestController
31 31 from rhodecode.tests.fixture import Fixture
32 32 from rhodecode.lib import helpers as h
33 33
34 34 fixture = Fixture()
35 35
36 36
37 37 def route_path(name, **kwargs):
38 38 return {
39 39 'home': '/',
40 40 'repo_group_home': '/{repo_group_name}'
41 41 }[name].format(**kwargs)
42 42
43 43
44 44 class TestHomeController(TestController):
45 45
46 46 def test_index(self):
47 47 self.log_user()
48 48 response = self.app.get(route_path('home'))
49 49 # if global permission is set
50 50 response.mustcontain('New Repository')
51 51
52 52 # search for objects inside the JavaScript JSON
53 53 for repo in Repository.getAll():
54 54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
55 55
56 56 def test_index_contains_statics_with_ver(self):
57 57 from rhodecode.lib.base import calculate_version_hash
58 58
59 59 self.log_user()
60 60 response = self.app.get(route_path('home'))
61 61
62 62 rhodecode_version_hash = calculate_version_hash(
63 63 {'beaker.session.secret': 'test-rc-uytcxaz'})
64 64 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
65 65 response.mustcontain('scripts.min.js?ver={0}'.format(rhodecode_version_hash))
66 66
67 67 def test_index_contains_backend_specific_details(self, backend):
68 68 self.log_user()
69 69 response = self.app.get(route_path('home'))
70 70 tip = backend.repo.get_commit().raw_id
71 71
72 72 # html in javascript variable:
73 73 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
74 74 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
75 75
76 76 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
77 77 response.mustcontain("""Added a symlink""")
78 78
79 79 def test_index_with_anonymous_access_disabled(self):
80 80 with fixture.anon_access(False):
81 81 response = self.app.get(route_path('home'), status=302)
82 82 assert 'login' in response.location
83 83
84 84 def test_index_page_on_groups(self, autologin_user, repo_group):
85 85 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1'))
86 86 response.mustcontain("gr1/repo_in_group")
87 87
88 88 def test_index_page_on_group_with_trailing_slash(
89 89 self, autologin_user, repo_group):
90 90 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1') + '/')
91 91 response.mustcontain("gr1/repo_in_group")
92 92
93 93 @pytest.fixture(scope='class')
94 94 def repo_group(self, request):
95 95 gr = fixture.create_repo_group('gr1')
96 96 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
97 97
98 98 @request.addfinalizer
99 99 def cleanup():
100 100 RepoModel().delete('gr1/repo_in_group')
101 101 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
102 102 Session().commit()
103 103
104 def test_index_with_name_with_tags(self, user_util, autologin_user):
105 user = user_util.create_user()
106 username = user.username
107 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
108 user.lastname = '#"><img src=x onerror=prompt(document.cookie);>'
109
110 Session().add(user)
111 Session().commit()
112 user_util.create_repo(owner=username)
113
114 response = self.app.get(route_path('home'))
115 response.mustcontain(h.html_escape(user.first_name))
116 response.mustcontain(h.html_escape(user.last_name))
117
118 104 @pytest.mark.parametrize("name, state", [
119 105 ('Disabled', False),
120 106 ('Enabled', True),
121 107 ])
122 108 def test_index_show_version(self, autologin_user, name, state):
123 109 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
124 110
125 111 sett = SettingsModel().create_or_update_setting(
126 112 'show_version', state, 'bool')
127 113 Session().add(sett)
128 114 Session().commit()
129 115 SettingsModel().invalidate_settings_cache()
130 116
131 117 response = self.app.get(route_path('home'))
132 118 if state is True:
133 119 response.mustcontain(version_string)
134 120 if state is False:
135 121 response.mustcontain(no=[version_string])
136 122
137 123 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
138 124 response = self.app.get(route_path('home'))
139 125 assert_response = response.assert_response()
140 126 element = assert_response.get_element('.logout #csrf_token')
141 127 assert element.value == csrf_token
@@ -1,148 +1,148 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 22
23 23 from rhodecode.lib.utils2 import md5
24 24 from rhodecode.model.db import Repository
25 25 from rhodecode.model.meta import Session
26 26 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
27 27
28 28
29 29 def route_path(name, params=None, **kwargs):
30 30 import urllib
31 31
32 32 base_url = {
33 33 'repo_summary': '/{repo_name}',
34 34 'edit_repo_issuetracker': '/{repo_name}/settings/issue_trackers',
35 35 'edit_repo_issuetracker_test': '/{repo_name}/settings/issue_trackers/test',
36 36 'edit_repo_issuetracker_delete': '/{repo_name}/settings/issue_trackers/delete',
37 37 'edit_repo_issuetracker_update': '/{repo_name}/settings/issue_trackers/update',
38 38 }[name].format(**kwargs)
39 39
40 40 if params:
41 41 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
42 42 return base_url
43 43
44 44
45 45 @pytest.mark.usefixtures("app")
46 46 class TestRepoIssueTracker(object):
47 47 def test_issuetracker_index(self, autologin_user, backend):
48 48 repo = backend.create_repo()
49 49 response = self.app.get(route_path('edit_repo_issuetracker',
50 50 repo_name=repo.repo_name))
51 51 assert response.status_code == 200
52 52
53 53 def test_add_and_test_issuetracker_patterns(
54 54 self, autologin_user, backend, csrf_token, request, xhr_header):
55 55 pattern = 'issuetracker_pat'
56 56 another_pattern = pattern+'1'
57 57 post_url = route_path(
58 58 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
59 59 post_data = {
60 60 'new_pattern_pattern_0': pattern,
61 61 'new_pattern_url_0': 'http://url',
62 62 'new_pattern_prefix_0': 'prefix',
63 63 'new_pattern_description_0': 'description',
64 64 'new_pattern_pattern_1': another_pattern,
65 65 'new_pattern_url_1': '/url1',
66 66 'new_pattern_prefix_1': 'prefix1',
67 67 'new_pattern_description_1': 'description1',
68 68 'csrf_token': csrf_token
69 69 }
70 70 self.app.post(post_url, post_data, status=302)
71 71 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
72 72 settings = self.settings_model.get_repo_settings()
73 73 self.uid = md5(pattern)
74 74 assert settings[self.uid]['pat'] == pattern
75 75 self.another_uid = md5(another_pattern)
76 76 assert settings[self.another_uid]['pat'] == another_pattern
77 77
78 78 # test pattern
79 79 data = {'test_text': 'example of issuetracker_pat replacement',
80 80 'csrf_token': csrf_token}
81 81 response = self.app.post(
82 82 route_path('edit_repo_issuetracker_test',
83 83 repo_name=backend.repo.repo_name),
84 84 extra_environ=xhr_header, params=data)
85 85
86 86 assert response.body == \
87 'example of <a class="issue-tracker-link" href="http://url">prefix</a> replacement'
87 'example of <a class="tooltip issue-tracker-link" href="http://url" title="description">prefix</a> replacement'
88 88
89 89 @request.addfinalizer
90 90 def cleanup():
91 91 self.settings_model.delete_entries(self.uid)
92 92 self.settings_model.delete_entries(self.another_uid)
93 93
94 94 def test_edit_issuetracker_pattern(
95 95 self, autologin_user, backend, csrf_token, request):
96 96 entry_key = 'issuetracker_pat_'
97 97 pattern = 'issuetracker_pat2'
98 98 old_pattern = 'issuetracker_pat'
99 99 old_uid = md5(old_pattern)
100 100
101 101 sett = SettingsModel(repo=backend.repo).create_or_update_setting(
102 102 entry_key+old_uid, old_pattern, 'unicode')
103 103 Session().add(sett)
104 104 Session().commit()
105 105 post_url = route_path(
106 106 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
107 107 post_data = {
108 108 'new_pattern_pattern_0': pattern,
109 109 'new_pattern_url_0': '/url',
110 110 'new_pattern_prefix_0': 'prefix',
111 111 'new_pattern_description_0': 'description',
112 112 'uid': old_uid,
113 113 'csrf_token': csrf_token
114 114 }
115 115 self.app.post(post_url, post_data, status=302)
116 116 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
117 117 settings = self.settings_model.get_repo_settings()
118 118 self.uid = md5(pattern)
119 119 assert settings[self.uid]['pat'] == pattern
120 120 with pytest.raises(KeyError):
121 121 key = settings[old_uid]
122 122
123 123 @request.addfinalizer
124 124 def cleanup():
125 125 self.settings_model.delete_entries(self.uid)
126 126
127 127 def test_delete_issuetracker_pattern(
128 128 self, autologin_user, backend, csrf_token, settings_util):
129 129 repo = backend.create_repo()
130 130 repo_name = repo.repo_name
131 131 entry_key = 'issuetracker_pat_'
132 132 pattern = 'issuetracker_pat3'
133 133 uid = md5(pattern)
134 134 settings_util.create_repo_rhodecode_setting(
135 135 repo=backend.repo, name=entry_key+uid,
136 136 value=entry_key, type_='unicode', cleanup=False)
137 137
138 138 self.app.post(
139 139 route_path(
140 140 'edit_repo_issuetracker_delete',
141 141 repo_name=backend.repo.repo_name),
142 142 {
143 143 'uid': uid,
144 144 'csrf_token': csrf_token
145 145 }, status=302)
146 146 settings = IssueTrackerSettingsModel(
147 147 repo=Repository.get_by_repo_name(repo_name)).get_repo_settings()
148 148 assert 'rhodecode_%s%s' % (entry_key, uid) not in settings
@@ -1,469 +1,469 b''
1 1 ## DATA TABLE RE USABLE ELEMENTS
2 2 ## usage:
3 3 ## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 4 <%namespace name="base" file="/base/base.mako"/>
5 5
6 6 <%def name="metatags_help()">
7 7 <table>
8 8 <%
9 9 example_tags = [
10 10 ('state','[stable]'),
11 11 ('state','[stale]'),
12 12 ('state','[featured]'),
13 13 ('state','[dev]'),
14 14 ('state','[dead]'),
15 15 ('state','[deprecated]'),
16 16
17 17 ('label','[personal]'),
18 18 ('generic','[v2.0.0]'),
19 19
20 20 ('lang','[lang =&gt; JavaScript]'),
21 21 ('license','[license =&gt; LicenseName]'),
22 22
23 23 ('ref','[requires =&gt; RepoName]'),
24 24 ('ref','[recommends =&gt; GroupName]'),
25 25 ('ref','[conflicts =&gt; SomeName]'),
26 26 ('ref','[base =&gt; SomeName]'),
27 27 ('url','[url =&gt; [linkName](https://rhodecode.com)]'),
28 28 ('see','[see =&gt; http://rhodecode.com]'),
29 29 ]
30 30 %>
31 31 % for tag_type, tag in example_tags:
32 32 <tr>
33 33 <td>${tag|n}</td>
34 34 <td>${h.style_metatag(tag_type, tag)|n}</td>
35 35 </tr>
36 36 % endfor
37 37 </table>
38 38 </%def>
39 39
40 40 <%def name="render_description(description, stylify_metatags)">
41 41 <%
42 42 tags = []
43 43 if stylify_metatags:
44 44 tags, description = h.extract_metatags(description)
45 45 %>
46 46 % for tag_type, tag in tags:
47 47 ${h.style_metatag(tag_type, tag)|n,trim}
48 48 % endfor
49 49 <code style="white-space: pre-wrap">${description}</code>
50 50 </%def>
51 51
52 52 ## REPOSITORY RENDERERS
53 53 <%def name="quick_menu(repo_name)">
54 54 <i class="icon-more"></i>
55 55 <div class="menu_items_container hidden">
56 56 <ul class="menu_items">
57 57 <li>
58 58 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
59 59 <span>${_('Summary')}</span>
60 60 </a>
61 61 </li>
62 62 <li>
63 63 <a title="${_('Commits')}" href="${h.route_path('repo_commits',repo_name=repo_name)}">
64 64 <span>${_('Commits')}</span>
65 65 </a>
66 66 </li>
67 67 <li>
68 68 <a title="${_('Files')}" href="${h.route_path('repo_files:default_commit',repo_name=repo_name)}">
69 69 <span>${_('Files')}</span>
70 70 </a>
71 71 </li>
72 72 <li>
73 73 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
74 74 <span>${_('Fork')}</span>
75 75 </a>
76 76 </li>
77 77 </ul>
78 78 </div>
79 79 </%def>
80 80
81 81 <%def name="repo_name(name,rtype,rstate,private,archived,fork_of,short_name=False,admin=False)">
82 82 <%
83 83 def get_name(name,short_name=short_name):
84 84 if short_name:
85 85 return name.split('/')[-1]
86 86 else:
87 87 return name
88 88 %>
89 89 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
90 90 ##NAME
91 91 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
92 92
93 93 ##TYPE OF REPO
94 94 %if h.is_hg(rtype):
95 95 <span title="${_('Mercurial repository')}"><i class="icon-hg" style="font-size: 14px;"></i></span>
96 96 %elif h.is_git(rtype):
97 97 <span title="${_('Git repository')}"><i class="icon-git" style="font-size: 14px"></i></span>
98 98 %elif h.is_svn(rtype):
99 99 <span title="${_('Subversion repository')}"><i class="icon-svn" style="font-size: 14px"></i></span>
100 100 %endif
101 101
102 102 ##PRIVATE/PUBLIC
103 103 %if private is True and c.visual.show_private_icon:
104 104 <i class="icon-lock" title="${_('Private repository')}"></i>
105 105 %elif private is False and c.visual.show_public_icon:
106 106 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
107 107 %else:
108 108 <span></span>
109 109 %endif
110 110 ${get_name(name)}
111 111 </a>
112 112 %if fork_of:
113 113 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
114 114 %endif
115 115 %if rstate == 'repo_state_pending':
116 116 <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
117 117 (${_('creating...')})
118 118 </span>
119 119 %endif
120 120
121 121 </div>
122 122 </%def>
123 123
124 124 <%def name="repo_desc(description, stylify_metatags)">
125 125 <%
126 126 tags, description = h.extract_metatags(description)
127 127 %>
128 128
129 129 <div class="truncate-wrap">
130 130 % if stylify_metatags:
131 131 % for tag_type, tag in tags:
132 132 ${h.style_metatag(tag_type, tag)|n}
133 133 % endfor
134 134 % endif
135 135 ${description}
136 136 </div>
137 137
138 138 </%def>
139 139
140 140 <%def name="last_change(last_change)">
141 141 ${h.age_component(last_change, time_is_local=True)}
142 142 </%def>
143 143
144 144 <%def name="revision(repo_name, rev, commit_id, author, last_msg, commit_date)">
145 145 <div>
146 146 %if rev >= 0:
147 <code><a class="tooltip-hovercard" data-hovercard-alt="${last_msg}" data-hovercard-url="${h.route_path('hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)}" href="${h.route_path('repo_commit',repo_name=repo_name,commit_id=commit_id)}">${'r{}:{}'.format(rev,h.short_id(commit_id))}</a></code>
147 <code><a class="tooltip-hovercard" data-hovercard-alt=${h.tooltip(last_msg)} data-hovercard-url="${h.route_path('hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)}" href="${h.route_path('repo_commit',repo_name=repo_name,commit_id=commit_id)}">${'r{}:{}'.format(rev,h.short_id(commit_id))}</a></code>
148 148 %else:
149 149 ${_('No commits yet')}
150 150 %endif
151 151 </div>
152 152 </%def>
153 153
154 154 <%def name="rss(name)">
155 155 %if c.rhodecode_user.username != h.DEFAULT_USER:
156 156 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
157 157 %else:
158 158 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
159 159 %endif
160 160 </%def>
161 161
162 162 <%def name="atom(name)">
163 163 %if c.rhodecode_user.username != h.DEFAULT_USER:
164 164 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
165 165 %else:
166 166 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
167 167 %endif
168 168 </%def>
169 169
170 170 <%def name="repo_actions(repo_name, super_user=True)">
171 171 <div>
172 172 <div class="grid_edit">
173 173 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
174 174 Edit
175 175 </a>
176 176 </div>
177 177 <div class="grid_delete">
178 178 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
179 179 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
180 180 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
181 181 ${h.end_form()}
182 182 </div>
183 183 </div>
184 184 </%def>
185 185
186 186 <%def name="repo_state(repo_state)">
187 187 <div>
188 188 %if repo_state == 'repo_state_pending':
189 189 <div class="tag tag4">${_('Creating')}</div>
190 190 %elif repo_state == 'repo_state_created':
191 191 <div class="tag tag1">${_('Created')}</div>
192 192 %else:
193 193 <div class="tag alert2" title="${h.tooltip(repo_state)}">invalid</div>
194 194 %endif
195 195 </div>
196 196 </%def>
197 197
198 198
199 199 ## REPO GROUP RENDERERS
200 200 <%def name="quick_repo_group_menu(repo_group_name)">
201 201 <i class="icon-more"></i>
202 202 <div class="menu_items_container hidden">
203 203 <ul class="menu_items">
204 204 <li>
205 205 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">${_('Summary')}</a>
206 206 </li>
207 207
208 208 </ul>
209 209 </div>
210 210 </%def>
211 211
212 212 <%def name="repo_group_name(repo_group_name, children_groups=None)">
213 213 <div>
214 214 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">
215 215 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
216 216 %if children_groups:
217 217 ${h.literal(' &raquo; '.join(children_groups))}
218 218 %else:
219 219 ${repo_group_name}
220 220 %endif
221 221 </a>
222 222 </div>
223 223 </%def>
224 224
225 225 <%def name="repo_group_desc(description, personal, stylify_metatags)">
226 226
227 227 <%
228 228 if stylify_metatags:
229 229 tags, description = h.extract_metatags(description)
230 230 %>
231 231
232 232 <div class="truncate-wrap">
233 233 % if personal:
234 234 <div class="metatag" tag="personal">${_('personal')}</div>
235 235 % endif
236 236
237 237 % if stylify_metatags:
238 238 % for tag_type, tag in tags:
239 239 ${h.style_metatag(tag_type, tag)|n}
240 240 % endfor
241 241 % endif
242 242 ${description}
243 243 </div>
244 244
245 245 </%def>
246 246
247 247 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
248 248 <div class="grid_edit">
249 249 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
250 250 </div>
251 251 <div class="grid_delete">
252 252 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
253 253 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
254 254 onclick="return confirm('"+_ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
255 255 ${h.end_form()}
256 256 </div>
257 257 </%def>
258 258
259 259
260 260 <%def name="user_actions(user_id, username)">
261 261 <div class="grid_edit">
262 262 <a href="${h.route_path('user_edit',user_id=user_id)}" title="${_('Edit')}">
263 263 ${_('Edit')}
264 264 </a>
265 265 </div>
266 266 <div class="grid_delete">
267 267 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
268 268 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
269 269 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
270 270 ${h.end_form()}
271 271 </div>
272 272 </%def>
273 273
274 274 <%def name="user_group_actions(user_group_id, user_group_name)">
275 275 <div class="grid_edit">
276 276 <a href="${h.route_path('edit_user_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
277 277 </div>
278 278 <div class="grid_delete">
279 279 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
280 280 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
281 281 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
282 282 ${h.end_form()}
283 283 </div>
284 284 </%def>
285 285
286 286
287 287 <%def name="user_name(user_id, username)">
288 288 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.route_path('user_edit', user_id=user_id))}
289 289 </%def>
290 290
291 291 <%def name="user_profile(username)">
292 292 ${base.gravatar_with_user(username, 16, tooltip=True)}
293 293 </%def>
294 294
295 295 <%def name="user_group_name(user_group_name)">
296 296 <div>
297 297 <i class="icon-user-group" title="${_('User group')}"></i>
298 298 ${h.link_to_group(user_group_name)}
299 299 </div>
300 300 </%def>
301 301
302 302
303 303 ## GISTS
304 304
305 305 <%def name="gist_gravatar(full_contact)">
306 306 <div class="gist_gravatar">
307 307 ${base.gravatar(full_contact, 30)}
308 308 </div>
309 309 </%def>
310 310
311 311 <%def name="gist_access_id(gist_access_id, full_contact)">
312 312 <div>
313 313 <b>
314 314 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
315 315 </b>
316 316 </div>
317 317 </%def>
318 318
319 319 <%def name="gist_author(full_contact, created_on, expires)">
320 320 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
321 321 </%def>
322 322
323 323
324 324 <%def name="gist_created(created_on)">
325 325 <div class="created">
326 326 ${h.age_component(created_on, time_is_local=True)}
327 327 </div>
328 328 </%def>
329 329
330 330 <%def name="gist_expires(expires)">
331 331 <div class="created">
332 332 %if expires == -1:
333 333 ${_('never')}
334 334 %else:
335 335 ${h.age_component(h.time_to_utcdatetime(expires))}
336 336 %endif
337 337 </div>
338 338 </%def>
339 339
340 340 <%def name="gist_type(gist_type)">
341 341 %if gist_type != 'public':
342 342 <div class="tag">${_('Private')}</div>
343 343 %endif
344 344 </%def>
345 345
346 346 <%def name="gist_description(gist_description)">
347 347 ${gist_description}
348 348 </%def>
349 349
350 350
351 351 ## PULL REQUESTS GRID RENDERERS
352 352
353 353 <%def name="pullrequest_target_repo(repo_name)">
354 354 <div class="truncate">
355 355 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
356 356 </div>
357 357 </%def>
358 358
359 359 <%def name="pullrequest_status(status)">
360 360 <i class="icon-circle review-status-${status}"></i>
361 361 </%def>
362 362
363 363 <%def name="pullrequest_title(title, description)">
364 364 ${title}
365 365 </%def>
366 366
367 367 <%def name="pullrequest_comments(comments_nr)">
368 368 <i class="icon-comment"></i> ${comments_nr}
369 369 </%def>
370 370
371 371 <%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)">
372 372 <a href="${h.route_path('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
373 373 % if short:
374 374 #${pull_request_id}
375 375 % else:
376 376 ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
377 377 % endif
378 378 </a>
379 379 </%def>
380 380
381 381 <%def name="pullrequest_updated_on(updated_on)">
382 382 ${h.age_component(h.time_to_utcdatetime(updated_on))}
383 383 </%def>
384 384
385 385 <%def name="pullrequest_author(full_contact)">
386 386 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
387 387 </%def>
388 388
389 389
390 390 ## ARTIFACT RENDERERS
391 391 <%def name="repo_artifact_name(repo_name, file_uid, artifact_display_name)">
392 392 <a href="${h.route_path('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}">
393 393 ${artifact_display_name or '_EMPTY_NAME_'}
394 394 </a>
395 395 </%def>
396 396
397 397 <%def name="repo_artifact_uid(repo_name, file_uid)">
398 398 <code>${h.shorter(file_uid, size=24, prefix=True)}</code>
399 399 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${h.route_url('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}" title="${_('Copy the full url')}"></i>
400 400 </%def>
401 401
402 402 <%def name="repo_artifact_sha256(artifact_sha256)">
403 403 <div class="code">${h.shorter(artifact_sha256, 12)}<i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${artifact_sha256}" title="${_('Copy the sha256 ({})').format(artifact_sha256)}"></i></div>
404 404 </%def>
405 405
406 406 <%def name="repo_artifact_actions(repo_name, file_store_id, file_uid)">
407 407 ## <div class="grid_edit">
408 408 ## <a href="#Edit" title="${_('Edit')}">${_('Edit')}</a>
409 409 ## </div>
410 410 <div class="grid_edit">
411 411 <a href="${h.route_path('repo_artifacts_info', repo_name=repo_name, uid=file_store_id)}" title="${_('Info')}">${_('Info')}</a>
412 412 </div>
413 413 % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
414 414 <div class="grid_delete">
415 415 ${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)}
416 416 ${h.submit('remove_',_('Delete'),id="remove_artifact_%s" % file_store_id, class_="btn btn-link btn-danger",
417 417 onclick="return confirm('"+_('Confirm to delete this artifact: %s') % file_uid+"');")}
418 418 ${h.end_form()}
419 419 </div>
420 420 % endif
421 421 </%def>
422 422
423 423 <%def name="markup_form(form_id, form_text='', help_text=None)">
424 424
425 425 <div class="markup-form">
426 426 <div class="markup-form-area">
427 427 <div class="markup-form-area-header">
428 428 <ul class="nav-links clearfix">
429 429 <li class="active">
430 430 <a href="#edit-text" tabindex="-1" id="edit-btn_${form_id}">${_('Write')}</a>
431 431 </li>
432 432 <li class="">
433 433 <a href="#preview-text" tabindex="-1" id="preview-btn_${form_id}">${_('Preview')}</a>
434 434 </li>
435 435 </ul>
436 436 </div>
437 437
438 438 <div class="markup-form-area-write" style="display: block;">
439 439 <div id="edit-container_${form_id}">
440 440 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
441 441 </div>
442 442 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
443 443 <div id="preview-box_${form_id}" class="preview-box"></div>
444 444 </div>
445 445 </div>
446 446
447 447 <div class="markup-form-area-footer">
448 448 <div class="toolbar">
449 449 <div class="toolbar-text">
450 450 ${(_('Parsed using %s syntax') % (
451 451 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
452 452 )
453 453 )|n}
454 454 </div>
455 455 </div>
456 456 </div>
457 457 </div>
458 458
459 459 <div class="markup-form-footer">
460 460 % if help_text:
461 461 <span class="help-block">${help_text}</span>
462 462 % endif
463 463 </div>
464 464 </div>
465 465 <script type="text/javascript">
466 466 new MarkupForm('${form_id}');
467 467 </script>
468 468
469 469 </%def>
@@ -1,235 +1,239 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import copy
22 22 import mock
23 23 import pytest
24 24
25 25 from rhodecode.lib import helpers
26 26 from rhodecode.lib.utils2 import AttributeDict
27 27 from rhodecode.model.settings import IssueTrackerSettingsModel
28 28 from rhodecode.tests import no_newline_id_generator
29 29
30 30
31 31 @pytest.mark.parametrize('url, expected_url', [
32 32 ('http://rc.rc/test', '<a href="http://rc.rc/test">http://rc.rc/test</a>'),
33 33 ('http://rc.rc/@foo', '<a href="http://rc.rc/@foo">http://rc.rc/@foo</a>'),
34 34 ('http://rc.rc/!foo', '<a href="http://rc.rc/!foo">http://rc.rc/!foo</a>'),
35 35 ('http://rc.rc/&foo', '<a href="http://rc.rc/&foo">http://rc.rc/&foo</a>'),
36 36 ('http://rc.rc/#foo', '<a href="http://rc.rc/#foo">http://rc.rc/#foo</a>'),
37 37 ])
38 38 def test_urlify_text(url, expected_url):
39 39 assert helpers.urlify_text(url) == expected_url
40 40
41 41
42 42 @pytest.mark.parametrize('repo_name, commit_id, path, expected_result', [
43 43 # Simple case 1
44 44 ('repo', 'commit', 'a/b',
45 45 '<a href="/repo/files/commit/"><i class="icon-home"></i></a>'
46 46 ' / '
47 47 '<a href="/repo/files/commit/a">a</a>'
48 48 ' / '
49 49 'b'),
50 50
51 51 # Simple case
52 52 ('rX<X', 'cX<X', 'pX<X/aX<X/bX<X',
53 53 '<a href="/rX%3CX/files/cX%3CX/"><i class="icon-home"></i></a>'
54 54 ' / '
55 55 '<a href="/rX%3CX/files/cX%3CX/pX%3CX">pX&lt;X</a>'
56 56 ' / '
57 57 '<a href="/rX%3CX/files/cX%3CX/pX%3CX/aX%3CX">aX&lt;X</a>'
58 58 ' / '
59 59 'bX&lt;X'),
60 60
61 61 # Path with only one segment
62 62 ('rX<X', 'cX<X', 'pX<X',
63 63 '<a href="/rX%3CX/files/cX%3CX/"><i class="icon-home"></i></a>'
64 64 ' / '
65 65 'pX&lt;X'),
66 66
67 67 # Empty path
68 68 ('rX<X', 'cX<X', '',
69 69 '<i class="icon-home"></i>'),
70 70
71 71 # simple quote
72 72 ('rX"X', 'cX"X', 'pX"X/aX"X/bX"X',
73 73 '<a href="/rX%22X/files/cX%22X/"><i class="icon-home"></i></a>'
74 74 ' / '
75 75 '<a href="/rX%22X/files/cX%22X/pX%22X">pX&#34;X</a>'
76 76 ' / '
77 77 '<a href="/rX%22X/files/cX%22X/pX%22X/aX%22X">aX&#34;X</a>'
78 78 ' / '
79 79 'bX&#34;X'),
80 80
81 81 ], ids=['simple1', 'simple2', 'one_segment', 'empty_path', 'simple_quote'])
82 82 def test_files_breadcrumbs_xss(
83 83 repo_name, commit_id, path, app, expected_result):
84 84 result = helpers.files_breadcrumbs(repo_name, commit_id, path)
85 85 # Expect it to encode all path fragments properly. This is important
86 86 # because it returns an instance of `literal`.
87 87 if path != '':
88 88 expected_result = expected_result + helpers.files_icon.format(helpers.escape(path))
89 89 assert result == expected_result
90 90
91 91
92 92 def test_format_binary():
93 93 assert helpers.format_byte_size_binary(298489462784) == '278.0 GiB'
94 94
95 95
96 96 @pytest.mark.parametrize('text_string, pattern, expected', [
97 97 ('No issue here', '(?:#)(?P<issue_id>\d+)', []),
98 98 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
99 99 [{'url': 'http://r.io/{repo}/i/42', 'id': '42'}]),
100 100 ('Fix #42, #53', '(?:#)(?P<issue_id>\d+)', [
101 101 {'url': 'http://r.io/{repo}/i/42', 'id': '42'},
102 102 {'url': 'http://r.io/{repo}/i/53', 'id': '53'}]),
103 103 ('Fix #42', '(?:#)?<issue_id>\d+)', []), # Broken regex
104 104 ])
105 105 def test_extract_issues(backend, text_string, pattern, expected):
106 106 repo = backend.create_repo()
107 107 config = {
108 108 '123': {
109 109 'uid': '123',
110 110 'pat': pattern,
111 111 'url': 'http://r.io/${repo}/i/${issue_id}',
112 112 'pref': '#',
113 'desc': 'Test Pattern'
113 114 }
114 115 }
115 116
116 117 def get_settings_mock(self, cache=True):
117 118 return config
118 119
119 120 with mock.patch.object(IssueTrackerSettingsModel,
120 121 'get_settings', get_settings_mock):
121 122 text, issues = helpers.process_patterns(text_string, repo.repo_name)
122 123
123 124 expected = copy.deepcopy(expected)
124 125 for item in expected:
125 126 item['url'] = item['url'].format(repo=repo.repo_name)
126 127
127 128 assert issues == expected
128 129
129 130
130 131 @pytest.mark.parametrize('text_string, pattern, link_format, expected_text', [
131 132 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'html',
132 'Fix <a class="issue-tracker-link" href="http://r.io/{repo}/i/42">#42</a>'),
133 'Fix <a class="tooltip issue-tracker-link" href="http://r.io/{repo}/i/42" title="Test Pattern">#42</a>'),
133 134
134 135 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'markdown',
135 136 'Fix [#42](http://r.io/{repo}/i/42)'),
136 137
137 138 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'rst',
138 139 'Fix `#42 <http://r.io/{repo}/i/42>`_'),
139 140
140 141 ('Fix #42', '(?:#)?<issue_id>\d+)', 'html',
141 142 'Fix #42'), # Broken regex
142 143 ])
143 144 def test_process_patterns_repo(backend, text_string, pattern, expected_text, link_format):
144 145 repo = backend.create_repo()
145 146
146 147 def get_settings_mock(self, cache=True):
147 148 return {
148 149 '123': {
149 150 'uid': '123',
150 151 'pat': pattern,
151 152 'url': 'http://r.io/${repo}/i/${issue_id}',
152 153 'pref': '#',
154 'desc': 'Test Pattern'
153 155 }
154 156 }
155 157
156 158 with mock.patch.object(IssueTrackerSettingsModel,
157 159 'get_settings', get_settings_mock):
158 160 processed_text, issues = helpers.process_patterns(
159 161 text_string, repo.repo_name, link_format)
160 162
161 163 assert processed_text == expected_text.format(repo=repo.repo_name)
162 164
163 165
164 166 @pytest.mark.parametrize('text_string, pattern, expected_text', [
165 167 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
166 'Fix <a class="issue-tracker-link" href="http://r.io/i/42">#42</a>'),
168 'Fix <a class="tooltip issue-tracker-link" href="http://r.io/i/42" title="Test Pattern">#42</a>'),
167 169 ('Fix #42', '(?:#)?<issue_id>\d+)',
168 170 'Fix #42'), # Broken regex
169 171 ])
170 172 def test_process_patterns_no_repo(text_string, pattern, expected_text):
171 173
172 174 def get_settings_mock(self, cache=True):
173 175 return {
174 176 '123': {
175 177 'uid': '123',
176 178 'pat': pattern,
177 179 'url': 'http://r.io/i/${issue_id}',
178 180 'pref': '#',
181 'desc': 'Test Pattern'
179 182 }
180 183 }
181 184
182 185 with mock.patch.object(IssueTrackerSettingsModel,
183 186 'get_global_settings', get_settings_mock):
184 187 processed_text, issues = helpers.process_patterns(
185 188 text_string, '')
186 189
187 190 assert processed_text == expected_text
188 191
189 192
190 193 def test_process_patterns_non_existent_repo_name(backend):
191 194 text_string = 'Fix #42'
192 195 pattern = '(?:#)(?P<issue_id>\d+)'
193 expected_text = ('Fix <a class="issue-tracker-link" '
194 'href="http://r.io/do-not-exist/i/42">#42</a>')
196 expected_text = ('Fix <a class="tooltip issue-tracker-link" '
197 'href="http://r.io/do-not-exist/i/42" title="Test Pattern">#42</a>')
195 198
196 199 def get_settings_mock(self, cache=True):
197 200 return {
198 201 '123': {
199 202 'uid': '123',
200 203 'pat': pattern,
201 204 'url': 'http://r.io/${repo}/i/${issue_id}',
202 205 'pref': '#',
206 'desc': 'Test Pattern'
203 207 }
204 208 }
205 209
206 210 with mock.patch.object(IssueTrackerSettingsModel,
207 211 'get_global_settings', get_settings_mock):
208 212 processed_text, issues = helpers.process_patterns(
209 213 text_string, 'do-not-exist')
210 214
211 215 assert processed_text == expected_text
212 216
213 217
214 218 def test_get_visual_attr(baseapp):
215 219 from rhodecode.apps._base import TemplateArgs
216 220 c = TemplateArgs()
217 221 assert None is helpers.get_visual_attr(c, 'fakse')
218 222
219 223 # emulate the c.visual behaviour
220 224 c.visual = AttributeDict({})
221 225 assert None is helpers.get_visual_attr(c, 'some_var')
222 226
223 227 c.visual.some_var = 'foobar'
224 228 assert 'foobar' == helpers.get_visual_attr(c, 'some_var')
225 229
226 230
227 231 @pytest.mark.parametrize('test_text, inclusive, expected_text', [
228 232 ('just a string', False, 'just a string'),
229 233 ('just a string\n', False, 'just a string'),
230 234 ('just a string\n next line', False, 'just a string...'),
231 235 ('just a string\n next line', True, 'just a string\n...'),
232 236 ], ids=no_newline_id_generator)
233 237 def test_chop_at(test_text, inclusive, expected_text):
234 238 assert helpers.chop_at_smart(
235 239 test_text, '\n', inclusive, '...') == expected_text
General Comments 0
You need to be logged in to leave comments. Login now